Clover icon

Coverage Report

  1. Project Clover database Mon Nov 18 2024 09:56:54 GMT
  2. Package jalview.gui

File IdPanel.java

 

Coverage histogram

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

Code metrics

74
156
23
2
620
381
74
0.47
6.78
11.5
3.22

Classes

Class Line # Actions
IdPanel 52 138 65
0.067264576.7%
IdPanel.ScrollThread 551 18 9
0.00%
 

Contributing tests

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