Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.gui

File ScalePanel.java

 

Coverage histogram

../../img/srcFileCovDistChart3.png
47% of files have more coverage

Code metrics

70
169
16
1
568
391
57
0.34
10.56
16
3.56

Classes

Class Line # Actions
ScalePanel 59 169 57 180
0.2941176629.4%
 

Contributing tests

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