JAL-2316 Moved url providers into own package
[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     // DEFAULT LINK IS FIRST IN THE LINK LIST
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     String url = urlProvider.getDefaultUrl(id);
221     String target = urlProvider.getDefaultTarget(id);
222
223     /*    String target = null;
224         String url = null;
225         int i = 0;
226         while (url == null && i < links.size())
227         {
228           // DEFAULT LINK IS FIRST IN THE LINK LIST
229           // BUT IF ITS A REGEX AND DOES NOT MATCH THE NEXT ONE WILL BE TRIED
230           url = links.elementAt(i++).toString();
231           jalview.util.UrlLink urlLink = null;
232           try
233           {
234             urlLink = new UrlLink(url);
235             target = urlLink.getTarget();
236           } catch (Exception foo)
237           {
238             System.err.println("Exception for URLLink '" + url + "'");
239             foo.printStackTrace();
240             url = null;
241             continue;
242           }
243
244           if (urlLink.usesDBAccession())
245           {
246             // this URL requires an accession id, not the name of a sequence
247             url = null;
248             continue;
249           }
250
251           if (!urlLink.isValid())
252           {
253             System.err.println(urlLink.getInvalidMessage());
254             url = null;
255             continue;
256           }
257
258           String urls[] = urlLink.makeUrls(id, true);
259           if (urls == null || urls[0] == null || urls[0].length() < 1)
260           {
261             url = null;
262             continue;
263           }
264           // just take first URL made from regex
265           url = urls[1];
266         }*/
267     try
268     {
269       alignPanel.alignFrame.showURL(url, target);
270     } catch (Exception ex)
271     {
272       ex.printStackTrace();
273     }
274   }
275
276   @Override
277   public void mouseEntered(MouseEvent e)
278   {
279     if (scrollThread != null)
280     {
281       scrollThread.running = false;
282     }
283   }
284
285   @Override
286   public void mouseExited(MouseEvent e)
287   {
288     if (av.getWrapAlignment())
289     {
290       return;
291     }
292
293     if (mouseDragging && e.getY() < 0 && av.getStartSeq() > 0)
294     {
295       scrollThread = new ScrollThread(true);
296     }
297
298     if (mouseDragging && e.getY() >= getSize().height
299             && av.getAlignment().getHeight() > av.getEndSeq())
300     {
301       scrollThread = new ScrollThread(false);
302     }
303   }
304
305   @Override
306   public void mousePressed(MouseEvent e)
307   {
308     if (e.getClickCount() > 1)
309     {
310       return;
311     }
312
313     int y = e.getY();
314     if (av.getWrapAlignment())
315     {
316       y -= 2 * av.getCharHeight();
317     }
318
319     int seq = alignPanel.seqPanel.findSeq(e);
320
321     if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
322     {
323       Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq);
324
325       // build a new links menu based on the current links + any non-positional
326       // features
327       Vector<String> nlinks = urlProvider.getLinksForDisplay();
328
329       SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
330       for (int sl = 0; sf != null && sl < sf.length; sl++)
331       {
332         if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
333         {
334           if (sf[sl].links != null && sf[sl].links.size() > 0)
335           {
336             for (int l = 0, lSize = sf[sl].links.size(); l < lSize; l++)
337             {
338               nlinks.addElement(sf[sl].links.elementAt(l));
339             }
340           }
341         }
342       }
343
344       APopupMenu popup = new APopupMenu(alignPanel, sq, nlinks);
345       this.add(popup);
346       popup.show(this, e.getX(), e.getY());
347       return;
348     }
349
350     if ((av.getSelectionGroup() == null)
351             || ((!jalview.util.Platform.isControlDown(e) && !e
352                     .isShiftDown()) && av.getSelectionGroup() != null))
353     {
354       av.setSelectionGroup(new SequenceGroup());
355       av.getSelectionGroup().setStartRes(0);
356       av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
357     }
358
359     if (e.isShiftDown() && lastid != -1)
360     {
361       selectSeqs(lastid, seq);
362     }
363     else
364     {
365       selectSeq(seq);
366     }
367
368     alignPanel.paintAlignment(false);
369   }
370
371   void selectSeq(int seq)
372   {
373     lastid = seq;
374     SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
375     av.getSelectionGroup().addOrRemove(pickedSeq, true);
376   }
377
378   void selectSeqs(int start, int end)
379   {
380
381     lastid = start;
382
383     if (end >= av.getAlignment().getHeight())
384     {
385       end = av.getAlignment().getHeight() - 1;
386     }
387
388     if (end < start)
389     {
390       int tmp = start;
391       start = end;
392       end = tmp;
393       lastid = end;
394     }
395     if (av.getSelectionGroup() == null)
396     {
397       av.setSelectionGroup(new SequenceGroup());
398     }
399     for (int i = start; i <= end; i++)
400     {
401       av.getSelectionGroup().addSequence(
402               av.getAlignment().getSequenceAt(i), i == end);
403     }
404
405   }
406
407   @Override
408   public void mouseReleased(MouseEvent e)
409   {
410     if (scrollThread != null)
411     {
412       scrollThread.running = false;
413     }
414
415     if (av.getSelectionGroup() != null)
416     {
417       av.getSelectionGroup().recalcConservation();
418     }
419
420     mouseDragging = false;
421     PaintRefresher.Refresh(this, av.getSequenceSetId());
422     // always send selection message when mouse is released
423     av.sendSelection();
424   }
425
426   public void highlightSearchResults(List<SequenceI> list)
427   {
428     idCanvas.setHighlighted(list);
429
430     if (list == null)
431     {
432       return;
433     }
434
435     int index = av.getAlignment().findIndex(list.get(0));
436
437     // do we need to scroll the panel?
438     if (av.getStartSeq() > index || av.getEndSeq() < index)
439     {
440       alignPanel.setScrollValues(av.getStartRes(), index);
441     }
442   }
443
444   // this class allows scrolling off the bottom of the visible alignment
445   class ScrollThread extends Thread
446   {
447     boolean running = false;
448
449     boolean up = true;
450
451     public ScrollThread(boolean up)
452     {
453       this.up = up;
454       start();
455     }
456
457     public void stopScrolling()
458     {
459       running = false;
460     }
461
462     @Override
463     public void run()
464     {
465       running = true;
466       while (running)
467       {
468         if (alignPanel.scrollUp(up))
469         {
470           // scroll was ok, so add new sequence to selection
471           int seq = av.getStartSeq();
472           if (!up)
473           {
474             seq = av.getEndSeq();
475           }
476
477           if (seq < lastid)
478           {
479             selectSeqs(lastid - 1, seq);
480           }
481           else if (seq > lastid && seq < av.getAlignment().getHeight())
482           {
483             selectSeqs(lastid + 1, seq);
484           }
485
486           lastid = seq;
487         }
488         else
489         {
490           running = false;
491         }
492
493         alignPanel.paintAlignment(true);
494         try
495         {
496           Thread.sleep(100);
497         } catch (Exception ex)
498         {
499         }
500       }
501     }
502   }
503 }