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