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