update author list in license for (JAL-826)
[jalview.git] / src / jalview / gui / IdPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.gui;
19
20 import java.awt.*;
21 import java.awt.event.*;
22 import java.util.Vector;
23
24 import javax.swing.*;
25
26 import jalview.datamodel.*;
27 import jalview.util.UrlLink;
28
29 /**
30  * DOCUMENT ME!
31  * 
32  * @author $author$
33  * @version $Revision$
34  */
35 public class IdPanel extends JPanel implements MouseListener,
36         MouseMotionListener, MouseWheelListener
37 {
38   protected IdCanvas idCanvas;
39
40   protected AlignViewport av;
41
42   protected AlignmentPanel alignPanel;
43
44   ScrollThread scrollThread = null;
45
46   String linkImageURL;
47
48   int offy;
49
50   // int width;
51   int lastid = -1;
52
53   boolean mouseDragging = false;
54
55   /**
56    * Creates a new IdPanel object.
57    * 
58    * @param av
59    *          DOCUMENT ME!
60    * @param parent
61    *          DOCUMENT ME!
62    */
63   public IdPanel(AlignViewport av, AlignmentPanel parent)
64   {
65     this.av = av;
66     alignPanel = parent;
67     idCanvas = new IdCanvas(av);
68     linkImageURL = getClass().getResource("/images/link.gif").toString();
69     setLayout(new BorderLayout());
70     add(idCanvas, BorderLayout.CENTER);
71     addMouseListener(this);
72     addMouseMotionListener(this);
73     addMouseWheelListener(this);
74     ToolTipManager.sharedInstance().registerComponent(this);
75   }
76
77   /**
78    * DOCUMENT ME!
79    * 
80    * @param e
81    *          DOCUMENT ME!
82    */
83   public void mouseMoved(MouseEvent e)
84   {
85     SeqPanel sp = alignPanel.seqPanel;
86     int seq = Math.max(0, sp.findSeq(e));
87     String tmp;
88     if (seq > -1 && seq < av.alignment.getHeight())
89     {
90       SequenceI sequence = av.alignment.getSequenceAt(seq);
91       StringBuffer tip = new StringBuffer();
92       tip.append("<i>");
93
94       int maxWidth = 0;
95       if (sequence.getDescription() != null)
96       {
97         tmp = sequence.getDescription();
98         tip.append("<br>" + tmp);
99         maxWidth = Math.max(maxWidth, tmp.length());
100       }
101
102       DBRefEntry[] dbrefs = sequence.getDatasetSequence().getDBRef();
103       if (av.isShowDbRefs() && dbrefs != null)
104       {
105         for (int i = 0; i < dbrefs.length; i++)
106         {
107           tip.append("<br>");
108           tmp = dbrefs[i].getSource() + " " + dbrefs[i].getAccessionId();
109           tip.append(tmp);
110           maxWidth = Math.max(maxWidth, tmp.length());
111         }
112       }
113
114       // ADD NON POSITIONAL SEQUENCE INFO
115       SequenceFeature[] features = sequence.getDatasetSequence()
116               .getSequenceFeatures();
117       SequenceFeature[] tfeat = new SequenceFeature[1];
118       if (av.isShowNpFeats() && features != null)
119       {
120         for (int i = 0; i < features.length; i++)
121         {
122           if (features[i].begin == 0 && features[i].end == 0)
123           {
124             int sz = -tip.length();
125             tfeat[0] = features[i];
126             sp.appendFeatures(tip, linkImageURL, 0, tfeat,
127                     sp.seqCanvas.fr.minmax);
128             sz += tip.length();
129             maxWidth = Math.max(maxWidth, sz);
130           }
131         }
132       }
133
134       if (maxWidth > 60)
135       {
136         tip.insert(0, "<table width=350 border=0><tr><td><i>");
137         tip.append("</i></td></tr></table>");
138       }
139
140       tip.append("</html>");
141
142       setToolTipText("<html>" + sequence.getDisplayId(true) + " "
143               + tip.toString());
144     }
145   }
146
147   /**
148    * DOCUMENT ME!
149    * 
150    * @param e
151    *          DOCUMENT ME!
152    */
153   public void mouseDragged(MouseEvent e)
154   {
155     mouseDragging = true;
156
157     int seq = Math.max(0, alignPanel.seqPanel.findSeq(e));
158
159     if (seq < lastid)
160     {
161       selectSeqs(lastid - 1, seq);
162     }
163     else if (seq > lastid)
164     {
165       selectSeqs(lastid + 1, seq);
166     }
167
168     lastid = seq;
169     alignPanel.paintAlignment(true);
170   }
171
172   public void mouseWheelMoved(MouseWheelEvent e)
173   {
174     e.consume();
175     if (e.getWheelRotation() > 0)
176     {
177       alignPanel.scrollUp(false);
178     }
179     else
180     {
181       alignPanel.scrollUp(true);
182     }
183   }
184
185   /**
186    * DOCUMENT ME!
187    * 
188    * @param e
189    *          DOCUMENT ME!
190    */
191   public void mouseClicked(MouseEvent e)
192   {
193     if (e.getClickCount() < 2)
194     {
195       return;
196     }
197
198     java.util.Vector links = Preferences.sequenceURLLinks;
199     if (links == null || links.size() < 1)
200     {
201       return;
202     }
203
204     int seq = alignPanel.seqPanel.findSeq(e);
205     String url = null;
206     int i = 0;
207     String id = av.getAlignment().getSequenceAt(seq).getName();
208     while (url == null && i < links.size())
209     {
210       // DEFAULT LINK IS FIRST IN THE LINK LIST
211       // BUT IF ITS A REGEX AND DOES NOT MATCH THE NEXT ONE WILL BE TRIED
212       url = links.elementAt(i++).toString();
213       jalview.util.UrlLink urlLink = null;
214       try
215       {
216         urlLink = new UrlLink(url);
217       } catch (Exception foo)
218       {
219         jalview.bin.Cache.log.error("Exception for URLLink '" + url + "'",
220                 foo);
221         url = null;
222         continue;
223       }
224       ;
225       if (!urlLink.isValid())
226       {
227         jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
228         url = null;
229         continue;
230       }
231
232       String urls[] = urlLink.makeUrls(id, true);
233       if (urls == null || urls[0] == null || urls[0].length() < 4)
234       {
235         url = null;
236         continue;
237       }
238       // just take first URL made from regex
239       url = urls[1];
240     }
241     try
242     {
243       jalview.util.BrowserLauncher.openURL(url);
244     } catch (Exception ex)
245     {
246       JOptionPane
247               .showInternalMessageDialog(
248                       Desktop.desktop,
249                       "Unixers: Couldn't find default web browser."
250                               + "\nAdd the full path to your browser in Preferences.",
251                       "Web browser not found", JOptionPane.WARNING_MESSAGE);
252       ex.printStackTrace();
253     }
254
255   }
256
257   /**
258    * DOCUMENT ME!
259    * 
260    * @param e
261    *          DOCUMENT ME!
262    */
263   public void mouseEntered(MouseEvent e)
264   {
265     if (scrollThread != null)
266     {
267       scrollThread.running = false;
268     }
269   }
270
271   /**
272    * DOCUMENT ME!
273    * 
274    * @param e
275    *          DOCUMENT ME!
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() >= getHeight())
290             && (av.alignment.getHeight() > av.getEndSeq()))
291     {
292       scrollThread = new ScrollThread(false);
293     }
294   }
295
296   /**
297    * DOCUMENT ME!
298    * 
299    * @param e
300    *          DOCUMENT ME!
301    */
302   public void mousePressed(MouseEvent e)
303   {
304     if (e.getClickCount() == 2)
305     {
306       return;
307     }
308
309     int seq = alignPanel.seqPanel.findSeq(e);
310
311     if (javax.swing.SwingUtilities.isRightMouseButton(e))
312     {
313       Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq);
314       // build a new links menu based on the current links + any non-positional
315       // features
316       Vector nlinks = new Vector(Preferences.sequenceURLLinks);
317       SequenceFeature sf[] = sq==null ? null : sq.getDatasetSequence().getSequenceFeatures();
318       for (int sl = 0; sf != null && sl < sf.length; sl++)
319       {
320         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
321         {
322           if (sf[sl].links != null && sf[sl].links.size() > 0)
323           {
324             for (int l = 0, lSize = sf[sl].links.size(); l < lSize; l++)
325             {
326               nlinks.addElement(sf[sl].links.elementAt(l));
327             }
328           }
329         }
330       }
331
332       jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(alignPanel, sq,
333               nlinks, new Vector(Preferences.getGroupURLLinks()));
334       pop.show(this, e.getX(), e.getY());
335
336       return;
337     }
338
339     if ((av.getSelectionGroup() == null)
340             || ((!e.isControlDown() && !e.isShiftDown()) && av
341                     .getSelectionGroup() != null))
342     {
343       av.setSelectionGroup(new SequenceGroup());
344       av.getSelectionGroup().setStartRes(0);
345       av.getSelectionGroup().setEndRes(av.alignment.getWidth() - 1);
346     }
347
348     if (e.isShiftDown() && (lastid != -1))
349     {
350       selectSeqs(lastid, seq);
351     }
352     else
353     {
354       selectSeq(seq);
355     }
356     alignPanel.paintAlignment(true);
357   }
358
359   /**
360    * DOCUMENT ME!
361    * 
362    * @param seq
363    *          DOCUMENT ME!
364    */
365   void selectSeq(int seq)
366   {
367     lastid = seq;
368
369     SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
370     av.getSelectionGroup().addOrRemove(pickedSeq, true);
371   }
372
373   /**
374    * DOCUMENT ME!
375    * 
376    * @param start
377    *          DOCUMENT ME!
378    * @param end
379    *          DOCUMENT ME!
380    */
381   void selectSeqs(int start, int end)
382   {
383     if (av.getSelectionGroup() == null)
384     {
385       return;
386     }
387
388     if (end >= av.getAlignment().getHeight())
389     {
390       end = av.getAlignment().getHeight() - 1;
391     }
392
393     lastid = start;
394
395     if (end < start)
396     {
397       int tmp = start;
398       start = end;
399       end = tmp;
400       lastid = end;
401     }
402
403     for (int i = start; i <= end; i++)
404     {
405       av.getSelectionGroup().addSequence(
406               av.getAlignment().getSequenceAt(i), true);
407     }
408   }
409
410   /**
411    * DOCUMENT ME!
412    * 
413    * @param e
414    *          DOCUMENT ME!
415    */
416   public void mouseReleased(MouseEvent e)
417   {
418     if (scrollThread != null)
419     {
420       scrollThread.running = false;
421     }
422
423     mouseDragging = false;
424     PaintRefresher.Refresh(this, av.getSequenceSetId());
425     // always send selection message when mouse is released
426     av.sendSelection();
427   }
428
429   /**
430    * DOCUMENT ME!
431    * 
432    * @param found
433    *          DOCUMENT ME!
434    */
435   public void highlightSearchResults(java.util.Vector found)
436   {
437     idCanvas.setHighlighted(found);
438
439     if (found == null)
440     {
441       return;
442     }
443
444     int index = av.alignment.findIndex((SequenceI) found.get(0));
445
446     // do we need to scroll the panel?
447     if ((av.getStartSeq() > index) || (av.getEndSeq() < index))
448     {
449       alignPanel.setScrollValues(av.getStartRes(), index);
450     }
451   }
452
453   // this class allows scrolling off the bottom of the visible alignment
454   class ScrollThread extends Thread
455   {
456     boolean running = false;
457
458     boolean up = true;
459
460     public ScrollThread(boolean up)
461     {
462       this.up = up;
463       start();
464     }
465
466     public void stopScrolling()
467     {
468       running = false;
469     }
470
471     public void run()
472     {
473       running = true;
474
475       while (running)
476       {
477         if (alignPanel.scrollUp(up))
478         {
479           // scroll was ok, so add new sequence to selection
480           int seq = av.getStartSeq();
481
482           if (!up)
483           {
484             seq = av.getEndSeq();
485           }
486
487           if (seq < lastid)
488           {
489             selectSeqs(lastid - 1, seq);
490           }
491           else if (seq > lastid)
492           {
493             selectSeqs(lastid + 1, seq);
494           }
495
496           lastid = seq;
497         }
498         else
499         {
500           running = false;
501         }
502
503         alignPanel.paintAlignment(false);
504
505         try
506         {
507           Thread.sleep(100);
508         } catch (Exception ex)
509         {
510         }
511       }
512     }
513   }
514 }