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