Clover icon

Coverage Report

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

File ScalePanel.java

 

Coverage histogram

../../img/srcFileCovDistChart8.png
20% of files have more coverage

Code metrics

70
178
17
1
588
395
57
0.32
10.47
17
3.35

Classes

Class Line # Actions
ScalePanel 57 178 57
0.716981171.7%
 

Contributing tests

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