Clover icon

Coverage Report

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

File ScalePanel.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
29% of files have more coverage

Code metrics

72
177
18
1
604
411
59
0.33
9.83
18
3.28

Classes

Class Line # Actions
ScalePanel 65 177 59
0.6779026467.8%
 

Contributing tests

This file is covered by 193 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.Color;
24    import java.awt.FontMetrics;
25    import java.awt.Graphics;
26    import java.awt.Graphics2D;
27    import java.awt.Point;
28    import java.awt.RenderingHints;
29    import java.awt.event.ActionEvent;
30    import java.awt.event.ActionListener;
31    import java.awt.event.MouseEvent;
32    import java.awt.event.MouseListener;
33    import java.awt.event.MouseMotionListener;
34    import java.beans.PropertyChangeEvent;
35    import java.util.Iterator;
36    import java.util.List;
37   
38    import javax.swing.JMenuItem;
39    import javax.swing.JPanel;
40    import javax.swing.JPopupMenu;
41    import javax.swing.ToolTipManager;
42   
43    import jalview.api.AlignViewportI;
44    import jalview.datamodel.AlignedCodonFrame;
45    import jalview.datamodel.AlignmentI;
46    import jalview.datamodel.ColumnSelection;
47    import jalview.datamodel.HiddenColumns;
48    import jalview.datamodel.SearchResults;
49    import jalview.datamodel.SearchResultsI;
50    import jalview.datamodel.SequenceGroup;
51    import jalview.renderer.ScaleRenderer;
52    import jalview.renderer.ScaleRenderer.ScaleMark;
53    import jalview.structure.StructureSelectionManager;
54    import jalview.util.MappingUtils;
55    import jalview.util.MessageManager;
56    import jalview.util.Platform;
57    import jalview.viewmodel.ViewportListenerI;
58    import jalview.viewmodel.ViewportRanges;
59    import jalview.workers.AlignmentComparisonThread;
60   
61    /**
62    * The panel containing the sequence ruler (when not in wrapped mode), and
63    * supports a range of mouse operations to select, hide or reveal columns.
64    */
 
