Clover icon

jalviewX

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

File ViewportRanges.java

 

Coverage histogram

../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

88
180
35
1
795
425
86
0.48
5.14
35
2.46

Classes

Class Line # Actions
ViewportRanges 32 180 86 4
0.986798798.7%
 

Contributing tests

This file is covered by 166 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.viewmodel;
22   
23    import jalview.datamodel.AlignmentI;
24    import jalview.datamodel.HiddenColumns;
25   
26    /**
27    * Supplies and updates viewport properties relating to position such as: start
28    * and end residues and sequences; ideally will serve hidden columns/rows too.
29    * Intention also to support calculations for positioning, scrolling etc. such
30    * as finding the middle of the viewport, checking for scrolls off screen
31    */
 
32    public class ViewportRanges extends ViewportProperties
33    {
34    public static final String STARTRES = "startres";
35   
36    public static final String ENDRES = "endres";
37   
38    public static final String STARTSEQ = "startseq";
39   
40    public static final String ENDSEQ = "endseq";
41   
42    public static final String STARTRESANDSEQ = "startresandseq";
43   
44    public static final String MOVE_VIEWPORT = "move_viewport";
45   
46    private boolean wrappedMode = false;
47   
48    // start residue of viewport
49    private int startRes;
50   
51    // end residue of viewport
52    private int endRes;
53   
54    // start sequence of viewport
55    private int startSeq;
56   
57    // end sequence of viewport
58    private int endSeq;
59   
60    // alignment
61    private AlignmentI al;
62   
63    /**
64    * Constructor
65    *
66    * @param alignment
67    * the viewport's alignment
68    */
 
69  322 toggle public ViewportRanges(AlignmentI alignment)
70    {
71    // initial values of viewport settings
72  322 this.startRes = 0;
73  322 this.endRes = alignment.getWidth() - 1;
74  322 this.startSeq = 0;
75  322 this.endSeq = alignment.getHeight() - 1;
76  322 this.al = alignment;
77    }
78   
79    /**
80    * Get alignment width in cols, including hidden cols
81    */
 
82  301 toggle public int getAbsoluteAlignmentWidth()
83    {
84  301 return al.getWidth();
85    }
86   
87    /**
88    * Get alignment height in rows, including hidden rows
89    */
 
90  302 toggle public int getAbsoluteAlignmentHeight()
91    {
92  302 return al.getHeight() + al.getHiddenSequences().getSize();
93    }
94   
95    /**
96    * Get alignment width in cols, excluding hidden cols
97    */
 
98  2463 toggle public int getVisibleAlignmentWidth()
99    {
100  2463 return al.getWidth() - al.getHiddenColumns().getSize();
101    }
102   
103    /**
104    * Get alignment height in rows, excluding hidden rows
105    */
 
106  2364 toggle public int getVisibleAlignmentHeight()
107    {
108  2364 return al.getHeight();
109    }
110   
111    /**
112    * Set first residue visible in the viewport, and retain the current width.
113    * Fires a property change event.
114    *
115    * @param res
116    * residue position
117    */
 
118  120 toggle public void setStartRes(int res)
119    {
120  120 int width = getViewportWidth();
121  120 setStartEndRes(res, res + width - 1);
122    }
123   
124    /**
125    * Set start and end residues at the same time. This method only fires one
126    * event for the two changes, and should be used in preference to separate
127    * calls to setStartRes and setEndRes.
128    *
129    * @param start
130    * the start residue
131    * @param end
132    * the end residue
133    */
 
134  708 toggle public void setStartEndRes(int start, int end)
135    {
136  708 int[] oldvalues = updateStartEndRes(start, end);
137  708 int oldstartres = oldvalues[0];
138  708 int oldendres = oldvalues[1];
139   
140  708 changeSupport.firePropertyChange(STARTRES, oldstartres, startRes);
141  708 if (oldstartres == startRes)
142    {
143    // event won't be fired if start positions are same
144    // fire an event for the end positions in case they changed
145  575 changeSupport.firePropertyChange(ENDRES, oldendres, endRes);
146    }
147    }
148   
149    /**
150    * Update start and end residue values, adjusting for width constraints if
151    * necessary
152    *
153    * @param start
154    * start residue
155    * @param end
156    * end residue
157    * @return array containing old start and end residue values
158    */
 
159  831 toggle private int[] updateStartEndRes(int start, int end)
160    {
161  831 int oldstartres = this.startRes;
162   
163    /*
164    * if not wrapped, don't leave white space at the right margin
165    */
166  831 int lastColumn = getVisibleAlignmentWidth() - 1;
167  831 if (!wrappedMode && (start > lastColumn))
168    {
169  4 startRes = Math.max(lastColumn, 0);
170    }
171  827 else if (start < 0)
172    {
173  4 startRes = 0;
174    }
175    else
176    {
177  823 startRes = start;
178    }
179   
180  831 int oldendres = this.endRes;
181  831 if (end < 0)
182    {
183  3 endRes = 0;
184    }
185  828 else if (!wrappedMode && (end > lastColumn))
186    {
187  75 endRes = Math.max(lastColumn, 0);
188    }
189    else
190    {
191  753 endRes = end;
192    }
193  831 return new int[] { oldstartres, oldendres };
194    }
195   
196    /**
197    * Set the first sequence visible in the viewport, maintaining the height. If
198    * the viewport would extend past the last sequence, sets the viewport so it
199    * sits at the bottom of the alignment. Fires a property change event.
200    *
201    * @param seq
202    * sequence position
203    */
 
204  386 toggle public void setStartSeq(int seq)
205    {
206  386 int startseq = seq;
207  386 int height = getViewportHeight();
208  386 if (startseq + height - 1 > getVisibleAlignmentHeight() - 1)
209    {
210  69 startseq = getVisibleAlignmentHeight() - height;
211    }
212  386 setStartEndSeq(startseq, startseq + height - 1);
213    }
214   
215    /**
216    * Set start and end sequences at the same time. The viewport height may
217    * change. This method only fires one event for the two changes, and should be
218    * used in preference to separate calls to setStartSeq and setEndSeq.
219    *
220    * @param start
221    * the start sequence
222    * @param end
223    * the end sequence
224    */
 
225  864 toggle public void setStartEndSeq(int start, int end)
226    {
227  864 int[] oldvalues = updateStartEndSeq(start, end);
228  864 int oldstartseq = oldvalues[0];
229  864 int oldendseq = oldvalues[1];
230   
231  864 changeSupport.firePropertyChange(STARTSEQ, oldstartseq, startSeq);
232  864 if (oldstartseq == startSeq)
233    {
234    // event won't be fired if start positions are the same
235    // fire in case the end positions changed
236  802 changeSupport.firePropertyChange(ENDSEQ, oldendseq, endSeq);
237    }
238    }
239   
240    /**
241    * Update start and end sequence values, adjusting for height constraints if
242    * necessary
243    *
244    * @param start
245    * start sequence
246    * @param end
247    * end sequence
248    * @return array containing old start and end sequence values
249    */
 
250  987 toggle private int[] updateStartEndSeq(int start, int end)
251    {
252  987 int oldstartseq = this.startSeq;
253  987 int visibleHeight = getVisibleAlignmentHeight();
254  987 if (start > visibleHeight - 1)
255    {
256  3 startSeq = Math.max(visibleHeight - 1, 0);
257    }
258  984 else if (start < 0)
259    {
260  72 startSeq = 0;
261    }
262    else
263    {
264  912 startSeq = start;
265    }
266   
267  987 int oldendseq = this.endSeq;
268  987 if (end >= visibleHeight)
269    {
270  55 endSeq = Math.max(visibleHeight - 1, 0);
271    }
272  932 else if (end < 0)
273    {
274  65 endSeq = 0;
275    }
276    else
277    {
278  867 endSeq = end;
279    }
280  987 return new int[] { oldstartseq, oldendseq };
281    }
282   
283    /**
284    * Set the last sequence visible in the viewport. Fires a property change
285    * event.
286    *
287    * @param seq
288    * sequence position
289    */
 
290  6 toggle public void setEndSeq(int seq)
291    {
292  6 int height = getViewportHeight();
293  6 setStartEndSeq(seq - height + 1, seq);
294    }
295   
296    /**
297    * Set start residue and start sequence together (fires single event). The
298    * event supplies a pair of old values and a pair of new values: [old start
299    * residue, old start sequence] and [new start residue, new start sequence]
300    *
301    * @param res
302    * the start residue
303    * @param seq
304    * the start sequence
305    */
 
306  119 toggle public void setStartResAndSeq(int res, int seq)
307    {
308  119 int width = getViewportWidth();
309  119 int[] oldresvalues = updateStartEndRes(res, res + width - 1);
310   
311  119 int startseq = seq;
312  119 int height = getViewportHeight();
313  119 if (startseq + height - 1 > getVisibleAlignmentHeight() - 1)
314    {
315  1 startseq = getVisibleAlignmentHeight() - height;
316    }
317  119 int[] oldseqvalues = updateStartEndSeq(startseq, startseq + height - 1);
318   
319  119 int[] old = new int[] { oldresvalues[0], oldseqvalues[0] };
320  119 int[] newresseq = new int[] { startRes, startSeq };
321  119 changeSupport.firePropertyChange(STARTRESANDSEQ, old, newresseq);
322    }
323   
324    /**
325    * Get start residue of viewport
326    */
 
327  6755 toggle public int getStartRes()
328    {
329  6755 return startRes;
330    }
331   
332    /**
333    * Get end residue of viewport
334    */
 
335  4969 toggle public int getEndRes()
336    {
337  4969 return endRes;
338    }
339   
340    /**
341    * Get start sequence of viewport
342    */
 
343  4853 toggle public int getStartSeq()
344    {
345  4853 return startSeq;
346    }
347   
348    /**
349    * Get end sequence of viewport
350    */
 
351  2999 toggle public int getEndSeq()
352    {
353  2999 return endSeq;
354    }
355   
356    /**
357    * Set viewport width in residues, without changing startRes. Use in
358    * preference to calculating endRes from the width, to avoid out by one
359    * errors! Fires a property change event.
360    *
361    * @param w
362    * width in residues
363    */
 
364  186 toggle public void setViewportWidth(int w)
365    {
366  186 setStartEndRes(startRes, startRes + w - 1);
367    }
368   
369    /**
370    * Set viewport height in residues, without changing startSeq. Use in
371    * preference to calculating endSeq from the height, to avoid out by one
372    * errors! Fires a property change event.
373    *
374    * @param h
375    * height in sequences
376    */
 
377  160 toggle public void setViewportHeight(int h)
378    {
379  160 setStartEndSeq(startSeq, startSeq + h - 1);
380    }
381   
382    /**
383    * Set viewport horizontal start position and width. Use in preference to
384    * calculating endRes from the width, to avoid out by one errors! Fires a
385    * property change event.
386    *
387    * @param start
388    * start residue
389    * @param w
390    * width in residues
391    */
 
392  383 toggle public void setViewportStartAndWidth(int start, int w)
393    {
394  383 int vpstart = start;
395  383 if (vpstart < 0)
396    {
397  2 vpstart = 0;
398    }
399   
400    /*
401    * if not wrapped, don't leave white space at the right margin
402    */
403  383 if (!wrappedMode)
404    {
405  228 if ((w <= getVisibleAlignmentWidth())
406    && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1))
407    {
408  1 vpstart = getVisibleAlignmentWidth() - w;
409    }
410   
411    }
412  383 setStartEndRes(vpstart, vpstart + w - 1);
413    }
414   
415    /**
416    * Set viewport vertical start position and height. Use in preference to
417    * calculating endSeq from the height, to avoid out by one errors! Fires a
418    * property change event.
419    *
420    * @param start
421    * start sequence
422    * @param h
423    * height in sequences
424    */
 
