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