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