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