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