425  297 toggle public void setViewportStartAndHeight(int start, int h)
426    {
427  297 int vpstart = start;
428  297 if (vpstart < 0)
429    {
430  3 vpstart = 0;
431    }
432  294 else if ((h <= getVisibleAlignmentHeight())
433    && (vpstart + h - 1 > getVisibleAlignmentHeight() - 1))
434    // viewport height is less than the full alignment and we are running off
435    // the bottom
436    {
437  5 vpstart = getVisibleAlignmentHeight() - h;
438    }
439  297 setStartEndSeq(vpstart, vpstart + h - 1);
440    }
441   
442    /**
443    * Get width of viewport in residues
444    *
445    * @return width of viewport
446    */
 
447  2308 toggle public int getViewportWidth()
448    {
449  2308 return (endRes - startRes + 1);
450    }
451   
452    /**
453    * Get height of viewport in residues
454    *
455    * @return height of viewport
456    */
 
457  1098 toggle public int getViewportHeight()
458    {
459  1098 return (endSeq - startSeq + 1);
460    }
461   
462    /**
463    * Scroll the viewport range vertically. Fires a property change event.
464    *
465    * @param up
466    * true if scrolling up, false if down
467    *
468    * @return true if the scroll is valid
469    */
 
470  28 toggle public boolean scrollUp(boolean up)
471    {
472    /*
473    * if in unwrapped mode, scroll up or down one sequence row;
474    * if in wrapped mode, scroll by one visible width of columns
475    */
476  28 if (up)
477    {
478  18 if (wrappedMode)
479    {
480  2 pageUp();
481    }
482    else
483    {
484  16 if (startSeq < 1)
485    {
486  2 return false;
487    }
488  14 setStartSeq(startSeq - 1);
489    }
490    }
491    else
492    {
493  10 if (wrappedMode)
494    {
495  4 pageDown();
496    }
497    else
498    {
499  6 if (endSeq >= getVisibleAlignmentHeight() - 1)
500    {
501  2 return false;
502    }
503  4 setStartSeq(startSeq + 1);
504    }
505    }
506  24 return true;
507    }
508   
509    /**
510    * Scroll the viewport range horizontally. Fires a property change event.
511    *
512    * @param right
513    * true if scrolling right, false if left
514    *
515    * @return true if the scroll is valid
516    */
 
