f859a16bccbebe794030455e5338d3b6af913e2d
[jalview.git] / src / jalview / gui / IdPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 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   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,sp.seqCanvas.fr.minmax);
127             sz+=tip.length();
128             maxWidth = Math.max(maxWidth, sz);
129           }
130         }
131       }
132
133       if (maxWidth > 60)
134       {
135         tip.insert(0, "<table width=350 border=0><tr><td><i>");
136         tip.append("</i></td></tr></table>");
137       }
138
139       tip.append("</html>");
140
141       setToolTipText("<html>" + sequence.getDisplayId(true)
142               + tip.toString());
143     }
144   }
145
146   /**
147    * DOCUMENT ME!
148    * 
149    * @param e
150    *                DOCUMENT ME!
151    */
152   public void mouseDragged(MouseEvent e)
153   {
154     mouseDragging = true;
155
156     int seq = Math.max(0, alignPanel.seqPanel.findSeq(e));
157
158     if (seq < lastid)
159     {
160       selectSeqs(lastid - 1, seq);
161     }
162     else if (seq > lastid)
163     {
164       selectSeqs(lastid + 1, seq);
165     }
166
167     lastid = seq;
168     alignPanel.paintAlignment(true);
169   }
170
171   public void mouseWheelMoved(MouseWheelEvent e)
172   {
173     e.consume();
174     if (e.getWheelRotation() > 0)
175     {
176       alignPanel.scrollUp(false);
177     }
178     else
179     {
180       alignPanel.scrollUp(true);
181     }
182   }
183
184   /**
185    * DOCUMENT ME!
186    * 
187    * @param e
188    *                DOCUMENT ME!
189    */
190   public void mouseClicked(MouseEvent e)
191   {
192     if (e.getClickCount() < 2)
193     {
194       return;
195     }
196
197     java.util.Vector links = Preferences.sequenceURLLinks;
198     if (links == null || links.size() < 1)
199     {
200       return;
201     }
202
203     int seq = alignPanel.seqPanel.findSeq(e);
204     String url = null;
205     int i = 0;
206     String id = av.getAlignment().getSequenceAt(seq).getName();
207     while (url == null && i < links.size())
208     {
209       // DEFAULT LINK IS FIRST IN THE LINK LIST
210       // BUT IF ITS A REGEX AND DOES NOT MATCH THE NEXT ONE WILL BE TRIED
211       url = links.elementAt(i++).toString();
212       jalview.util.UrlLink urlLink = null;
213       try
214       {
215         urlLink = new UrlLink(url);
216       } catch (Exception foo)
217       {
218         jalview.bin.Cache.log.error("Exception for URLLink '" + url + "'",
219                 foo);
220         url = null;
221         continue;
222       }
223       ;
224       if (!urlLink.isValid())
225       {
226         jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
227         url = null;
228         continue;
229       }
230
231       String urls[] = urlLink.makeUrls(id, true);
232       if (urls == null || urls[0] == null || urls[0].length() < 4)
233       {
234         url = null;
235         continue;
236       }
237       // just take first URL made from regex
238       url = urls[1];
239     }
240     try
241     {
242       jalview.util.BrowserLauncher.openURL(url);
243     } catch (Exception ex)
244     {
245       JOptionPane
246               .showInternalMessageDialog(
247                       Desktop.desktop,
248                       "Unixers: Couldn't find default web browser."
249                               + "\nAdd the full path to your browser in Preferences.",
250                       "Web browser not found", JOptionPane.WARNING_MESSAGE);
251       ex.printStackTrace();
252     }
253
254   }
255
256   /**
257    * DOCUMENT ME!
258    * 
259    * @param e
260    *                DOCUMENT ME!
261    */
262   public void mouseEntered(MouseEvent e)
263   {
264     if (scrollThread != null)
265     {
266       scrollThread.running = false;
267     }
268   }
269
270   /**
271    * DOCUMENT ME!
272    * 
273    * @param e
274    *                DOCUMENT ME!
275    */
276   public void mouseExited(MouseEvent e)
277   {
278     if (av.getWrapAlignment())
279     {
280       return;
281     }
282
283     if (mouseDragging && (e.getY() < 0) && (av.getStartSeq() > 0))
284     {
285       scrollThread = new ScrollThread(true);
286     }
287
288     if (mouseDragging && (e.getY() >= getHeight())
289             && (av.alignment.getHeight() > av.getEndSeq()))
290     {
291       scrollThread = new ScrollThread(false);
292     }
293   }
294
295   /**
296    * DOCUMENT ME!
297    * 
298    * @param e
299    *                DOCUMENT ME!
300    */
301   public void mousePressed(MouseEvent e)
302   {
303     if (e.getClickCount() == 2)
304     {
305       return;
306     }
307
308     int seq = alignPanel.seqPanel.findSeq(e);
309
310     if (javax.swing.SwingUtilities.isRightMouseButton(e))
311     {
312       Sequence sq = (Sequence) av
313       .getAlignment().getSequenceAt(seq);
314       // build a new links menu based on the current links + any non-positional features
315       Vector nlinks = new Vector(Preferences.sequenceURLLinks);
316       SequenceFeature sf[] = sq.getDatasetSequence().getSequenceFeatures();
317       for (int sl=0;sf!=null && sl<sf.length;sl++)
318       {
319         if (sf[sl].begin==sf[sl].end && sf[sl].begin==0)
320         {
321           if (sf[sl].links!=null && sf[sl].links.size()>0)
322           {
323             for (int l=0, lSize=sf[sl].links.size(); l<lSize; l++)
324             { 
325               nlinks.addElement(sf[sl].links.elementAt(l));
326             }
327           }
328         }
329       }
330       
331       jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(alignPanel,
332               sq,
333               nlinks);
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     av.sendSelection();
357     alignPanel.paintAlignment(true);
358   }
359
360   /**
361    * DOCUMENT ME!
362    * 
363    * @param seq
364    *                DOCUMENT ME!
365    */
366   void selectSeq(int seq)
367   {
368     lastid = seq;
369
370     SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
371     av.getSelectionGroup().addOrRemove(pickedSeq, true);
372   }
373
374   /**
375    * DOCUMENT ME!
376    * 
377    * @param start
378    *                DOCUMENT ME!
379    * @param end
380    *                DOCUMENT ME!
381    */
382   void selectSeqs(int start, int end)
383   {
384     if (av.getSelectionGroup() == null)
385     {
386       return;
387     }
388
389     if (end >= av.getAlignment().getHeight())
390     {
391       end = av.getAlignment().getHeight() - 1;
392     }
393
394     lastid = start;
395
396     if (end < start)
397     {
398       int tmp = start;
399       start = end;
400       end = tmp;
401       lastid = end;
402     }
403
404     for (int i = start; i <= end; i++)
405     {
406       av.getSelectionGroup().addSequence(
407               av.getAlignment().getSequenceAt(i), true);
408     }
409   }
410
411   /**
412    * DOCUMENT ME!
413    * 
414    * @param e
415    *                DOCUMENT ME!
416    */
417   public void mouseReleased(MouseEvent e)
418   {
419     if (scrollThread != null)
420     {
421       scrollThread.running = false;
422     }
423
424     mouseDragging = false;
425     PaintRefresher.Refresh(this, av.getSequenceSetId());
426   }
427
428   /**
429    * DOCUMENT ME!
430    * 
431    * @param found
432    *                DOCUMENT ME!
433    */
434   public void highlightSearchResults(java.util.Vector found)
435   {
436     idCanvas.setHighlighted(found);
437
438     if (found == null)
439     {
440       return;
441     }
442
443     int index = av.alignment.findIndex((SequenceI) found.get(0));
444
445     // do we need to scroll the panel?
446     if ((av.getStartSeq() > index) || (av.getEndSeq() < index))
447     {
448       alignPanel.setScrollValues(av.getStartRes(), index);
449     }
450   }
451
452   // this class allows scrolling off the bottom of the visible alignment
453   class ScrollThread extends Thread
454   {
455     boolean running = false;
456
457     boolean up = true;
458
459     public ScrollThread(boolean up)
460     {
461       this.up = up;
462       start();
463     }
464
465     public void stopScrolling()
466     {
467       running = false;
468     }
469
470     public void run()
471     {
472       running = true;
473
474       while (running)
475       {
476         if (alignPanel.scrollUp(up))
477         {
478           // scroll was ok, so add new sequence to selection
479           int seq = av.getStartSeq();
480
481           if (!up)
482           {
483             seq = av.getEndSeq();
484           }
485
486           if (seq < lastid)
487           {
488             selectSeqs(lastid - 1, seq);
489           }
490           else if (seq > lastid)
491           {
492             selectSeqs(lastid + 1, seq);
493           }
494
495           lastid = seq;
496         }
497         else
498         {
499           running = false;
500         }
501
502         alignPanel.paintAlignment(false);
503
504         try
505         {
506           Thread.sleep(100);
507         } catch (Exception ex)
508         {
509         }
510       }
511     }
512   }
513 }