regex links for launching URL from single click on sequence ID
[jalview.git] / src / jalview / gui / IdPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 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 javax.swing.*;
24
25 import jalview.datamodel.*;
26 import jalview.util.UrlLink;
27
28 /**
29  * DOCUMENT ME!
30  *
31  * @author $author$
32  * @version $Revision$
33  */
34 public class IdPanel
35     extends JPanel implements MouseListener,
36     MouseMotionListener, MouseWheelListener
37 {
38   protected IdCanvas idCanvas;
39   protected AlignViewport av;
40   protected AlignmentPanel alignPanel;
41   ScrollThread scrollThread = null;
42   int offy;
43   // int width;
44   int lastid = -1;
45   boolean mouseDragging = false;
46
47   /**
48    * Creates a new IdPanel object.
49    *
50    * @param av DOCUMENT ME!
51    * @param parent DOCUMENT ME!
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     addMouseListener(this);
61     addMouseMotionListener(this);
62     addMouseWheelListener(this);
63     ToolTipManager.sharedInstance().registerComponent(this);
64   }
65
66   /**
67    * DOCUMENT ME!
68    *
69    * @param e DOCUMENT ME!
70    */
71   public void mouseMoved(MouseEvent e)
72   {
73     int seq = Math.max(0, alignPanel.seqPanel.findSeq(e));
74     String tmp;
75     if (seq > -1 && seq < av.alignment.getHeight())
76     {
77       SequenceI sequence = av.alignment.getSequenceAt(seq);
78       StringBuffer tip = new StringBuffer();
79       tip.append("<i>");
80
81       int maxWidth = 0;
82       if (sequence.getDescription() != null)
83       {
84         tmp = sequence.getDescription();
85         tip.append("<br>"+tmp);
86         maxWidth = Math.max(maxWidth, tmp.length());
87       }
88
89       DBRefEntry[] dbrefs = sequence.getDatasetSequence().getDBRef();
90       if (dbrefs != null)
91       {
92         for (int i = 0; i < dbrefs.length; i++)
93         {
94           tip.append("<br>");
95           tmp = dbrefs[i].getSource() + " " + dbrefs[i].getAccessionId();
96           tip.append(tmp);
97           maxWidth = Math.max(maxWidth, tmp.length());
98         }
99       }
100
101
102       //ADD NON POSITIONAL SEQUENCE INFO
103       SequenceFeature[] features = sequence.getDatasetSequence().
104           getSequenceFeatures();
105       if (features != null)
106       {
107         for (int i = 0; i < features.length; i++)
108         {
109           if (features[i].begin == 0 && features[i].end == 0)
110           {
111             tmp = features[i].featureGroup
112                        + " " + features[i].getType() + " " +
113                        features[i].description;
114             tip.append("<br>" + tmp);
115             maxWidth = Math.max(maxWidth, tmp.length());
116           }
117         }
118       }
119
120       if(maxWidth > 60)
121       {
122          tip.insert(0, "<table width=350 border=0><tr><td><i>");
123          tip.append("</i></td></tr></table>");
124       }
125
126       tip.append("</html>");
127
128       setToolTipText("<html>"+sequence.getDisplayId(true)+tip.toString());
129     }
130   }
131
132   /**
133    * DOCUMENT ME!
134    *
135    * @param e DOCUMENT ME!
136    */
137   public void mouseDragged(MouseEvent e)
138   {
139     mouseDragging = true;
140
141     int seq = Math.max(0, alignPanel.seqPanel.findSeq(e));
142
143     if (seq < lastid)
144     {
145       selectSeqs(lastid - 1, seq);
146     }
147     else if (seq > lastid)
148     {
149       selectSeqs(lastid + 1, seq);
150     }
151
152     lastid = seq;
153     alignPanel.paintAlignment(true);
154   }
155
156   public void mouseWheelMoved(MouseWheelEvent e)
157   {
158     e.consume();
159       if (e.getWheelRotation() > 0)
160       {
161         alignPanel.scrollUp(false);
162       }
163       else
164       {
165         alignPanel.scrollUp(true);
166       }
167   }
168
169   /**
170    * DOCUMENT ME!
171    *
172    * @param e DOCUMENT ME!
173    */
174   public void mouseClicked(MouseEvent e)
175   {
176     if (e.getClickCount() < 2)
177     {
178       return;
179     }
180
181     java.util.Vector links = Preferences.sequenceURLLinks;
182     if (links == null || links.size() < 1)
183     {
184       return;
185     }
186
187     int seq = alignPanel.seqPanel.findSeq(e);
188     String url = null;
189     int i = 0;
190     String id = av.getAlignment().getSequenceAt(seq).getName();
191     while (url == null && i < links.size())
192     {
193       // DEFAULT LINK IS FIRST IN THE LINK LIST
194       // BUT IF ITS A REGEX AND DOES NOT MATCH THE NEXT ONE WILL BE TRIED
195       url = links.elementAt(i++).toString();
196       jalview.util.UrlLink urlLink = null;
197       try
198       {
199         urlLink = new UrlLink(url);
200       } catch (Exception foo)
201       {
202         jalview.bin.Cache.log.error("Exception for URLLink '" + url + "'",
203                 foo);
204         url = null;
205         continue;
206       }
207       ;
208       if (!urlLink.isValid())
209       {
210         jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
211         url = null;
212         continue;
213       }
214
215       String urls[] = urlLink.makeUrls(id, true);
216       if (urls == null || urls[0]==null || urls[0].length()<4)
217       {
218         url = null;
219         continue;
220       }
221       // just take first URL made from regex
222       url = urls[1];
223     }
224     try
225     {
226       jalview.util.BrowserLauncher.openURL(url);
227     }
228     catch (Exception ex)
229     {
230       JOptionPane.showInternalMessageDialog(Desktop.desktop,
231                                             "Unixers: Couldn't find default web browser."
232                                             +
233           "\nAdd the full path to your browser in Preferences.",
234                                             "Web browser not found",
235                                             JOptionPane.WARNING_MESSAGE);
236       ex.printStackTrace();
237     }
238     
239   }
240
241   /**
242    * DOCUMENT ME!
243    *
244    * @param e DOCUMENT ME!
245    */
246   public void mouseEntered(MouseEvent e)
247   {
248     if (scrollThread != null)
249     {
250       scrollThread.running = false;
251     }
252   }
253
254   /**
255    * DOCUMENT ME!
256    *
257    * @param e DOCUMENT ME!
258    */
259   public void mouseExited(MouseEvent e)
260   {
261     if (av.getWrapAlignment())
262     {
263       return;
264     }
265
266     if (mouseDragging && (e.getY() < 0) && (av.getStartSeq() > 0))
267     {
268       scrollThread = new ScrollThread(true);
269     }
270
271     if (mouseDragging && (e.getY() >= getHeight()) &&
272         (av.alignment.getHeight() > av.getEndSeq()))
273     {
274       scrollThread = new ScrollThread(false);
275     }
276   }
277
278   /**
279    * DOCUMENT ME!
280    *
281    * @param e DOCUMENT ME!
282    */
283   public void mousePressed(MouseEvent e)
284   {
285     if (e.getClickCount() == 2)
286     {
287       return;
288     }
289
290     int seq = alignPanel.seqPanel.findSeq(e);
291
292     if (javax.swing.SwingUtilities.isRightMouseButton(e))
293     {
294       jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(alignPanel,
295           (Sequence) av.getAlignment().getSequenceAt(seq),
296           Preferences.sequenceURLLinks);
297       pop.show(this, e.getX(), e.getY());
298
299       return;
300     }
301
302     if ( (av.getSelectionGroup() == null) ||
303         ( (!e.isControlDown() && !e.isShiftDown()) && av.getSelectionGroup() != null))
304     {
305       av.setSelectionGroup(new SequenceGroup());
306       av.getSelectionGroup().setStartRes(0);
307       av.getSelectionGroup().setEndRes(av.alignment.getWidth() - 1);
308     }
309
310     if (e.isShiftDown() && (lastid != -1))
311     {
312       selectSeqs(lastid, seq);
313     }
314     else
315     {
316       selectSeq(seq);
317     }
318
319     alignPanel.paintAlignment(true);
320   }
321
322   /**
323    * DOCUMENT ME!
324    *
325    * @param seq DOCUMENT ME!
326    */
327   void selectSeq(int seq)
328   {
329     lastid = seq;
330
331     SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
332     av.getSelectionGroup().addOrRemove(pickedSeq, true);
333   }
334
335   /**
336    * DOCUMENT ME!
337    *
338    * @param start DOCUMENT ME!
339    * @param end DOCUMENT ME!
340    */
341   void selectSeqs(int start, int end)
342   {
343     if (av.getSelectionGroup() == null)
344     {
345       return;
346     }
347
348     if (end >= av.getAlignment().getHeight())
349     {
350       end = av.getAlignment().getHeight() - 1;
351     }
352
353     lastid = start;
354
355     if (end < start)
356     {
357       int tmp = start;
358       start = end;
359       end = tmp;
360       lastid = end;
361     }
362
363     for (int i = start; i <= end; i++)
364     {
365       av.getSelectionGroup().addSequence(av.getAlignment().getSequenceAt(i),
366                                          true);
367     }
368   }
369
370   /**
371    * DOCUMENT ME!
372    *
373    * @param e DOCUMENT ME!
374    */
375   public void mouseReleased(MouseEvent e)
376   {
377     if (scrollThread != null)
378     {
379       scrollThread.running = false;
380     }
381
382     mouseDragging = false;
383     PaintRefresher.Refresh(this, av.getSequenceSetId());
384   }
385
386   /**
387    * DOCUMENT ME!
388    *
389    * @param found DOCUMENT ME!
390    */
391   public void highlightSearchResults(java.util.Vector found)
392   {
393     idCanvas.setHighlighted(found);
394
395     if (found == null)
396     {
397       return;
398     }
399
400     int index = av.alignment.findIndex( (SequenceI) found.get(0));
401
402     // do we need to scroll the panel?
403     if ( (av.getStartSeq() > index) || (av.getEndSeq() < index))
404     {
405       alignPanel.setScrollValues(av.getStartRes(), index);
406     }
407   }
408
409   // this class allows scrolling off the bottom of the visible alignment
410   class ScrollThread
411       extends Thread
412   {
413     boolean running = false;
414     boolean up = true;
415
416     public ScrollThread(boolean up)
417     {
418       this.up = up;
419       start();
420     }
421
422     public void stopScrolling()
423     {
424       running = false;
425     }
426
427     public void run()
428     {
429       running = true;
430
431       while (running)
432       {
433         if (alignPanel.scrollUp(up))
434         {
435           // scroll was ok, so add new sequence to selection
436           int seq = av.getStartSeq();
437
438           if (!up)
439           {
440             seq = av.getEndSeq();
441           }
442
443           if (seq < lastid)
444           {
445             selectSeqs(lastid - 1, seq);
446           }
447           else if (seq > lastid)
448           {
449             selectSeqs(lastid + 1, seq);
450           }
451
452           lastid = seq;
453         }
454         else
455         {
456           running = false;
457         }
458
459         alignPanel.paintAlignment(false);
460
461         try
462         {
463           Thread.sleep(100);
464         }
465         catch (Exception ex)
466         {
467         }
468       }
469     }
470   }
471 }