517  24 toggle public boolean scrollRight(boolean right)
518    {
519  24 if (!right)
520    {
521  18 if (startRes < 1)
522    {
523  2 return false;
524    }
525   
526  16 setStartRes(startRes - 1);
527    }
528    else
529    {
530  6 if (endRes >= getVisibleAlignmentWidth() - 1)
531    {
532  2 return false;
533    }
534   
535  4 setStartRes(startRes + 1);
536    }
537   
538  20 return true;
539    }
540   
541    /**
542    * Scroll a wrapped alignment so that the specified residue is in the first
543    * repeat of the wrapped view. Fires a property change event. Answers true if
544    * the startRes changed, else false.
545    *
546    * @param res
547    * residue position to scroll to NB visible position not absolute
548    * alignment position
549    * @return
550    */
 
551  7 toggle public boolean scrollToWrappedVisible(int res)
552    {
553  7 int newStartRes = calcWrappedStartResidue(res);
554  7 if (newStartRes == startRes)
555    {
556  3 return false;
557    }
558  4 setStartRes(newStartRes);
559   
560  4 return true;
561    }
562   
563    /**
564    * Calculate wrapped start residue from visible start residue
565    *
566    * @param res
567    * visible start residue
568    * @return left column of panel res will be located in
569    */
 
