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