Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.gui

File IdPanel.java

 

Coverage histogram

../../img/srcFileCovDistChart1.png
52% of files have more coverage

Code metrics

82
166
24
2
658
412
79
0.48
6.92
12
3.29

Classes

Class Line # Actions
IdPanel 57 148 70
0.061983476.2%
IdPanel.ScrollThread 589 18 9
0.00%
 

Contributing tests

This file is covered by 100 tests. .

Source view

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.gui;
22   
23    import java.awt.BorderLayout;
24    import java.awt.event.ActionEvent;
25    import java.awt.event.ActionListener;
26    import java.awt.event.MouseEvent;
27    import java.awt.event.MouseListener;
28    import java.awt.event.MouseMotionListener;
29    import java.awt.event.MouseWheelEvent;
30    import java.awt.event.MouseWheelListener;
31    import java.util.List;
32   
33    import javax.swing.JPanel;
34    import javax.swing.JPopupMenu;
35    import javax.swing.SwingUtilities;
36    import javax.swing.Timer;
37    import javax.swing.ToolTipManager;
38   
39    import jalview.datamodel.AlignmentAnnotation;
40    import jalview.datamodel.Sequence;
41    import jalview.datamodel.SequenceGroup;
42    import jalview.datamodel.SequenceI;
43    import jalview.gui.SeqPanel.MousePos;
44    import jalview.io.SequenceAnnotationReport;
45    import jalview.util.MessageManager;
46    import jalview.util.Platform;
47    import jalview.viewmodel.AlignmentViewport;
48    import jalview.viewmodel.ViewportRanges;
49   
50    /**
51    * This panel hosts alignment sequence ids and responds to mouse clicks on them,
52    * as well as highlighting ids matched by a search from the Find menu.
53    *
54    * @author $author$
55    * @version $Revision$
56    */
 
