JAL-1620 version bump and release notes
[jalview.git] / src / jalview / appletgui / IdPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
3  * Copyright (C) 2014 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 java.awt.*;
24 import java.awt.event.*;
25 import java.util.List;
26 import java.util.Vector;
27
28 import jalview.datamodel.*;
29 import jalview.util.UrlLink;
30
31 public class IdPanel extends Panel implements MouseListener,
32         MouseMotionListener
33 {
34
35   protected IdCanvas idCanvas;
36
37   protected AlignViewport av;
38
39   protected AlignmentPanel alignPanel;
40
41   ScrollThread scrollThread = null;
42
43   int offy;
44
45   int width;
46
47   int lastid = -1;
48
49   boolean mouseDragging = false;
50
51   java.util.Vector links = new java.util.Vector();
52
53   public IdPanel(AlignViewport av, AlignmentPanel parent)
54   {
55     this.av = av;
56     alignPanel = parent;
57     idCanvas = new IdCanvas(av);
58     setLayout(new BorderLayout());
59     add(idCanvas, BorderLayout.CENTER);
60     idCanvas.addMouseListener(this);
61     idCanvas.addMouseMotionListener(this);
62
63     String label, url;
64     // TODO: add in group link parameter
65     if (av.applet != null)
66     {
67       for (int i = 1; i < 10; i++)
68       {
69         label = av.applet.getParameter("linkLabel_" + i);
70         url = av.applet.getParameter("linkURL_" + i);
71
72         if (label != null && url != null)
73         {
74           links.addElement(label + "|" + url);
75         }
76
77       }
78     }
79     {
80       // upgrade old SRS link
81       int srsPos = links
82               .indexOf("SRS|http://srs.ebi.ac.uk/srsbin/cgi-bin/wgetz?-newId+(([uniprot-all:$SEQUENCE_ID$]))+-view+SwissEntry");
83       if (srsPos > -1)
84       {
85         links.setElementAt(
86                 "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$",
87                 srsPos);
88       }
89     }
90     if (links.size() < 1)
91     {
92       links = new java.util.Vector();
93       links.addElement("EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$");
94     }
95   }
96
97   Tooltip tooltip;
98
99   public void mouseMoved(MouseEvent e)
100   {
101     int seq = alignPanel.seqPanel.findSeq(e);
102
103     SequenceI sequence = av.getAlignment().getSequenceAt(seq);
104
105     // look for non-pos features
106     StringBuffer tooltiptext = new StringBuffer();
107     if (sequence != null)
108     {
109       if (sequence.getDescription() != null)
110       {
111         tooltiptext.append(sequence.getDescription());
112         tooltiptext.append("\n");
113       }
114
115       SequenceFeature sf[] = sequence.getSequenceFeatures();
116       for (int sl = 0; sf != null && sl < sf.length; sl++)
117       {
118         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
119         {
120           boolean nl = false;
121           if (sf[sl].getFeatureGroup() != null)
122           {
123             tooltiptext.append(sf[sl].getFeatureGroup());
124             nl = true;
125           }
126           ;
127           if (sf[sl].getType() != null)
128           {
129             tooltiptext.append(" ");
130             tooltiptext.append(sf[sl].getType());
131             nl = true;
132           }
133           ;
134           if (sf[sl].getDescription() != null)
135           {
136             tooltiptext.append(" ");
137             tooltiptext.append(sf[sl].getDescription());
138             nl = true;
139           }
140           ;
141           if (sf[sl].getScore() != Float.NaN && sf[sl].getScore() != 0f)
142           {
143             tooltiptext.append(" Score = ");
144             tooltiptext.append(sf[sl].getScore());
145             nl = true;
146           }
147           ;
148           if (sf[sl].getStatus() != null && sf[sl].getStatus().length() > 0)
149           {
150             tooltiptext.append(" (");
151             tooltiptext.append(sf[sl].getStatus());
152             tooltiptext.append(")");
153             nl = true;
154           }
155           ;
156           if (nl)
157           {
158             tooltiptext.append("\n");
159           }
160         }
161       }
162     }
163     if (tooltiptext.length() == 0)
164     {
165       // nothing to display - so clear tooltip if one is visible
166       if (tooltip != null)
167       {
168         tooltip.setVisible(false);
169       }
170       tooltip = null;
171       tooltiptext = null;
172       return;
173     }
174     if (tooltip == null)
175     {
176       tooltip = new Tooltip(sequence.getDisplayId(true) + "\n"
177               + tooltiptext.toString(), idCanvas);
178     }
179     else
180     {
181       tooltip.setTip(sequence.getDisplayId(true) + "\n"
182               + tooltiptext.toString());
183     }
184     tooltiptext = null;
185   }
186
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   public void mouseClicked(MouseEvent e)
207   {
208     if (e.getClickCount() < 2)
209     {
210       return;
211     }
212
213     // DEFAULT LINK IS FIRST IN THE LINK LIST
214     int seq = alignPanel.seqPanel.findSeq(e);
215     SequenceI sq = av.getAlignment().getSequenceAt(seq);
216     if (sq == null)
217     {
218       return;
219     }
220     String id = sq.getName();
221
222     String target = null;
223     String url = null;
224     int i = 0;
225     while (url == null && i < links.size())
226     {
227       // DEFAULT LINK IS FIRST IN THE LINK LIST
228       // BUT IF ITS A REGEX AND DOES NOT MATCH THE NEXT ONE WILL BE TRIED
229       url = links.elementAt(i++).toString();
230       jalview.util.UrlLink urlLink = null;
231       try
232       {
233         urlLink = new UrlLink(url);
234         target = urlLink.getTarget();
235       } catch (Exception foo)
236       {
237         System.err.println("Exception for URLLink '" + url + "'");
238         foo.printStackTrace();
239         url = null;
240         continue;
241       }
242       ;
243       if (!urlLink.isValid())
244       {
245         System.err.println(urlLink.getInvalidMessage());
246         url = null;
247         continue;
248       }
249
250       String urls[] = urlLink.makeUrls(id, true);
251       if (urls == null || urls[0] == null || urls[0].length() < 1)
252       {
253         url = null;
254         continue;
255       }
256       // just take first URL made from regex
257       url = urls[1];
258     }
259     try
260     {
261
262       alignPanel.alignFrame.showURL(url, target);
263     } catch (Exception ex)
264     {
265       ex.printStackTrace();
266     }
267   }
268
269   public void mouseEntered(MouseEvent e)
270   {
271     if (scrollThread != null)
272     {
273       scrollThread.running = false;
274     }
275   }
276
277   public void mouseExited(MouseEvent e)
278   {
279     if (av.getWrapAlignment())
280     {
281       return;
282     }
283
284     if (mouseDragging && e.getY() < 0 && av.getStartSeq() > 0)
285     {
286       scrollThread = new ScrollThread(true);
287     }
288
289     if (mouseDragging && e.getY() >= getSize().height
290             && av.getAlignment().getHeight() > av.getEndSeq())
291     {
292       scrollThread = new ScrollThread(false);
293     }
294   }
295
296   public void mousePressed(MouseEvent e)
297   {
298     if (e.getClickCount() > 1)
299     {
300       return;
301     }
302
303     int y = e.getY();
304     if (av.getWrapAlignment())
305     {
306       y -= 2 * av.charHeight;
307     }
308
309     int seq = alignPanel.seqPanel.findSeq(e);
310
311     if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
312     {
313       Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq);
314
315       // build a new links menu based on the current links + any non-positional
316       // features
317       Vector nlinks = new Vector();
318       for (int l = 0, lSize = links.size(); l < lSize; l++)
319       {
320         nlinks.addElement(links.elementAt(l));
321       }
322       SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
323       for (int sl = 0; sf != null && sl < sf.length; sl++)
324       {
325         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
326         {
327           if (sf[sl].links != null && sf[sl].links.size() > 0)
328           {
329             for (int l = 0, lSize = sf[sl].links.size(); l < lSize; l++)
330             {
331               nlinks.addElement(sf[sl].links.elementAt(l));
332             }
333           }
334         }
335       }
336
337       APopupMenu popup = new APopupMenu(alignPanel, sq, nlinks);
338       this.add(popup);
339       popup.show(this, e.getX(), e.getY());
340       return;
341     }
342
343     if ((av.getSelectionGroup() == null)
344             || ((!e.isControlDown() && !e.isShiftDown()) && av
345                     .getSelectionGroup() != null))
346     {
347       av.setSelectionGroup(new SequenceGroup());
348       av.getSelectionGroup().setStartRes(0);
349       av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
350     }
351
352     if (e.isShiftDown() && lastid != -1)
353     {
354       selectSeqs(lastid, seq);
355     }
356     else
357     {
358       selectSeq(seq);
359     }
360
361     alignPanel.paintAlignment(false);
362   }
363
364   void selectSeq(int seq)
365   {
366     lastid = seq;
367     SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
368     av.getSelectionGroup().addOrRemove(pickedSeq, true);
369   }
370
371   void selectSeqs(int start, int end)
372   {
373
374     lastid = start;
375
376     if (end >= av.getAlignment().getHeight())
377     {
378       end = av.getAlignment().getHeight() - 1;
379     }
380
381     if (end < start)
382     {
383       int tmp = start;
384       start = end;
385       end = tmp;
386       lastid = end;
387     }
388     if (av.getSelectionGroup() == null)
389     {
390       av.setSelectionGroup(new SequenceGroup());
391     }
392     for (int i = start; i <= end; i++)
393     {
394       av.getSelectionGroup().addSequence(
395               av.getAlignment().getSequenceAt(i), i == end);
396     }
397
398   }
399
400   public void mouseReleased(MouseEvent e)
401   {
402     if (scrollThread != null)
403     {
404       scrollThread.running = false;
405     }
406
407     if (av.getSelectionGroup() != null)
408     {
409       av.getSelectionGroup().recalcConservation();
410     }
411
412     mouseDragging = false;
413     PaintRefresher.Refresh(this, av.getSequenceSetId());
414     // always send selection message when mouse is released
415     av.sendSelection();
416   }
417
418   public void highlightSearchResults(List<SequenceI> list)
419   {
420     idCanvas.setHighlighted(list);
421
422     if (list == null)
423     {
424       return;
425     }
426
427     int index = av.getAlignment().findIndex(list.get(0));
428
429     // do we need to scroll the panel?
430     if (av.getStartSeq() > index || av.getEndSeq() < index)
431     {
432       alignPanel.setScrollValues(av.getStartRes(), index);
433     }
434   }
435
436   // this class allows scrolling off the bottom of the visible alignment
437   class ScrollThread extends Thread
438   {
439     boolean running = false;
440
441     boolean up = true;
442
443     public ScrollThread(boolean up)
444     {
445       this.up = up;
446       start();
447     }
448
449     public void stopScrolling()
450     {
451       running = false;
452     }
453
454     public void run()
455     {
456       running = true;
457       while (running)
458       {
459         if (alignPanel.scrollUp(up))
460         {
461           // scroll was ok, so add new sequence to selection
462           int seq = av.getStartSeq();
463           if (!up)
464           {
465             seq = av.getEndSeq();
466           }
467
468           if (seq < lastid)
469           {
470             selectSeqs(lastid - 1, seq);
471           }
472           else if (seq > lastid && seq < av.getAlignment().getHeight())
473           {
474             selectSeqs(lastid + 1, seq);
475           }
476
477           lastid = seq;
478         }
479         else
480         {
481           running = false;
482         }
483
484         alignPanel.paintAlignment(true);
485         try
486         {
487           Thread.sleep(100);
488         } catch (Exception ex)
489         {
490         }
491       }
492     }
493   }
494
495 }