JAL-2388 Applied ViewportRanges to code
[jalview.git] / src / jalview / appletgui / IdPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import jalview.datamodel.Sequence;
24 import jalview.datamodel.SequenceFeature;
25 import jalview.datamodel.SequenceGroup;
26 import jalview.datamodel.SequenceI;
27 import jalview.urls.api.UrlProviderFactoryI;
28 import jalview.urls.api.UrlProviderI;
29 import jalview.urls.applet.AppletUrlProviderFactory;
30 import jalview.viewmodel.AlignmentViewport;
31
32 import java.awt.BorderLayout;
33 import java.awt.Panel;
34 import java.awt.event.InputEvent;
35 import java.awt.event.MouseEvent;
36 import java.awt.event.MouseListener;
37 import java.awt.event.MouseMotionListener;
38 import java.util.HashMap;
39 import java.util.List;
40
41 public class IdPanel extends Panel implements MouseListener,
42         MouseMotionListener
43 {
44
45   protected IdCanvas idCanvas;
46
47   protected AlignmentViewport av;
48
49   protected AlignmentPanel alignPanel;
50
51   ScrollThread scrollThread = null;
52
53   int lastid = -1;
54
55   boolean mouseDragging = false;
56
57   UrlProviderI urlProvider = null;
58
59   public IdPanel(AlignViewport av, AlignmentPanel parent)
60   {
61     this.av = av;
62     alignPanel = parent;
63     idCanvas = new IdCanvas(av);
64     setLayout(new BorderLayout());
65     add(idCanvas, BorderLayout.CENTER);
66     idCanvas.addMouseListener(this);
67     idCanvas.addMouseMotionListener(this);
68
69     String label, url;
70     // TODO: add in group link parameter
71
72     // make a list of label,url pairs
73     HashMap<String, String> urlList = new HashMap<String, String>();
74     if (av.applet != null)
75     {
76       for (int i = 1; i < 10; i++)
77       {
78         label = av.applet.getParameter("linkLabel_" + i);
79         url = av.applet.getParameter("linkURL_" + i);
80
81         // only add non-null parameters
82         if (label != null)
83         {
84           urlList.put(label, url);
85         }
86       }
87
88       if (!urlList.isEmpty())
89       {
90         // set default as first entry in list
91         String defaultUrl = av.applet.getParameter("linkLabel_1");
92         UrlProviderFactoryI factory = new AppletUrlProviderFactory(
93                 defaultUrl, urlList);
94         urlProvider = factory.createUrlProvider();
95       }
96     }
97   }
98
99   Tooltip tooltip;
100
101   @Override
102   public void mouseMoved(MouseEvent e)
103   {
104     int seq = alignPanel.seqPanel.findSeq(e);
105
106     SequenceI sequence = av.getAlignment().getSequenceAt(seq);
107
108     // look for non-pos features
109     StringBuffer tooltiptext = new StringBuffer();
110     if (sequence != null)
111     {
112       if (sequence.getDescription() != null)
113       {
114         tooltiptext.append(sequence.getDescription());
115         tooltiptext.append("\n");
116       }
117
118       SequenceFeature sf[] = sequence.getSequenceFeatures();
119       for (int sl = 0; sf != null && sl < sf.length; sl++)
120       {
121         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
122         {
123           boolean nl = false;
124           if (sf[sl].getFeatureGroup() != null)
125           {
126             tooltiptext.append(sf[sl].getFeatureGroup());
127             nl = true;
128           }
129           ;
130           if (sf[sl].getType() != null)
131           {
132             tooltiptext.append(" ");
133             tooltiptext.append(sf[sl].getType());
134             nl = true;
135           }
136           ;
137           if (sf[sl].getDescription() != null)
138           {
139             tooltiptext.append(" ");
140             tooltiptext.append(sf[sl].getDescription());
141             nl = true;
142           }
143           ;
144           if (!Float.isNaN(sf[sl].getScore()) && sf[sl].getScore() != 0f)
145           {
146             tooltiptext.append(" Score = ");
147             tooltiptext.append(sf[sl].getScore());
148             nl = true;
149           }
150           ;
151           if (sf[sl].getStatus() != null && sf[sl].getStatus().length() > 0)
152           {
153             tooltiptext.append(" (");
154             tooltiptext.append(sf[sl].getStatus());
155             tooltiptext.append(")");
156             nl = true;
157           }
158           ;
159           if (nl)
160           {
161             tooltiptext.append("\n");
162           }
163         }
164       }
165     }
166     if (tooltiptext.length() == 0)
167     {
168       // nothing to display - so clear tooltip if one is visible
169       if (tooltip != null)
170       {
171         tooltip.setVisible(false);
172       }
173       tooltip = null;
174       tooltiptext = null;
175       return;
176     }
177     if (tooltip == null)
178     {
179       tooltip = new Tooltip(sequence.getDisplayId(true) + "\n"
180               + tooltiptext.toString(), idCanvas);
181     }
182     else
183     {
184       tooltip.setTip(sequence.getDisplayId(true) + "\n"
185               + tooltiptext.toString());
186     }
187     tooltiptext = null;
188   }
189
190   @Override
191   public void mouseDragged(MouseEvent e)
192   {
193     mouseDragging = true;
194
195     int seq = Math.max(0, alignPanel.seqPanel.findSeq(e));
196
197     if (seq < lastid)
198     {
199       selectSeqs(lastid - 1, seq);
200     }
201     else if (seq > lastid)
202     {
203       selectSeqs(lastid + 1, seq);
204     }
205
206     lastid = seq;
207     alignPanel.paintAlignment(false);
208   }
209
210   @Override
211   public void mouseClicked(MouseEvent e)
212   {
213     if (e.getClickCount() < 2)
214     {
215       return;
216     }
217
218     // get the sequence details
219     int seq = alignPanel.seqPanel.findSeq(e);
220     SequenceI sq = av.getAlignment().getSequenceAt(seq);
221     if (sq == null)
222     {
223       return;
224     }
225     String id = sq.getName();
226
227     // get the default url with the sequence details filled in
228     String url = urlProvider.getPrimaryUrl(id);
229     String target = urlProvider.getPrimaryTarget(id);
230     try
231     {
232       alignPanel.alignFrame.showURL(url, target);
233     } catch (Exception ex)
234     {
235       ex.printStackTrace();
236     }
237   }
238
239   @Override
240   public void mouseEntered(MouseEvent e)
241   {
242     if (scrollThread != null)
243     {
244       scrollThread.running = false;
245     }
246   }
247
248   @Override
249   public void mouseExited(MouseEvent e)
250   {
251     if (av.getWrapAlignment())
252     {
253       return;
254     }
255
256     if (mouseDragging && e.getY() < 0 && av.getRanges().getStartSeq() > 0)
257     {
258       scrollThread = new ScrollThread(true);
259     }
260
261     if (mouseDragging && e.getY() >= getSize().height
262             && av.getAlignment().getHeight() > av.getRanges().getEndSeq())
263     {
264       scrollThread = new ScrollThread(false);
265     }
266   }
267
268   @Override
269   public void mousePressed(MouseEvent e)
270   {
271     if (e.getClickCount() > 1)
272     {
273       return;
274     }
275
276     int y = e.getY();
277     if (av.getWrapAlignment())
278     {
279       y -= 2 * av.getCharHeight();
280     }
281
282     int seq = alignPanel.seqPanel.findSeq(e);
283
284     if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
285     {
286       Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq);
287
288       // build a new links menu based on the current links + any non-positional
289       // features
290       List<String> nlinks = urlProvider.getLinksForMenu();
291
292       SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
293       for (int sl = 0; sf != null && sl < sf.length; sl++)
294       {
295         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
296         {
297           if (sf[sl].links != null && sf[sl].links.size() > 0)
298           {
299             for (int l = 0, lSize = sf[sl].links.size(); l < lSize; l++)
300             {
301               nlinks.add(sf[sl].links.elementAt(l));
302             }
303           }
304         }
305       }
306
307       APopupMenu popup = new APopupMenu(alignPanel, sq, nlinks);
308       this.add(popup);
309       popup.show(this, e.getX(), e.getY());
310       return;
311     }
312
313     if ((av.getSelectionGroup() == null)
314             || ((!jalview.util.Platform.isControlDown(e) && !e
315                     .isShiftDown()) && av.getSelectionGroup() != null))
316     {
317       av.setSelectionGroup(new SequenceGroup());
318       av.getSelectionGroup().setStartRes(0);
319       av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
320     }
321
322     if (e.isShiftDown() && lastid != -1)
323     {
324       selectSeqs(lastid, seq);
325     }
326     else
327     {
328       selectSeq(seq);
329     }
330
331     alignPanel.paintAlignment(false);
332   }
333
334   void selectSeq(int seq)
335   {
336     lastid = seq;
337     SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
338     av.getSelectionGroup().addOrRemove(pickedSeq, true);
339   }
340
341   void selectSeqs(int start, int end)
342   {
343
344     lastid = start;
345
346     if (end >= av.getAlignment().getHeight())
347     {
348       end = av.getAlignment().getHeight() - 1;
349     }
350
351     if (end < start)
352     {
353       int tmp = start;
354       start = end;
355       end = tmp;
356       lastid = end;
357     }
358     if (av.getSelectionGroup() == null)
359     {
360       av.setSelectionGroup(new SequenceGroup());
361     }
362     for (int i = start; i <= end; i++)
363     {
364       av.getSelectionGroup().addSequence(
365               av.getAlignment().getSequenceAt(i), i == end);
366     }
367
368   }
369
370   @Override
371   public void mouseReleased(MouseEvent e)
372   {
373     if (scrollThread != null)
374     {
375       scrollThread.running = false;
376     }
377
378     if (av.getSelectionGroup() != null)
379     {
380       av.getSelectionGroup().recalcConservation();
381     }
382
383     mouseDragging = false;
384     PaintRefresher.Refresh(this, av.getSequenceSetId());
385     // always send selection message when mouse is released
386     av.sendSelection();
387   }
388
389   public void highlightSearchResults(List<SequenceI> list)
390   {
391     idCanvas.setHighlighted(list);
392
393     if (list == null)
394     {
395       return;
396     }
397
398     int index = av.getAlignment().findIndex(list.get(0));
399
400     // do we need to scroll the panel?
401     if (av.getRanges().getStartSeq() > index
402             || av.getRanges().getEndSeq() < index)
403     {
404       alignPanel.setScrollValues(av.getRanges().getStartRes(), index);
405     }
406   }
407
408   // this class allows scrolling off the bottom of the visible alignment
409   class ScrollThread extends Thread
410   {
411     boolean running = false;
412
413     boolean up = true;
414
415     public ScrollThread(boolean up)
416     {
417       this.up = up;
418       start();
419     }
420
421     public void stopScrolling()
422     {
423       running = false;
424     }
425
426     @Override
427     public void run()
428     {
429       running = true;
430       while (running)
431       {
432         if (alignPanel.scrollUp(up))
433         {
434           // scroll was ok, so add new sequence to selection
435           int seq = av.getRanges().getStartSeq();
436           if (!up)
437           {
438             seq = av.getRanges().getEndSeq();
439           }
440
441           if (seq < lastid)
442           {
443             selectSeqs(lastid - 1, seq);
444           }
445           else if (seq > lastid && seq < av.getAlignment().getHeight())
446           {
447             selectSeqs(lastid + 1, seq);
448           }
449
450           lastid = seq;
451         }
452         else
453         {
454           running = false;
455         }
456
457         alignPanel.paintAlignment(true);
458         try
459         {
460           Thread.sleep(100);
461         } catch (Exception ex)
462         {
463         }
464       }
465     }
466   }
467 }