57    public class IdPanel extends JPanel
58    implements MouseListener, MouseMotionListener, MouseWheelListener
59    {
60    private IdCanvas idCanvas;
61   
62    protected AlignmentViewport av;
63   
64    protected AlignmentPanel alignPanel;
65   
66    ScrollThread scrollThread = null;
67   
68    int offy;
69   
70    // int width;
71    int lastid = -1;
72   
73    boolean mouseDragging = false;
74   
75    private final SequenceAnnotationReport seqAnnotReport;
76   
77    /**
78    * Creates a new IdPanel object.
79    *
80    * @param av
81    * @param parent
82    */
 
83  267 toggle public IdPanel(AlignViewport av, AlignmentPanel parent)
84    {
85  267 this.av = av;
86  267 alignPanel = parent;
87  267 setIdCanvas(new IdCanvas(av));
88  267 seqAnnotReport = new SequenceAnnotationReport(true);
89  267 setLayout(new BorderLayout());
90  267 add(getIdCanvas(), BorderLayout.CENTER);
91  267 addMouseListener(this);
92  267 addMouseMotionListener(this);
93  267 addMouseWheelListener(this);
94  267 ToolTipManager.sharedInstance().registerComponent(this);
95    }
96   
97    /**
98    * Responds to mouse movement by setting tooltip text for the sequence id
99    * under the mouse (or possibly annotation label, when in wrapped mode)
100    *
101    * @param e
102    */
 
103  0 toggle @Override
104    public void mouseMoved(MouseEvent e)
105    {
106  0 SeqPanel sp = alignPanel.getSeqPanel();
107  0 MousePos pos = sp.findMousePosition(e);
108  0 if (pos.isOverAnnotation())
109    {
110    /*
111    * mouse is over an annotation label in wrapped mode
112    */
113  0 AlignmentAnnotation[] anns = av.getAlignment()
114    .getAlignmentAnnotation();
115  0 AlignmentAnnotation annotation = anns[pos.annotationIndex];
116  0 setToolTipText(AnnotationLabels.getTooltip(annotation));
117  0 alignPanel.alignFrame.setStatus(
118    AnnotationLabels.getStatusMessage(annotation, anns));
119    }
120    else
121    {
122  0 int seq = Math.max(0, pos.seqIndex);
123  0 if (seq < av.getAlignment().getHeight())
124    {
125  0 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
126  0 StringBuilder tip = new StringBuilder(64);
127  0 tip.append(sequence.getDisplayId(true)).append(" ");
128  0 seqAnnotReport.createTooltipAnnotationReport(tip, sequence,
129    av.isShowDBRefs(), av.isShowNPFeats(), sp.seqCanvas.fr);
130  0 setToolTipText(JvSwingUtils.wrapTooltip(true, tip.toString()));
131   
132  0 StringBuilder text = new StringBuilder();
133  0 text.append("Sequence ").append(String.valueOf(seq + 1))
134    .append(" ID: ")
135    .append(sequence.getName());
136  0 alignPanel.alignFrame.setStatus(text.toString());
137    }
138    }
139    }
140   
141    /**
142    * Responds to a mouse drag by selecting the sequences under the dragged
143    * region.
144    *
145    * @param e
146    */
 
147  0 toggle @Override
148    public void mouseDragged(MouseEvent e)
149    {
150  0 mouseDragging = true;
151   
152  0 MousePos pos = alignPanel.getSeqPanel().findMousePosition(e);
153  0 if (pos.isOverAnnotation())
154    {
155    // mouse is over annotation label in wrapped mode
156  0 return;
157    }
158   
159  0 int seq = Math.max(0, pos.seqIndex);
160   
161  0 if (seq < lastid)
162    {
163  0 selectSeqs(lastid - 1, seq);
164    }
165  0 else if (seq > lastid)
166    {
167  0 selectSeqs(lastid + 1, seq);
168    }
169   
170  0 lastid = seq;
171  0 alignPanel.paintAlignment(false, false);
172    }
173   
174    /**
175    * Response to the mouse wheel by scrolling the alignment panel.
176    */
 
177  0 toggle @Override
178    public void mouseWheelMoved(MouseWheelEvent e)
179    {
180  0 e.consume();
181  0 double wheelRotation = e.getPreciseWheelRotation();
182  0 if (wheelRotation > 0)
183    {
184  0 if (e.isShiftDown())
185    {
186  0 av.getRanges().scrollRight(true);
187    }
188    else
189    {
190  0 av.getRanges().scrollUp(false);
191    }
192    }
193  0 else if (wheelRotation < 0)
194    {
195  0 if (e.isShiftDown())
196    {
197  0 av.getRanges().scrollRight(false);
198    }
199    else
200    {
201  0 av.getRanges().scrollUp(true);
202    }
203    }
204    }
205   
206    /**
207    * Handle a mouse click event. Currently only responds to a double-click. The
208    * action is to try to open a browser window at a URL that searches for the
209    * selected sequence id. The search URL is configured in Preferences |
210    * Connections | URL link from Sequence ID. For example:
211    *
212    * http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$
213    *
214    * @param e
215    */
 
216  0 toggle @Override
217    public void mouseClicked(MouseEvent e)
218    {
219    /*
220    * Ignore single click. Ignore 'left' click followed by 'right' click (user
221    * selects a row then its pop-up menu).
222    */
223  0 if (e.getClickCount() < 2 || SwingUtilities.isRightMouseButton(e))
224    {
225    // reinstate isRightMouseButton check to ignore mouse-related popup events
226    // note - this does nothing on default MacBookPro force-trackpad config!
227  0 return;
228    }
229   
230  0 MousePos pos = alignPanel.getSeqPanel().findMousePosition(e);
231  0 int seq = pos.seqIndex;
232  0 if (pos.isOverAnnotation() || seq < 0)
233    {
234  0 return;
235    }
236   
237  0 String id = av.getAlignment().getSequenceAt(seq).getName();
238  0 String url = Preferences.sequenceUrlLinks.getPrimaryUrl(id);
239   
240  0 try
241    {
242  0 jalview.util.BrowserLauncher.openURL(url);
243    } catch (Exception ex)
244    {
245  0 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
246    MessageManager.getString("label.web_browser_not_found_unix"),
247    MessageManager.getString("label.web_browser_not_found"),
248    JvOptionPane.WARNING_MESSAGE);
249  0 ex.printStackTrace();
250    }
251    }
252   
253    /**
254    * On (re-)entering the panel, stop any scrolling
255    *
256    * @param e
257    */
 
