JAL-2316 Adjusted unit tests. Tidied UrlLinkProviderI interface.
[jalview.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.urls.UrlProvider;
28 import jalview.urls.UrlProviderI;
29 import jalview.viewmodel.AlignmentViewport;
30
31 import java.awt.BorderLayout;
32 import java.awt.Panel;
33 import java.awt.event.InputEvent;
34 import java.awt.event.MouseEvent;
35 import java.awt.event.MouseListener;
36 import java.awt.event.MouseMotionListener;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Vector;
40
41 public class IdPanel extends Panel implements MouseListener,
42         MouseMotionListener
43 {
44
45   protected IdCanvas idCanvas;
46
47   protected AlignmentViewport av;
48
49   protected AlignmentPanel alignPanel;
50
51   ScrollThread scrollThread = null;
52
53   int lastid = -1;
54
55   boolean mouseDragging = false;
56
57   UrlProviderI urlProvider = null;
58
59   public IdPanel(AlignViewport av, AlignmentPanel parent)
60   {
61     this.av = av;
62     alignPanel = parent;
63     idCanvas = new IdCanvas(av);
64     setLayout(new BorderLayout());
65     add(idCanvas, BorderLayout.CENTER);
66     idCanvas.addMouseListener(this);
67     idCanvas.addMouseMotionListener(this);
68
69     String label, url;
70     // TODO: add in group link parameter
71
72     // make a list of label,url pairs
73     HashMap<String, String> urlList = new HashMap<String, String>();
74     if (av.applet != null)
75     {
76       for (int i = 1; i < 10; i++)
77       {
78         label = av.applet.getParameter("linkLabel_" + i);
79         url = av.applet.getParameter("linkURL_" + i);
80
81         urlList.put(label, url);
82       }
83
84       if (!urlList.isEmpty())
85       {
86         // set default as first entry in list
87         String defaultUrl = av.applet.getParameter("linkLabel_1");
88         urlProvider = new UrlProvider(defaultUrl, urlList);
89       }
90     }
91   }
92
93   Tooltip tooltip;
94
95   @Override
96   public void mouseMoved(MouseEvent e)
97   {
98     int seq = alignPanel.seqPanel.findSeq(e);
99
100     SequenceI sequence = av.getAlignment().getSequenceAt(seq);
101
102     // look for non-pos features
103     StringBuffer tooltiptext = new StringBuffer();
104     if (sequence != null)
105     {
106       if (sequence.getDescription() != null)
107       {
108         tooltiptext.append(sequence.getDescription());
109         tooltiptext.append("\n");
110       }
111
112       SequenceFeature sf[] = sequence.getSequenceFeatures();
113       for (int sl = 0; sf != null && sl < sf.length; sl++)
114       {
115         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
116         {
117           boolean nl = false;
118           if (sf[sl].getFeatureGroup() != null)
119           {
120             tooltiptext.append(sf[sl].getFeatureGroup());
121             nl = true;
122           }
123           ;
124           if (sf[sl].getType() != null)
125           {
126             tooltiptext.append(" ");
127             tooltiptext.append(sf[sl].getType());
128             nl = true;
129           }
130           ;
131           if (sf[sl].getDescription() != null)
132           {
133             tooltiptext.append(" ");
134             tooltiptext.append(sf[sl].getDescription());
135             nl = true;
136           }
137           ;
138           if (!Float.isNaN(sf[sl].getScore()) && sf[sl].getScore() != 0f)
139           {
140             tooltiptext.append(" Score = ");
141             tooltiptext.append(sf[sl].getScore());
142             nl = true;
143           }
144           ;
145           if (sf[sl].getStatus() != null && sf[sl].getStatus().length() > 0)
146           {
147             tooltiptext.append(" (");
148             tooltiptext.append(sf[sl].getStatus());
149             tooltiptext.append(")");
150             nl = true;
151           }
152           ;
153           if (nl)
154           {
155             tooltiptext.append("\n");
156           }
157         }
158       }
159     }
160     if (tooltiptext.length() == 0)
161     {
162       // nothing to display - so clear tooltip if one is visible
163       if (tooltip != null)
164       {
165         tooltip.setVisible(false);
166       }
167       tooltip = null;
168       tooltiptext = null;
169       return;
170     }
171     if (tooltip == null)
172     {
173       tooltip = new Tooltip(sequence.getDisplayId(true) + "\n"
174               + tooltiptext.toString(), idCanvas);
175     }
176     else
177     {
178       tooltip.setTip(sequence.getDisplayId(true) + "\n"
179               + tooltiptext.toString());
180     }
181     tooltiptext = null;
182   }
183
184   @Override
185   public void mouseDragged(MouseEvent e)
186   {
187     mouseDragging = true;
188
189     int seq = Math.max(0, alignPanel.seqPanel.findSeq(e));
190
191     if (seq < lastid)
192     {
193       selectSeqs(lastid - 1, seq);
194     }
195     else if (seq > lastid)
196     {
197       selectSeqs(lastid + 1, seq);
198     }
199
200     lastid = seq;
201     alignPanel.paintAlignment(false);
202   }
203
204   @Override
205   public void mouseClicked(MouseEvent e)
206   {
207     if (e.getClickCount() < 2)
208     {
209       return;
210     }
211
212     // get the sequence details
213     int seq = alignPanel.seqPanel.findSeq(e);
214     SequenceI sq = av.getAlignment().getSequenceAt(seq);
215     if (sq == null)
216     {
217       return;
218     }
219     String id = sq.getName();
220
221     // get the default url with the sequence details filled in
222     String url = urlProvider.getDefaultUrl(id);
223     String target = urlProvider.getDefaultTarget(id);
224     try
225     {
226       alignPanel.alignFrame.showURL(url, target);
227     } catch (Exception ex)
228     {
229       ex.printStackTrace();
230     }
231   }
232
233   @Override
234   public void mouseEntered(MouseEvent e)
235   {
236     if (scrollThread != null)
237     {
238       scrollThread.running = false;
239     }
240   }
241
242   @Override
243   public void mouseExited(MouseEvent e)
244   {
245     if (av.getWrapAlignment())
246     {
247       return;
248     }
249
250     if (mouseDragging && e.getY() < 0 && av.getStartSeq() > 0)
251     {
252       scrollThread = new ScrollThread(true);
253     }
254
255     if (mouseDragging && e.getY() >= getSize().height
256             && av.getAlignment().getHeight() > av.getEndSeq())
257     {
258       scrollThread = new ScrollThread(false);
259     }
260   }
261
262   @Override
263   public void mousePressed(MouseEvent e)
264   {
265     if (e.getClickCount() > 1)
266     {
267       return;
268     }
269
270     int y = e.getY();
271     if (av.getWrapAlignment())
272     {
273       y -= 2 * av.getCharHeight();
274     }
275
276     int seq = alignPanel.seqPanel.findSeq(e);
277
278     if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
279     {
280       Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq);
281
282       // build a new links menu based on the current links + any non-positional
283       // features
284       Vector<String> nlinks = urlProvider.getLinksForMenu();
285
286       SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
287       for (int sl = 0; sf != null && sl < sf.length; sl++)
288       {
289         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
290         {
291           if (sf[sl].links != null && sf[sl].links.size() > 0)
292           {
293             for (int l = 0, lSize = sf[sl].links.size(); l < lSize; l++)
294             {
295               nlinks.addElement(sf[sl].links.elementAt(l));
296             }
297           }
298         }
299       }
300
301       APopupMenu popup = new APopupMenu(alignPanel, sq, nlinks);
302       this.add(popup);
303       popup.show(this, e.getX(), e.getY());
304       return;
305     }
306
307     if ((av.getSelectionGroup() == null)
308             || ((!jalview.util.Platform.isControlDown(e) && !e
309                     .isShiftDown()) && av.getSelectionGroup() != null))
310     {
311       av.setSelectionGroup(new SequenceGroup());
312       av.getSelectionGroup().setStartRes(0);
313       av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
314     }
315
316     if (e.isShiftDown() && lastid != -1)
317     {
318       selectSeqs(lastid, seq);
319     }
320     else
321     {
322       selectSeq(seq);
323     }
324
325     alignPanel.paintAlignment(false);
326   }
327
328   void selectSeq(int seq)
329   {
330     lastid = seq;
331     SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
332     av.getSelectionGroup().addOrRemove(pickedSeq, true);
333   }
334
335   void selectSeqs(int start, int end)
336   {
337
338     lastid = start;
339
340     if (end >= av.getAlignment().getHeight())
341     {
342       end = av.getAlignment().getHeight() - 1;
343     }
344
345     if (end < start)
346     {
347       int tmp = start;
348       start = end;
349       end = tmp;
350       lastid = end;
351     }
352     if (av.getSelectionGroup() == null)
353     {
354       av.setSelectionGroup(new SequenceGroup());
355     }
356     for (int i = start; i <= end; i++)
357     {
358       av.getSelectionGroup().addSequence(
359               av.getAlignment().getSequenceAt(i), i == end);
360     }
361
362   }
363
364   @Override
365   public void mouseReleased(MouseEvent e)
366   {
367     if (scrollThread != null)
368     {
369       scrollThread.running = false;
370     }
371
372     if (av.getSelectionGroup() != null)
373     {
374       av.getSelectionGroup().recalcConservation();
375     }
376
377     mouseDragging = false;
378     PaintRefresher.Refresh(this, av.getSequenceSetId());
379     // always send selection message when mouse is released
380     av.sendSelection();
381   }
382
383   public void highlightSearchResults(List<SequenceI> list)
384   {
385     idCanvas.setHighlighted(list);
386
387     if (list == null)
388     {
389       return;
390     }
391
392     int index = av.getAlignment().findIndex(list.get(0));
393
394     // do we need to scroll the panel?
395     if (av.getStartSeq() > index || av.getEndSeq() < index)
396     {
397       alignPanel.setScrollValues(av.getStartRes(), index);
398     }
399   }
400
401   // this class allows scrolling off the bottom of the visible alignment
402   class ScrollThread extends Thread
403   {
404     boolean running = false;
405
406     boolean up = true;
407
408     public ScrollThread(boolean up)
409     {
410       this.up = up;
411       start();
412     }
413
414     public void stopScrolling()
415     {
416       running = false;
417     }
418
419     @Override
420     public void run()
421     {
422       running = true;
423       while (running)
424       {
425         if (alignPanel.scrollUp(up))
426         {
427           // scroll was ok, so add new sequence to selection
428           int seq = av.getStartSeq();
429           if (!up)
430           {
431             seq = av.getEndSeq();
432           }
433
434           if (seq < lastid)
435           {
436             selectSeqs(lastid - 1, seq);
437           }
438           else if (seq > lastid && seq < av.getAlignment().getHeight())
439           {
440             selectSeqs(lastid + 1, seq);
441           }
442
443           lastid = seq;
444         }
445         else
446         {
447           running = false;
448         }
449
450         alignPanel.paintAlignment(true);
451         try
452         {
453           Thread.sleep(100);
454         } catch (Exception ex)
455         {
456         }
457       }
458     }
459   }
460 }