570  9 toggle private int calcWrappedStartResidue(int res)
571    {
572  9 int oldStartRes = startRes;
573  9 int width = getViewportWidth();
574   
575  9 boolean up = res < oldStartRes;
576  9 int widthsToScroll = Math.abs((res - oldStartRes) / width);
577  9 if (up)
578    {
579  2 widthsToScroll++;
580    }
581   
582  9 int residuesToScroll = width * widthsToScroll;
583  9 int newStartRes = up ? oldStartRes - residuesToScroll : oldStartRes
584    + residuesToScroll;
585  9 if (newStartRes < 0)
586    {
587  2 newStartRes = 0;
588    }
589  9 return newStartRes;
590    }
591   
592    /**
593    * Scroll so that (x,y) is visible. Fires a property change event.
594    *
595    * @param x
596    * x position in alignment (absolute position)
597    * @param y
598    * y position in alignment (absolute position)
599    */
 
600  5 toggle public void scrollToVisible(int x, int y)
601    {
602  16 while (y < startSeq)
603    {
604  11 scrollUp(true);
605    }
606  6 while (y > endSeq)
607    {
608  1 scrollUp(false);
609    }
610   
611  5 HiddenColumns hidden = al.getHiddenColumns();
612  18 while (x < hidden.visibleToAbsoluteColumn(startRes))
613    {
614  13 if (!scrollRight(false))
615    {
616  0 break;
617    }
618    }
619  6 while (x > hidden.visibleToAbsoluteColumn(endRes))
620    {
621  1 if (!scrollRight(true))
622    {
623  0 break;
624    }
625    }
626    }
627   
628    /**
629    * Set the viewport location so that a position is visible
630    *
631    * @param x
632    * column to be visible: absolute position in alignment
633    * @param y
634    * row to be visible: absolute position in alignment
635    */
 
