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