JAL-1807 Bob's JalviewJS prototype first commit
[jalviewjs.git] / src / jalview / appletgui / IdPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
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
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import jalview.datamodel.Sequence;
24 import jalview.datamodel.SequenceFeature;
25 import jalview.datamodel.SequenceGroup;
26 import jalview.datamodel.SequenceI;
27 import jalview.util.UrlLink;
28 import jalview.viewmodel.AlignmentViewport;
29
30 import java.awt.BorderLayout;
31 import awt2swing.Panel;
32 import java.awt.event.InputEvent;
33 import java.awt.event.MouseEvent;
34 import java.awt.event.MouseListener;
35 import java.awt.event.MouseMotionListener;
36 import java.util.List;
37 import java.util.Vector;
38
39 public class IdPanel extends Panel implements MouseListener,
40         MouseMotionListener
41 {
42
43   protected IdCanvas idCanvas;
44
45   protected AlignmentViewport av;
46
47   protected AlignmentPanel alignPanel;
48
49   ScrollThread scrollThread = null;
50
51   int lastid = -1;
52
53   boolean mouseDragging = false;
54
55   java.util.Vector links = new java.util.Vector();
56
57   public IdPanel(AlignViewport av, AlignmentPanel parent)
58   {
59         setName("idPanel");
60     this.av = av;
61     alignPanel = parent;
62     idCanvas = new IdCanvas(av);
63     setLayout(new BorderLayout());
64     add(idCanvas, BorderLayout.CENTER);
65     idCanvas.addMouseListener(this);
66     idCanvas.addMouseMotionListener(this);
67
68     // TODO: add in group link parameter
69     if (av.applet != null)
70     {
71         av.applet.getLinkParams(links);
72     }
73     {
74       // upgrade old SRS link
75       int srsPos = links
76               .indexOf("SRS|http://srs.ebi.ac.uk/srsbin/cgi-bin/wgetz?-newId+(([uniprot-all:$SEQUENCE_ID$]))+-view+SwissEntry");
77       if (srsPos > -1)
78       {
79         links.setElementAt(
80                 "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$",
81                 srsPos);
82       }
83     }
84     if (links.size() < 1)
85     {
86       links = new java.util.Vector();
87       links.addElement("EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$");
88     }
89   }
90
91         Tooltip tooltip;
92
93   public void mouseMoved(MouseEvent e)
94   {
95     int seq = alignPanel.seqPanel.findSeq(e);
96
97     SequenceI sequence = av.getAlignment().getSequenceAt(seq);
98
99     // look for non-pos features
100     StringBuffer tooltiptext = new StringBuffer();
101     if (sequence != null)
102     {
103       if (sequence.getDescription() != null)
104       {
105         tooltiptext.append(sequence.getDescription());
106         tooltiptext.append("\n");
107       }
108
109       SequenceFeature sf[] = sequence.getSequenceFeatures();
110       for (int sl = 0; sf != null && sl < sf.length; sl++)
111       {
112         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
113         {
114           boolean nl = false;
115           if (sf[sl].getFeatureGroup() != null)
116           {
117             tooltiptext.append(sf[sl].getFeatureGroup());
118             nl = true;
119           }
120           ;
121           if (sf[sl].getType() != null)
122           {
123             tooltiptext.append(" ");
124             tooltiptext.append(sf[sl].getType());
125             nl = true;
126           }
127           ;
128           if (sf[sl].getDescription() != null)
129           {
130             tooltiptext.append(" ");
131             tooltiptext.append(sf[sl].getDescription());
132             nl = true;
133           }
134           ;
135           if (!Float.isNaN(sf[sl].getScore()) && sf[sl].getScore() != 0f)
136           {
137             tooltiptext.append(" Score = ");
138             tooltiptext.append(sf[sl].getScore());
139             nl = true;
140           }
141           ;
142           if (sf[sl].getStatus() != null && sf[sl].getStatus().length() > 0)
143           {
144             tooltiptext.append(" (");
145             tooltiptext.append(sf[sl].getStatus());
146             tooltiptext.append(")");
147             nl = true;
148           }
149           ;
150           if (nl)
151           {
152             tooltiptext.append("\n");
153           }
154         }
155       }
156     }
157     if (tooltiptext.length() == 0)
158     {
159       // nothing to display - so clear tooltip if one is visible
160       if (tooltip != null)
161       {
162         tooltip.setVisible(false);
163       }
164       tooltip = null;
165       tooltiptext = null;
166       return;
167     }
168     if (tooltip == null)
169     {
170       tooltip = new Tooltip(sequence.getDisplayId(true) + "\n"
171               + tooltiptext.toString(), idCanvas);
172     }
173     else
174     {
175       tooltip.setTip(sequence.getDisplayId(true) + "\n"
176               + tooltiptext.toString());
177     }
178     tooltiptext = null;
179   }
180
181   public void mouseDragged(MouseEvent e)
182   {
183     mouseDragging = true;
184
185     int seq = Math.max(0, alignPanel.seqPanel.findSeq(e));
186
187     if (seq < lastid)
188     {
189       selectSeqs(lastid - 1, seq);
190     }
191     else if (seq > lastid)
192     {
193       selectSeqs(lastid + 1, seq);
194     }
195
196     lastid = seq;
197     alignPanel.paintAlignment(false);
198   }
199
200   public void mouseClicked(MouseEvent e)
201   {
202     if (e.getClickCount() < 2)
203     {
204       return;
205     }
206
207     // DEFAULT LINK IS FIRST IN THE LINK LIST
208     int seq = alignPanel.seqPanel.findSeq(e);
209     SequenceI sq = av.getAlignment().getSequenceAt(seq);
210     if (sq == null)
211     {
212       return;
213     }
214     String id = sq.getName();
215
216     String target = null;
217     String url = null;
218     int i = 0;
219     while (url == null && i < links.size())
220     {
221       // DEFAULT LINK IS FIRST IN THE LINK LIST
222       // BUT IF ITS A REGEX AND DOES NOT MATCH THE NEXT ONE WILL BE TRIED
223       url = links.elementAt(i++).toString();
224       UrlLink urlLink = null;
225       try
226       {
227         urlLink = new UrlLink(url);
228         target = urlLink.getTarget();
229       } catch (Exception foo)
230       {
231         System.err.println("Exception for URLLink '" + url + "'");
232         foo.printStackTrace();
233         url = null;
234         continue;
235       }
236       ;
237       if (!urlLink.isValid())
238       {
239         System.err.println(urlLink.getInvalidMessage());
240         url = null;
241         continue;
242       }
243
244       String urls[] = urlLink.makeUrls(id, true);
245       if (urls == null || urls[0] == null || urls[0].length() < 1)
246       {
247         url = null;
248         continue;
249       }
250       // just take first URL made from regex
251       url = urls[1];
252     }
253     try
254     {
255
256       alignPanel.alignFrame.showURL(url, target);
257     } catch (Exception ex)
258     {
259       ex.printStackTrace();
260     }
261   }
262
263   public void mouseEntered(MouseEvent e)
264   {
265     if (scrollThread != null)
266     {
267       scrollThread.running = false;
268     }
269   }
270
271   public void mouseExited(MouseEvent e)
272   {
273     if (av.getWrapAlignment())
274     {
275       return;
276     }
277
278     if (mouseDragging && e.getY() < 0 && av.getStartSeq() > 0)
279     {
280       scrollThread = new ScrollThread(true);
281     }
282
283     if (mouseDragging && e.getY() >= getSize().height
284             && av.getAlignment().getHeight() > av.getEndSeq())
285     {
286       scrollThread = new ScrollThread(false);
287     }
288   }
289
290   public void mousePressed(MouseEvent e)
291   {
292     if (e.getClickCount() > 1)
293     {
294       return;
295     }
296
297     int y = e.getY();
298     if (av.getWrapAlignment())
299     {
300       y -= 2 * av.getCharHeight();
301     }
302
303     int seq = alignPanel.seqPanel.findSeq(e);
304
305     if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
306     {
307       Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq);
308
309       // build a new links menu based on the current links + any non-positional
310       // features
311       Vector nlinks = new Vector();
312       for (int l = 0, lSize = links.size(); l < lSize; l++)
313       {
314         nlinks.addElement(links.elementAt(l));
315       }
316       SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
317       for (int sl = 0; sf != null && sl < sf.length; sl++)
318       {
319         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
320         {
321           if (sf[sl].links != null && sf[sl].links.size() > 0)
322           {
323             for (int l = 0, lSize = sf[sl].links.size(); l < lSize; l++)
324             {
325               nlinks.addElement(sf[sl].links.elementAt(l));
326             }
327           }
328         }
329       }
330
331       APopupMenu popup = new APopupMenu(alignPanel, sq, nlinks);
332       this.add(popup);
333       popup.show(this, e.getX(), e.getY());
334       return;
335     }
336
337     if ((av.getSelectionGroup() == null)
338             || ((!e.isControlDown() && !e.isShiftDown()) && av
339                     .getSelectionGroup() != null))
340     {
341       av.setSelectionGroup(new SequenceGroup());
342       av.getSelectionGroup().setStartRes(0);
343       av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
344     }
345
346     if (e.isShiftDown() && lastid != -1)
347     {
348       selectSeqs(lastid, seq);
349     }
350     else
351     {
352       selectSeq(seq);
353     }
354
355     alignPanel.paintAlignment(false);
356   }
357
358   void selectSeq(int seq)
359   {
360     lastid = seq;
361     SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
362     av.getSelectionGroup().addOrRemove(pickedSeq, true);
363   }
364
365   void selectSeqs(int start, int end)
366   {
367
368     lastid = start;
369
370     if (end >= av.getAlignment().getHeight())
371     {
372       end = av.getAlignment().getHeight() - 1;
373     }
374
375     if (end < start)
376     {
377       int tmp = start;
378       start = end;
379       end = tmp;
380       lastid = end;
381     }
382     if (av.getSelectionGroup() == null)
383     {
384       av.setSelectionGroup(new SequenceGroup());
385     }
386     for (int i = start; i <= end; i++)
387     {
388       av.getSelectionGroup().addSequence(
389               av.getAlignment().getSequenceAt(i), i == end);
390     }
391
392   }
393
394   public void mouseReleased(MouseEvent e)
395   {
396     if (scrollThread != null)
397     {
398       scrollThread.running = false;
399     }
400
401     if (av.getSelectionGroup() != null)
402     {
403       av.getSelectionGroup().recalcConservation();
404     }
405
406     mouseDragging = false;
407     PaintRefresher.Refresh(this, av.getSequenceSetId());
408     // always send selection message when mouse is released
409     av.sendSelection();
410   }
411
412   public void highlightSearchResults(List<SequenceI> list)
413   {
414     idCanvas.setHighlighted(list);
415
416     if (list == null)
417     {
418       return;
419     }
420
421     int index = av.getAlignment().findIndex(list.get(0));
422
423     // do we need to scroll the panel?
424     if (av.getStartSeq() > index || av.getEndSeq() < index)
425     {
426       alignPanel.setScrollValues(av.getStartRes(), index);
427     }
428   }
429
430   // this class allows scrolling off the bottom of the visible alignment
431   class ScrollThread extends Thread
432   {
433     boolean running = false;
434
435     boolean up = true;
436
437     public ScrollThread(boolean up)
438     {
439       this.up = up;
440       start();
441     }
442
443     public void stopScrolling()
444     {
445       running = false;
446     }
447
448     public void run()
449     {
450       running = true;
451       while (running)
452       {
453         if (alignPanel.scrollUp(up))
454         {
455           // scroll was ok, so add new sequence to selection
456           int seq = av.getStartSeq();
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 && seq < av.getAlignment().getHeight())
467           {
468             selectSeqs(lastid + 1, seq);
469           }
470
471           lastid = seq;
472         }
473         else
474         {
475           running = false;
476         }
477
478         alignPanel.paintAlignment(true);
479         try
480         {
481           Thread.sleep(100);
482         } catch (Exception ex)
483         {
484         }
485       }
486     }
487   }
488 }