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