258  0 toggle @Override
259    public void mouseEntered(MouseEvent e)
260    {
261  0 stopScrolling();
262    }
263   
264    /**
265    * Interrupts the scroll thread if one is running
266    */
 
267  0 toggle void stopScrolling()
268    {
269  0 if (scrollThread != null)
270    {
271  0 scrollThread.stopScrolling();
272  0 scrollThread = null;
273    }
274    }
275   
276    /**
277    * DOCUMENT ME!
278    *
279    * @param e
280    * DOCUMENT ME!
281    */
 
282  0 toggle @Override
283    public void mouseExited(MouseEvent e)
284    {
285  0 if (av.getWrapAlignment())
286    {
287  0 return;
288    }
289   
290  0 if (mouseDragging)
291    {
292    /*
293    * on mouse drag above or below the panel, start
294    * scrolling if there are more sequences to show
295    */
296  0 ViewportRanges ranges = av.getRanges();
297  0 if (e.getY() < 0 && ranges.getStartSeq() > 0)
298    {
299  0 startScrolling(true);
300    }
301  0 else if (e.getY() >= getHeight()
302    && ranges.getEndSeq() <= av.getAlignment().getHeight())
303    {
304  0 startScrolling(false);
305    }
306    }
307    }
308   
309    /**
310    * Starts scrolling either up or down
311    *
312    * @param up
313    */
 
314  0 toggle void startScrolling(boolean up)
315    {
316  0 scrollThread = new ScrollThread(up);
317  0 if (Platform.isJS())
318    {
319    /*
320    * for JalviewJS using Swing Timer
321    */
322  0 Timer t = new Timer(20, new ActionListener()
323    {
 
324  0 toggle @Override
325    public void actionPerformed(ActionEvent e)
326    {
327  0 if (scrollThread != null)
328    {
329    // if (!scrollOnce() {t.stop();}) gives compiler error :-(
330  0 scrollThread.scrollOnce();
331    }
332    }
333    });
334  0 t.addActionListener(new ActionListener()
335    {
 
336  0 toggle @Override
337    public void actionPerformed(ActionEvent e)
338    {
339  0 if (scrollThread == null)
340    {
341    // IdPanel.stopScrolling called
342  0 t.stop();
343    }
344    }
345    });
346  0 t.start();
347    }
348    else
349    /**
350    * Java only
351    *
352    * @j2sIgnore
353    */
354    {
355  0 scrollThread.start();
356    }
357    }
358   
359    /**
360    * Respond to a mouse press. Does nothing for (left) double-click as this is
361    * handled by mouseClicked().
362    *
363    * Right mouse down - construct and show context menu.
364    *
365    * Ctrl-down or Shift-down - add to or expand current selection group if there
366    * is one.
367    *
368    * Mouse down - select this sequence.
369    *
370    * @param e
371    */
 
