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