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