372  0 toggle @Override
373    public void mousePressed(MouseEvent e)
374    {
375  0 if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e))
376    {
377  0 return;
378    }
379   
380  0 MousePos pos = alignPanel.getSeqPanel().findMousePosition(e);
381   
382  0 if (e.isPopupTrigger()) // Mac reports this in mousePressed
383    {
384  0 showPopupMenu(e, pos);
385  0 return;
386    }
387   
388    /*
389    * defer right-mouse click handling to mouseReleased on Windows
390    * (where isPopupTrigger() will answer true)
391    * NB isRightMouseButton is also true for Cmd-click on Mac
392    */
393  0 if (Platform.isWinRightButton(e))
394    {
395  0 return;
396    }
397   
398  0 if ((av.getSelectionGroup() == null)
399    || (!jalview.util.Platform.isControlDown(e) && !e.isShiftDown()
400    && av.getSelectionGroup() != null))
401    {
402  0 av.setSelectionGroup(new SequenceGroup());
403  0 av.getSelectionGroup().setStartRes(0);
404  0 av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
405    }
406   
407  0 if (e.isShiftDown() && (lastid != -1))
408    {
409  0 selectSeqs(lastid, pos.seqIndex);
410    }
411    else
412    {
413  0 selectSeq(pos.seqIndex);
414    }
415   
416  0 av.isSelectionGroupChanged(true);
417   
418  0 alignPanel.paintAlignment(false, false);
419    }
420   
421    /**
422    * Build and show the popup-menu at the right-click mouse position
423    *
424    * @param e
425    */
 
426  0 toggle void showPopupMenu(MouseEvent e, MousePos pos)
427    {
428  0 if (pos.isOverAnnotation())
429    {
430  0 showAnnotationMenu(e, pos);
431  0 return;
432    }
433   
434  0 Sequence sq = (Sequence) av.getAlignment().getSequenceAt(pos.seqIndex);
435  0 if (sq != null)
436    {
437  0 PopupMenu pop = new PopupMenu(alignPanel, sq,
438    Preferences.getGroupURLLinks());
439  0 pop.show(this, e.getX(), e.getY());
440    }
441    }
442   
443    /**
444    * On right mouse click on a Consensus annotation label, shows a limited popup
445    * menu, with options to configure the consensus calculation and rendering.
446    *
447    * @param e
448    * @param pos
449    * @see AnnotationLabels#showPopupMenu(MouseEvent)
450    */
 
451  0 toggle void showAnnotationMenu(MouseEvent e, MousePos pos)
452    {
453  0 if (pos.annotationIndex == -1)
454    {
455  0 return;
456    }
457  0 AlignmentAnnotation[] anns = this.av.getAlignment()
458    .getAlignmentAnnotation();
459  0 if (anns == null || pos.annotationIndex >= anns.length)
460    {
461  0 return;
462    }
463  0 AlignmentAnnotation ann = anns[pos.annotationIndex];
464  0 if (!ann.label.contains("Consensus"))
465    {
466  0 return;
467    }
468   
469  0 JPopupMenu pop = new JPopupMenu(
470    MessageManager.getString("label.annotations"));
471  0 AnnotationLabels.addConsensusMenuOptions(this.alignPanel, ann, pop);
472  0 pop.show(this, e.getX(), e.getY());
473    }
474   
475    /**
476    * Toggle whether the sequence is part of the current selection group.
477    *
478    * @param seq
479    */
 
480  0 toggle void selectSeq(int seq)
481    {
482  0 lastid = seq;
483   
484  0 SequenceI pickedSeq = av.getAlignment().getSequenceAt(seq);
485  0 av.getSelectionGroup().addOrRemove(pickedSeq, false);
486    }
487   
488    /**
489    * Add contiguous rows of the alignment to the current selection group. Does
490    * nothing if there is no selection group.
491    *
492    * @param start
493    * @param end
494    */
 
495  0 toggle void selectSeqs(int start, int end)
496    {
497  0 if (av.getSelectionGroup() == null)
498    {
499  0 return;
500    }
501   
502  0 if (end >= av.getAlignment().getHeight())
503    {
504  0 end = av.getAlignment().getHeight() - 1;
505    }
506   
507  0 lastid = start;
508   
509  0 if (end < start)
510    {
511  0 int tmp = start;
512  0 start = end;
513  0 end = tmp;
514  0 lastid = end;
515    }
516   
517  0 for (int i = start; i <= end; i++)
518    {
519  0 av.getSelectionGroup().addSequence(av.getAlignment().getSequenceAt(i),
520    false);
521    }
522    }
523   
524    /**
525    * Respond to mouse released. Refreshes the display and triggers broadcast of
526    * the new selection group to any listeners.
527    *
528    * @param e
529    */
 
