Clover icon

Coverage Report

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