Merge branch 'develop' into features/JAL-2446NCList
[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.SequenceFeature;
24 import jalview.datamodel.SequenceGroup;
25 import jalview.datamodel.SequenceI;
26 import jalview.urls.api.UrlProviderFactoryI;
27 import jalview.urls.api.UrlProviderI;
28 import jalview.urls.applet.AppletUrlProviderFactory;
29 import jalview.viewmodel.AlignmentViewport;
30
31 import java.awt.BorderLayout;
32 import java.awt.Panel;
33 import java.awt.event.InputEvent;
34 import java.awt.event.MouseEvent;
35 import java.awt.event.MouseListener;
36 import java.awt.event.MouseMotionListener;
37 import java.util.ArrayList;
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 viewport, AlignmentPanel parent)
60   {
61     this.av = viewport;
62     alignPanel = parent;
63     idCanvas = new IdCanvas(viewport);
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 (viewport.applet != null)
75     {
76       for (int i = 1; i < 10; i++)
77       {
78         label = viewport.applet.getParameter("linkLabel_" + i);
79         url = viewport.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 = viewport.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     StringBuffer tooltiptext = new StringBuffer();
109     if (sequence == null)
110     {
111       return;
112     }
113     if (sequence.getDescription() != null)
114     {
115       tooltiptext.append(sequence.getDescription());
116       tooltiptext.append("\n");
117     }
118
119     for (SequenceFeature sf : sequence.getFeatures()
120             .getNonPositionalFeatures())
121     {
122       boolean nl = false;
123       if (sf.getFeatureGroup() != null)
124       {
125         tooltiptext.append(sf.getFeatureGroup());
126         nl = true;
127       }
128       if (sf.getType() != null)
129       {
130         tooltiptext.append(" ");
131         tooltiptext.append(sf.getType());
132         nl = true;
133       }
134       if (sf.getDescription() != null)
135       {
136         tooltiptext.append(" ");
137         tooltiptext.append(sf.getDescription());
138         nl = true;
139       }
140       if (!Float.isNaN(sf.getScore()) && sf.getScore() != 0f)
141       {
142         tooltiptext.append(" Score = ");
143         tooltiptext.append(sf.getScore());
144         nl = true;
145       }
146       if (sf.getStatus() != null && sf.getStatus().length() > 0)
147       {
148         tooltiptext.append(" (");
149         tooltiptext.append(sf.getStatus());
150         tooltiptext.append(")");
151         nl = true;
152       }
153       if (nl)
154       {
155         tooltiptext.append("\n");
156       }
157     }
158
159     if (tooltiptext.length() == 0)
160     {
161       // nothing to display - so clear tooltip if one is visible
162       if (tooltip != null)
163       {
164         tooltip.setVisible(false);
165       }
166       tooltip = null;
167       tooltiptext = null;
168       return;
169     }
170     if (tooltip == null)
171     {
172       tooltip = new Tooltip(sequence.getDisplayId(true) + "\n"
173               + tooltiptext.toString(), idCanvas);
174     }
175     else
176     {
177       tooltip.setTip(sequence.getDisplayId(true) + "\n"
178               + tooltiptext.toString());
179     }
180     tooltiptext = null;
181   }
182
183   @Override
184   public void mouseDragged(MouseEvent e)
185   {
186     mouseDragging = true;
187
188     int seq = Math.max(0, alignPanel.seqPanel.findSeq(e));
189
190     if (seq < lastid)
191     {
192       selectSeqs(lastid - 1, seq);
193     }
194     else if (seq > lastid)
195     {
196       selectSeqs(lastid + 1, seq);
197     }
198
199     lastid = seq;
200     alignPanel.paintAlignment(false);
201   }
202
203   @Override
204   public void mouseClicked(MouseEvent e)
205   {
206     if (e.getClickCount() < 2)
207     {
208       return;
209     }
210
211     // get the sequence details
212     int seq = alignPanel.seqPanel.findSeq(e);
213     SequenceI sq = av.getAlignment().getSequenceAt(seq);
214     if (sq == null)
215     {
216       return;
217     }
218     String id = sq.getName();
219
220     // get the default url with the sequence details filled in
221     if (urlProvider == null)
222     {
223       return;
224     }
225     String url = urlProvider.getPrimaryUrl(id);
226     String target = urlProvider.getPrimaryTarget(id);
227     try
228     {
229       alignPanel.alignFrame.showURL(url, target);
230     } catch (Exception ex)
231     {
232       ex.printStackTrace();
233     }
234   }
235
236   @Override
237   public void mouseEntered(MouseEvent e)
238   {
239     if (scrollThread != null)
240     {
241       scrollThread.running = false;
242     }
243   }
244
245   @Override
246   public void mouseExited(MouseEvent e)
247   {
248     if (av.getWrapAlignment())
249     {
250       return;
251     }
252
253     if (mouseDragging && e.getY() < 0 && av.getRanges().getStartSeq() > 0)
254     {
255       scrollThread = new ScrollThread(true);
256     }
257
258     if (mouseDragging && e.getY() >= getSize().height
259             && av.getAlignment().getHeight() > av.getRanges().getEndSeq())
260     {
261       scrollThread = new ScrollThread(false);
262     }
263   }
264
265   @Override
266   public void mousePressed(MouseEvent e)
267   {
268     if (e.getClickCount() > 1)
269     {
270       return;
271     }
272
273     int y = e.getY();
274     if (av.getWrapAlignment())
275     {
276       y -= 2 * av.getCharHeight();
277     }
278
279     int seq = alignPanel.seqPanel.findSeq(e);
280
281     if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
282     {
283       SequenceI sq = av.getAlignment().getSequenceAt(seq);
284
285       /*
286        *  build a new links menu based on the current links
287        *  and any non-positional features
288        */
289       List<String> nlinks;
290       if (urlProvider != null)
291       {
292         nlinks = urlProvider.getLinksForMenu();
293       }
294       else
295       {
296         nlinks = new ArrayList<String>();
297       }
298
299       for (SequenceFeature sf : sq.getFeatures().getNonPositionalFeatures())
300       {
301         if (sf.links != null)
302         {
303           for (String link : sf.links)
304           {
305             nlinks.add(link);
306           }
307         }
308       }
309
310       APopupMenu popup = new APopupMenu(alignPanel, sq, nlinks);
311       this.add(popup);
312       popup.show(this, e.getX(), e.getY());
313       return;
314     }
315
316     if ((av.getSelectionGroup() == null)
317             || ((!jalview.util.Platform.isControlDown(e) && !e
318                     .isShiftDown()) && av.getSelectionGroup() != null))
319     {
320       av.setSelectionGroup(new SequenceGroup());
321       av.getSelectionGroup().setStartRes(0);
322       av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
323     }
324
325     if (e.isShiftDown() && lastid != -1)
326     {
327       selectSeqs(lastid, seq);
328     }
329     else
330     {
331       selectSeq(seq);
332     }
333
334     alignPanel.paintAlignment(false);
335   }
336
337   void selectSeq(int seq)
338   {
339     lastid = seq;
340     SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
341     av.getSelectionGroup().addOrRemove(pickedSeq, true);
342   }
343
344   void selectSeqs(int start, int end)
345   {
346
347     lastid = start;
348
349     if (end >= av.getAlignment().getHeight())
350     {
351       end = av.getAlignment().getHeight() - 1;
352     }
353
354     if (end < start)
355     {
356       int tmp = start;
357       start = end;
358       end = tmp;
359       lastid = end;
360     }
361     if (av.getSelectionGroup() == null)
362     {
363       av.setSelectionGroup(new SequenceGroup());
364     }
365     for (int i = start; i <= end; i++)
366     {
367       av.getSelectionGroup().addSequence(
368               av.getAlignment().getSequenceAt(i), i == end);
369     }
370
371   }
372
373   @Override
374   public void mouseReleased(MouseEvent e)
375   {
376     if (scrollThread != null)
377     {
378       scrollThread.running = false;
379     }
380
381     if (av.getSelectionGroup() != null)
382     {
383       av.getSelectionGroup().recalcConservation();
384     }
385
386     mouseDragging = false;
387     PaintRefresher.Refresh(this, av.getSequenceSetId());
388     // always send selection message when mouse is released
389     av.sendSelection();
390   }
391
392   public void highlightSearchResults(List<SequenceI> list)
393   {
394     idCanvas.setHighlighted(list);
395
396     if (list == null)
397     {
398       return;
399     }
400
401     int index = av.getAlignment().findIndex(list.get(0));
402
403     // do we need to scroll the panel?
404     if (av.getRanges().getStartSeq() > index
405             || av.getRanges().getEndSeq() < index)
406     {
407       av.getRanges().setStartSeq(index);
408     }
409   }
410
411   // this class allows scrolling off the bottom of the visible alignment
412   class ScrollThread extends Thread
413   {
414     boolean running = false;
415
416     boolean up = true;
417
418     public ScrollThread(boolean isUp)
419     {
420       this.up = isUp;
421       start();
422     }
423
424     public void stopScrolling()
425     {
426       running = false;
427     }
428
429     @Override
430     public void run()
431     {
432       running = true;
433       while (running)
434       {
435         if (av.getRanges().scrollUp(up))
436         {
437           // scroll was ok, so add new sequence to selection
438           int seq = av.getRanges().getStartSeq();
439           if (!up)
440           {
441             seq = av.getRanges().getEndSeq();
442           }
443
444           if (seq < lastid)
445           {
446             selectSeqs(lastid - 1, seq);
447           }
448           else if (seq > lastid && seq < av.getAlignment().getHeight())
449           {
450             selectSeqs(lastid + 1, seq);
451           }
452
453           lastid = seq;
454         }
455         else
456         {
457           running = false;
458         }
459
460         alignPanel.paintAlignment(true);
461         try
462         {
463           Thread.sleep(100);
464         } catch (Exception ex)
465         {
466         }
467       }
468     }
469   }
470 }