530  0 toggle @Override
531    public void mouseReleased(MouseEvent e)
532    {
533  0 if (scrollThread != null)
534    {
535  0 stopScrolling();
536    }
537  0 MousePos pos = alignPanel.getSeqPanel().findMousePosition(e);
538   
539  0 mouseDragging = false;
540  0 PaintRefresher.Refresh(this, av.getSequenceSetId());
541    // always send selection message when mouse is released
542  0 av.sendSelection();
543   
544  0 if (e.isPopupTrigger()) // Windows reports this in mouseReleased
545    {
546  0 showPopupMenu(e, pos);
547    }
548    }
549   
550    /**
551    * Highlight sequence ids that match the given list, and if necessary scroll
552    * to the start sequence of the list.
553    *
554    * @param list
555    */
 
556  0 toggle public void highlightSearchResults(List<SequenceI> list)
557    {
558  0 getIdCanvas().setHighlighted(list);
559   
560  0 if (list == null || list.isEmpty())
561    {
562  0 return;
563    }
564   
565  0 int index = av.getAlignment().findIndex(list.get(0));
566   
567    // do we need to scroll the panel?
568  0 if ((av.getRanges().getStartSeq() > index)
569    || (av.getRanges().getEndSeq() < index))
570    {
571  0 av.getRanges().setStartSeq(index);
572    }
573    }
574   
 
575  1865 toggle public IdCanvas getIdCanvas()
576    {
577  1865 return idCanvas;
578    }
579   
 
580  267 toggle public void setIdCanvas(IdCanvas idCanvas)
581    {
582  267 this.idCanvas = idCanvas;
583    }
584   
585    /**
586    * Performs scrolling of the visible alignment up or down, adding newly
587    * visible sequences to the current selection
588    */
 
589    class ScrollThread extends Thread
590    {
591    private boolean running = false;
592   
593    private boolean up;
594   
595    /**
596    * Constructor for a thread that scrolls either up or down
597    *
598    * @param up
599    */
 
600  0 toggle public ScrollThread(boolean up)
601    {
602  0 this.up = up;
603  0 setName("IdPanel$ScrollThread$" + String.valueOf(up));
604    }
605   
606    /**
607    * Sets a flag to stop the scrolling
608    */
 
609  0 toggle public void stopScrolling()
610    {
611  0 running = false;
612    }
613   
614    /**
615    * Scrolls the alignment either up or down, one row at a time, adding newly
616    * visible sequences to the current selection. Speed is limited to a maximum
617    * of ten rows per second. The thread exits when the end of the alignment is
618    * reached or a flag is set to stop it by a call to stopScrolling.
619    */
 
620  0 toggle @Override
621    public void run()
622    {
623  0 running = true;
624   
625  0 while (running)
626    {
627  0 running = scrollOnce();
628  0 try
629    {
630  0 Thread.sleep(100);
631    } catch (Exception ex)
632    {
633    }
634    }
635  0 IdPanel.this.scrollThread = null;
636    }
637   
638    /**
639    * Scrolls one row up or down. Answers true if a scroll could be done, false
640    * if not (top or bottom of alignment reached).
641    */
 
642  0 toggle boolean scrollOnce()
643    {
644  0 ViewportRanges ranges = IdPanel.this.av.getRanges();
645  0 if (ranges.scrollUp(up))
646    {
647  0 int toSeq = up ? ranges.getStartSeq() : ranges.getEndSeq();
648  0 int fromSeq = toSeq < lastid ? lastid - 1 : lastid + 1;
649  0 IdPanel.this.selectSeqs(fromSeq, toSeq);
650  0 lastid = toSeq;
651  0 alignPanel.paintAlignment(false, false);
652  0 return true;
653    }
654   
655  0 return false;
656    }
657    }
658    }