65    public class ScalePanel extends JPanel
66    implements MouseMotionListener, MouseListener, ViewportListenerI
67    {
68    protected int offy = 4;
69   
70    public int width;
71   
72    protected AlignViewport av;
73   
74    AlignmentPanel ap;
75   
76    boolean stretchingGroup = false;
77   
78    /*
79    * min, max hold the extent of a mouse drag action
80    */
81    int min;
82   
83    int max;
84   
85    boolean mouseDragging = false;
86   
87    /*
88    * holds a hidden column range when the mouse is over an adjacent column
89    */
90    int[] reveal;
91   
92    /**
93    * Constructor
94    *
95    * @param av
96    * @param ap
97    */
 
98  475 toggle public ScalePanel(AlignViewport av, AlignmentPanel ap)
99    {
100  475 this.av = av;
101  475 this.ap = ap;
102   
103  475 addMouseListener(this);
104  475 addMouseMotionListener(this);
105   
106  475 av.getRanges().addPropertyChangeListener(this);
107    }
108   
109    /**
110    * DOCUMENT ME!
111    *
112    * @param evt
113    * DOCUMENT ME!
114    */
 
115  3 toggle @Override
116    public void mousePressed(MouseEvent evt)
117    {
118  3 int res = ap.getSeqPanel().findAlignmentColumn(evt);
119   
120  3 min = res;
121  3 max = res;
122   
123  3 if (evt.isPopupTrigger()) // Mac: mousePressed
124    {
125  0 rightMouseButtonPressed(evt, res);
126  0 return;
127    }
128  3 if (Platform.isWinRightButton(evt))
129    {
130    /*
131    * defer right-mouse click handling to mouse up on Windows
132    * (where isPopupTrigger() will answer true)
133    * but accept Cmd-click on Mac which passes isRightMouseButton
134    */
135  0 return;
136    }
137  3 leftMouseButtonPressed(evt, res);
138    }
139   
140    /**
141    * Handles right mouse button press. If pressed in a selected column, opens
142    * context menu for 'Hide Columns'. If pressed on a hidden columns marker,
143    * opens context menu for 'Reveal / Reveal All'. Else does nothing.
144    *
145    * @param evt
146    * @param res
147    */
 
148  0 toggle protected void rightMouseButtonPressed(MouseEvent evt, final int res)
149    {
150  0 JPopupMenu pop = buildPopupMenu(res);
151  0 if (pop.getSubElements().length > 0)
152    {
153  0 pop.show(this, evt.getX(), evt.getY());
154    }
155    }
156   
157    /**
158    * Builds a popup menu with 'Hide' or 'Reveal' options, or both, or neither
159    *
160    * @param res
161    * column number (0..)
162    * @return
163    */
 
164  9 toggle protected JPopupMenu buildPopupMenu(final int res)
165    {
166  9 JPopupMenu pop = new JPopupMenu();
167   
168    /*
169    * logic here depends on 'reveal', set in mouseMoved;
170    * grab the hidden range in case mouseMoved nulls it later
171    */
172  9 final int[] hiddenRange = reveal;
173  9 if (hiddenRange != null)
174    {
175  6 JMenuItem item = new JMenuItem(
176    MessageManager.getString("label.reveal"));
177  6 item.addActionListener(new ActionListener()
178    {
 
179  0 toggle @Override
180    public void actionPerformed(ActionEvent e)
181    {
182  0 av.showColumn(hiddenRange[0]);
183  0 reveal = null;
184  0 ap.updateLayout();
185  0 ap.paintAlignment(true, true);
186  0 av.sendSelection();
187    }
188    });
189  6 pop.add(item);
190   
191  6 if (av.getAlignment().getHiddenColumns()
192    .hasMultiHiddenColumnRegions())
193    {
194  2 item = new JMenuItem(MessageManager.getString("action.reveal_all"));
195  2 item.addActionListener(new ActionListener()
196    {
 
197  0 toggle @Override
198    public void actionPerformed(ActionEvent e)
199    {
200  0 av.showAllHiddenColumns();
201  0 reveal = null;
202  0 ap.updateLayout();
203  0 ap.paintAlignment(true, true);
204  0 av.sendSelection();
205    }
206    });
207  2 pop.add(item);
208    }
209    }
210   
211  9 if (av.getColumnSelection().contains(res))
212    {
213  4 JMenuItem item = new JMenuItem(
214    MessageManager.getString("label.hide_columns"));
215  4 item.addActionListener(new ActionListener()
216    {
 
217  0 toggle @Override
218    public void actionPerformed(ActionEvent e)
219    {
220  0 av.hideColumns(res, res);
221  0 if (av.getSelectionGroup() != null && av.getSelectionGroup()
222    .getSize() == av.getAlignment().getHeight())
223    {
224  0 av.setSelectionGroup(null);
225    }
226   
227  0 ap.updateLayout();
228  0 ap.paintAlignment(true, true);
229  0 av.sendSelection();
230    }
231    });
232  4 pop.add(item);
233    }
234  9 return pop;
235    }
236   
237    /**
238    * Handles left mouse button press
239    *
240    * @param evt
241    * @param res
242    */
 
243  3 toggle protected void leftMouseButtonPressed(MouseEvent evt, final int res)
244    {
245    /*
246    * Ctrl-click/Cmd-click adds to the selection
247    * Shift-click extends the selection
248    */
249    // TODO Problem: right-click on Windows not reported until mouseReleased?!?
250  3 if (!Platform.isControlDown(evt) && !evt.isShiftDown())
251    {
252  3 av.getColumnSelection().clear();
253    }
254   
255  3 av.getColumnSelection().addElement(res);
256  3 SequenceGroup sg = new SequenceGroup(av.getAlignment().getSequences());
257  3 sg.setStartRes(res);
258  3 sg.setEndRes(res);
259   
260  3 if (evt.isShiftDown())
261    {
262  0 int min = Math.min(av.getColumnSelection().getMin(), res);
263  0 int max = Math.max(av.getColumnSelection().getMax(), res);
264  0 for (int i = min; i < max; i++)
265    {
266  0 av.getColumnSelection().addElement(i);
267    }
268  0 sg.setStartRes(min);
269  0 sg.setEndRes(max);
270    }
271  3 av.setSelectionGroup(sg);
272  3 ap.paintAlignment(false, false);
273  3 PaintRefresher.Refresh(this, av.getSequenceSetId());
274  3 av.sendSelection();
275    }
276   
277    /**
278    * Action on mouseUp is to set the limit of the current selection group (if
279    * there is one) and broadcast the selection
280    *
281    * @param evt
282    */
 
283  3 toggle @Override
284    public void mouseReleased(MouseEvent evt)
285    {
286  3 boolean wasDragging = mouseDragging;
287  3 mouseDragging = false;
288  3 ap.getSeqPanel().stopScrolling();
289   
290  3 int res = ap.getSeqPanel().findAlignmentColumn(evt);
291   
292  3 if (!stretchingGroup)
293    {
294  0 if (evt.isPopupTrigger()) // Windows: mouseReleased
295    {
296  0 rightMouseButtonPressed(evt, res);
297    }
298    else
299    {
300  0 ap.paintAlignment(false, false);
301    }
302  0 return;
303    }
304   
305  3 SequenceGroup sg = av.getSelectionGroup();
306   
307  3 if (sg != null)
308    {
309  3 if (res > sg.getStartRes())
310    {
311  2 sg.setEndRes(res);
312    }
313  1 else if (res < sg.getStartRes())
314    {
315  0 sg.setStartRes(res);
316    }
317  3 if (wasDragging)
318    {
319  3 min = Math.min(res, min);
320  3 max = Math.max(res, max);
321  3 av.getColumnSelection().stretchGroup(res, sg, min, max);
322    }
323    }
324  3 stretchingGroup = false;
325  3 ap.paintAlignment(false, false);
326  3 av.isSelectionGroupChanged(true);
327  3 av.isColSelChanged(true);
328  3 PaintRefresher.Refresh(ap, av.getSequenceSetId());
329  3 av.sendSelection();
330    }
331   
332    /**
333    * Action on dragging the mouse in the scale panel is to expand or shrink the
334    * selection group range (including any hidden columns that it spans). Note
335    * that the selection is only broadcast at the start of the drag (on
336    * mousePressed) and at the end (on mouseReleased), to avoid overload
337    * redrawing of other views.
338    *
339    * @param evt
340    */
 
341  3 toggle @Override
342    public void mouseDragged(MouseEvent evt)
343    {
344  3 mouseDragging = true;
345  3 int res = ap.getSeqPanel().findAlignmentColumn(evt);
346   
347  3 ColumnSelection cs = av.getColumnSelection();
348   
349  3 min = Math.min(res, min);
350  3 max = Math.max(res, max);
351   
352  3 SequenceGroup sg = av.getSelectionGroup();
353  3 if (sg != null)
354    {
355  3 stretchingGroup = true;
356  3 cs.stretchGroup(res, sg, min, max);
357  3 ap.paintAlignment(false, false);
358  3 PaintRefresher.Refresh(ap, av.getSequenceSetId());
359    }
360    }
361   
 
362  0 toggle @Override
363    public void mouseEntered(MouseEvent evt)
364    {
365  0 if (mouseDragging)
366    {
367  0 mouseDragging = false;
368  0 ap.getSeqPanel().stopScrolling();
369    }
370    }
371   
372    /**
373    * Action on leaving the panel bounds with mouse drag in progress is to start
374    * scrolling the alignment in the direction of the mouse. To restrict
375    * scrolling to left-right (not up-down), the y-value of the mouse position is
376    * replaced with zero.
377    */
 
378  0 toggle @Override
379    public void mouseExited(MouseEvent evt)
380    {
381  0 if (mouseDragging)
382    {
383  0 ap.getSeqPanel().startScrolling(new Point(evt.getX(), 0));
384    }
385    }
386   
 
387  0 toggle @Override
388    public void mouseClicked(MouseEvent evt)
389    {
390    }
391   
392    /**
393    * Creates a tooltip when the mouse is over a hidden columns marker
394    */
 
395  8 toggle @Override
396    public void mouseMoved(MouseEvent evt)
397    {
398  8 this.setToolTipText(null);
399  8 reveal = null;
400  8 final int res = ap.getSeqPanel().findAlignmentColumn(evt);
401   
402  8 highlightAllStructPos(res);
403  8 if (av.getCalcManager().getRegisteredWorkersOfClass(AlignmentComparisonThread.class)!=null)
404    {
405  0 AlignmentI alignment = av.getAlignment();
406  0 AlignViewportI codingComplement = av.getCodingComplement();
407  0 List<AlignedCodonFrame> ourMappings = alignment
408    .getCodonFrames();
409  0 SearchResultsI mappedPos = MappingUtils.allMappedRegionsForColumn(res,
410    ourMappings, alignment.getSequences(),codingComplement.getAlignment().getSequences(),
411    alignment.getGapCharacter());
412  0 if (mappedPos.getCount()>0)
413    {
414  0 Desktop.getAlignFrameFor(codingComplement).alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(mappedPos,true);
415    }
416   
417    }
418  8 if (!av.hasHiddenColumns())
419    {
420  0 return;
421    }
422  8 reveal = av.getAlignment().getHiddenColumns()
423    .getRegionWithEdgeAtRes(av.getAlignment().getHiddenColumns()
424    .absoluteToVisibleColumn(res));
425  8 if (reveal == null)
426    {
427  3 return;
428    }
429  5 ToolTipManager.sharedInstance().registerComponent(this);
430  5 this.setToolTipText(
431    MessageManager.getString("label.reveal_hidden_columns"));
432  5 repaint();
433    }
434   
 
435  8 toggle public void highlightAllStructPos(int col)
436    {
437  8 ap.getStructureSelectionManager().highlightPositionsOnMany(
438    ap.av.getAlignment().getSequencesArray(), new int[]
439    { col, col }, ap);
440   
441    }
442   
443    /**
444    * DOCUMENT ME!
445    *
446    * @param g
447    * DOCUMENT ME!
448    */
 
449  2450 toggle @Override
450    public void paintComponent(Graphics g)
451    {
452    // super.paintComponent(g); // BH 2019
453   
454    /*
455    * shouldn't get called in wrapped mode as the scale above is
456    * drawn instead by SeqCanvas.drawNorthScale
457    */
458  2450 if (!av.getWrapAlignment())
459    {
460  2207 drawScale(g, av.getRanges().getStartRes(), av.getRanges().getEndRes(),
461    getWidth(), getHeight());
462    }
463    }
464   
465    // scalewidth will normally be screenwidth,
 
466  2245 toggle public void drawScale(Graphics g, int startx, int endx, int width,
467    int height)
468    {
469  2245 Graphics2D gg = (Graphics2D) g;
470  2245 gg.setFont(av.getFont());
471   
472  2245 if (av.antiAlias)
473    {
474  762 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
475    RenderingHints.VALUE_ANTIALIAS_ON);
476    }
477   
478    // Fill in the background
479  2245 gg.setColor(Color.white);
480  2245 gg.fillRect(0, 0, width, height);
481  2245 gg.setColor(Color.black);
482   
483    // Fill the selected columns
484  2245 ColumnSelection cs = av.getColumnSelection();
485  2245 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
486  2245 int avCharWidth = av.getCharWidth();
487  2245 int avCharHeight = av.getCharHeight();
488   
489  2245 if (cs != null)
490    {
491  2245 gg.setColor(new Color(220, 0, 0));
492   
493  2245 for (int sel : cs.getSelected())
494    {
495    // TODO: JAL-2001 - provide a fast method to list visible selected in a
496    // given range
497   
498  42 if (av.hasHiddenColumns())
499    {
500  0 if (hidden.isVisible(sel))
501    {
502  0 sel = hidden.absoluteToVisibleColumn(sel);
503    }
504    else
505    {
506  0 continue;
507    }
508    }
509   
510  42 if ((sel >= startx) && (sel <= endx))
511    {
512  42 gg.fillRect((sel - startx) * avCharWidth, 0, avCharWidth,
513    getHeight());
514    }
515    }
516    }
517   
518  2245 int widthx = 1 + endx - startx;
519   
520  2245 FontMetrics fm = gg.getFontMetrics(av.getFont());
521  2245 int y = avCharHeight;
522  2245 int yOf = fm.getDescent();
523  2245 y -= yOf;
524  2245 if (av.hasHiddenColumns())
525    {
526    // draw any hidden column markers
527  261 gg.setColor(Color.blue);
528  261 int res;
529   
530  261 if (av.getShowHiddenMarkers())
531    {
532  261 Iterator<Integer> it = hidden.getStartRegionIterator(startx,
533    startx + widthx + 1);
534  513 while (it.hasNext())
535    {
536  252 res = it.next() - startx;
537   
538  252 gg.fillPolygon(
539    new int[]
540    { -1 + res * avCharWidth - avCharHeight / 4,
541    -1 + res * avCharWidth + avCharHeight / 4,
542    -1 + res * avCharWidth },
543    new int[]
544    { y, y, y + 2 * yOf }, 3);
545    }
546    }
547    }
548    // Draw the scale numbers
549  2245 gg.setColor(Color.black);
550   
551  2245 int maxX = 0;
552  2245 List<ScaleMark> marks = new ScaleRenderer().calculateMarks(av, startx,
553    endx);
554   
555  2245 for (ScaleMark mark : marks)
556    {
557  20416 boolean major = mark.major;
558  20416 int mpos = mark.column; // (i - startx - 1)
559  20416 String mstring = mark.text;
560  20416 if (mstring != null)
561    {
562  9730 if (mpos * avCharWidth > maxX)
563    {
564  9579 gg.drawString(mstring, mpos * avCharWidth, y);
565  9579 maxX = (mpos + 2) * avCharWidth + fm.stringWidth(mstring);
566    }
567    }
568  20416 if (major)
569    {
570  9730 gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + 2,
571    (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2));
572    }
573    else
574    {
575  10686 gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + yOf,
576    (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2));
577    }
578    }
579    }
580   
 
581  610 toggle @Override
582    public void propertyChange(PropertyChangeEvent evt)
583    {
584    // Respond to viewport change events (e.g. alignment panel was scrolled)
585    // Both scrolling and resizing change viewport ranges: scrolling changes
586    // both start and end points, but resize only changes end values.
587    // Here we only want to fastpaint on a scroll, with resize using a normal
588    // paint, so scroll events are identified as changes to the horizontal or
589    // vertical start value.
590  610 if (evt.getPropertyName().equals(ViewportRanges.STARTRES)
591    || evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)
592    || evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
593    {
594    // scroll event, repaint panel
595   
596    // Call repaint on alignment panel so that repaints from other alignment
597    // panel components can be aggregated. Otherwise performance of the
598    // overview
599    // window and others may be adversely affected.
600  32 av.getAlignPanel().repaint();
601    }
602    }
603   
604    }