Clover icon

Coverage Report

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

File ViewportRanges.java

 

Coverage histogram

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

Code metrics

88
180
35
1
800
424
86
0.48
5.14
35
2.46

Classes

Class Line # Actions
ViewportRanges 32 180 86
0.986798798.7%
 

Contributing tests

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