636  7 toggle public boolean setViewportLocation(int x, int y)
637    {
638  7 boolean changedLocation = false;
639   
640    // convert the x,y location to visible coordinates
641  7 int visX = al.getHiddenColumns().absoluteToVisibleColumn(x);
642  7 int visY = al.getHiddenSequences().findIndexWithoutHiddenSeqs(y);
643   
644    // if (vis_x,vis_y) is already visible don't do anything
645  7 if (startRes > visX || visX > endRes
646    || startSeq > visY && visY > endSeq)
647    {
648  6 int[] old = new int[] { startRes, startSeq };
649  6 int[] newresseq;
650  6 if (wrappedMode)
651    {
652  2 int newstartres = calcWrappedStartResidue(visX);
653  2 setStartRes(newstartres);
654  2 newresseq = new int[] { startRes, startSeq };
655    }
656    else
657    {
658    // set the viewport x location to contain vis_x
659  4 int newstartres = visX;
660  4 int width = getViewportWidth();
661  4 if (newstartres + width - 1 > getVisibleAlignmentWidth() - 1)
662    {
663  2 newstartres = getVisibleAlignmentWidth() - width;
664    }
665  4 updateStartEndRes(newstartres, newstartres + width - 1);
666   
667    // set the viewport y location to contain vis_y
668  4 int newstartseq = visY;
669  4 int height = getViewportHeight();
670  4 if (newstartseq + height - 1 > getVisibleAlignmentHeight() - 1)
671    {
672  1 newstartseq = getVisibleAlignmentHeight() - height;
673    }
674  4 updateStartEndSeq(newstartseq, newstartseq + height - 1);
675   
676  4 newresseq = new int[] { startRes, startSeq };
677    }
678  6 changedLocation = true;
679  6 changeSupport.firePropertyChange(MOVE_VIEWPORT, old, newresseq);
680    }
681  7 return changedLocation;
682    }
683   
684    /**
685    * Adjust sequence position for page up. Fires a property change event.
686    */
 
687  12 toggle public void pageUp()
688    {
689  12 if (wrappedMode)
690    {
691  8 setStartRes(Math.max(0, getStartRes() - getViewportWidth()));
692    }
693    else
694    {
695  4 setViewportStartAndHeight(startSeq - (endSeq - startSeq),
696    getViewportHeight());
697    }
698    }
699   
700    /**
701    * Adjust sequence position for page down. Fires a property change event.
702    */
 
703  17 toggle public void pageDown()
704    {
705  17 if (wrappedMode)
706    {
707    /*
708    * if height is more than width (i.e. not all sequences fit on screen),
709    * increase page down to height
710    */
711  10 int newStart = getStartRes()
712    + Math.max(getViewportHeight(), getViewportWidth());
713   
714    /*
715    * don't page down beyond end of alignment, or if not all
716    * sequences fit in the visible height
717    */
718  10 if (newStart < getVisibleAlignmentWidth())
719    {
720  7 setStartRes(newStart);
721    }
722    }
723    else
724    {
725  7 setViewportStartAndHeight(endSeq, getViewportHeight());
726    }
727    }
728   
 
729  85 toggle public void setWrappedMode(boolean wrapped)
730    {
731  85 wrappedMode = wrapped;
732    }
733   
 
734  116 toggle public boolean isWrappedMode()
735    {
736  116 return wrappedMode;
737    }
738   
739    /**
740    * Answers the vertical scroll position (0..) to set, given the visible column
741    * that is at top left.
742    *
743    * <pre>
744    * Example:
745    * viewport width 40 columns (0-39, 40-79, 80-119...)
746    * column 0 returns scroll position 0
747    * columns 1-40 return scroll position 1
748    * columns 41-80 return scroll position 2
749    * etc
750    * </pre>
751    *
752    * @param topLeftColumn
753    * (0..)
754    * @return
755    */
 
756  603 toggle public int getWrappedScrollPosition(final int topLeftColumn)
757    {
758  603 int w = getViewportWidth();
759   
760    /*
761    * visible whole widths
762    */
763  603 int scroll = topLeftColumn / w;
764   
765    /*
766    * add 1 for a part width if there is one
767    */
768  603 scroll += topLeftColumn % w > 0 ? 1 : 0;
769   
770  603 return scroll;
771    }
772   
773    /**
774    * Answers the maximum wrapped vertical scroll value, given the column
775    * position (0..) to show at top left of the visible region.
776    *
777    * @param topLeftColumn
778    * @return
779    */
 
780  318 toggle public int getWrappedMaxScroll(int topLeftColumn)
781    {
782  318 int scrollPosition = getWrappedScrollPosition(topLeftColumn);
783   
784    /*
785    * how many more widths could be drawn after this one?
786    */
787  318 int columnsRemaining = getVisibleAlignmentWidth() - topLeftColumn;
788  318 int width = getViewportWidth();
789  318 int widthsRemaining = columnsRemaining / width
790  318 + (columnsRemaining % width > 0 ? 1 : 0) - 1;
791  318 int maxScroll = scrollPosition + widthsRemaining;
792   
793  318 return maxScroll;
794    }
795    }