2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.viewmodel;
23 import jalview.datamodel.AlignmentI;
24 import jalview.datamodel.HiddenColumns;
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
32 public class ViewportRanges extends ViewportProperties
34 public static final String STARTRES = "startres";
36 public static final String ENDRES = "endres";
38 public static final String STARTSEQ = "startseq";
40 public static final String ENDSEQ = "endseq";
42 public static final String STARTRESANDSEQ = "startresandseq";
44 private boolean wrappedMode = false;
46 // start residue of viewport
49 // end residue of viewport
52 // start sequence of viewport
55 // end sequence of viewport
59 private AlignmentI al;
65 * the viewport's alignment
67 public ViewportRanges(AlignmentI alignment)
69 // initial values of viewport settings
71 this.endRes = alignment.getWidth() - 1;
73 this.endSeq = alignment.getHeight() - 1;
78 * Get alignment width in cols, including hidden cols
80 public int getAbsoluteAlignmentWidth()
86 * Get alignment height in rows, including hidden rows
88 public int getAbsoluteAlignmentHeight()
90 return al.getHeight() + al.getHiddenSequences().getSize();
94 * Get alignment width in cols, excluding hidden cols
96 public int getVisibleAlignmentWidth()
98 return al.getWidth() - al.getHiddenColumns().getSize();
102 * Get alignment height in rows, excluding hidden rows
104 public int getVisibleAlignmentHeight()
106 return al.getHeight();
110 * Set first residue visible in the viewport, and retain the current width.
111 * Fires a property change event.
116 public void setStartRes(int res)
118 int width = getViewportWidth();
119 setStartEndRes(res, res + width - 1);
123 * Set start and end residues at the same time. This method only fires one
124 * event for the two changes, and should be used in preference to separate
125 * calls to setStartRes and setEndRes.
132 public void setStartEndRes(int start, int end)
134 int[] oldvalues = updateStartEndRes(start, end);
135 int oldstartres = oldvalues[0];
136 int oldendres = oldvalues[1];
138 changeSupport.firePropertyChange(STARTRES, oldstartres, startRes);
139 if (oldstartres == startRes)
141 // event won't be fired if start positions are same
142 // fire an event for the end positions in case they changed
143 changeSupport.firePropertyChange(ENDRES, oldendres, endRes);
148 * Update start and end residue values, adjusting for width constraints if
155 * @return array containing old start and end residue values
157 private int[] updateStartEndRes(int start, int end)
159 int oldstartres = this.startRes;
162 * if not wrapped, don't leave white space at the right margin
164 int lastColumn = getVisibleAlignmentWidth() - 1;
165 if (!wrappedMode && (start > lastColumn))
167 startRes = Math.max(lastColumn, 0);
178 int oldendres = this.endRes;
183 else if (!wrappedMode && (end > lastColumn))
185 endRes = Math.max(lastColumn, 0);
191 return new int[] { oldstartres, oldendres };
195 * Set the first sequence visible in the viewport, maintaining the height. If
196 * the viewport would extend past the last sequence, sets the viewport so it
197 * sits at the bottom of the alignment. Fires a property change event.
202 public void setStartSeq(int seq)
205 int height = getViewportHeight();
206 if (startseq + height - 1 > getVisibleAlignmentHeight() - 1)
208 startseq = getVisibleAlignmentHeight() - height;
210 setStartEndSeq(startseq, startseq + height - 1);
214 * Set start and end sequences at the same time. The viewport height may
215 * change. This method only fires one event for the two changes, and should be
216 * used in preference to separate calls to setStartSeq and setEndSeq.
223 public void setStartEndSeq(int start, int end)
225 int[] oldvalues = updateStartEndSeq(start, end);
226 int oldstartseq = oldvalues[0];
227 int oldendseq = oldvalues[1];
229 changeSupport.firePropertyChange(STARTSEQ, oldstartseq, startSeq);
230 if (oldstartseq == startSeq)
232 // event won't be fired if start positions are the same
233 // fire in case the end positions changed
234 changeSupport.firePropertyChange(ENDSEQ, oldendseq, endSeq);
239 * Update start and end sequence values, adjusting for height constraints if
246 * @return array containing old start and end sequence values
248 private int[] updateStartEndSeq(int start, int end)
250 int oldstartseq = this.startSeq;
251 int visibleHeight = getVisibleAlignmentHeight();
252 if (start > visibleHeight - 1)
254 startSeq = Math.max(visibleHeight - 1, 0);
265 int oldendseq = this.endSeq;
266 if (end >= visibleHeight)
268 endSeq = Math.max(visibleHeight - 1, 0);
278 return new int[] { oldstartseq, oldendseq };
282 * Set the last sequence visible in the viewport. Fires a property change
288 public void setEndSeq(int seq)
290 int height = getViewportHeight();
291 setStartEndSeq(seq - height + 1, seq);
295 * Set start residue and start sequence together (fires single event). The
296 * event supplies a pair of old values and a pair of new values: [old start
297 * residue, old start sequence] and [new start residue, new start sequence]
304 public void setStartResAndSeq(int res, int seq)
306 int width = getViewportWidth();
307 int[] oldresvalues = updateStartEndRes(res, res + width - 1);
310 int height = getViewportHeight();
311 if (startseq + height - 1 > getVisibleAlignmentHeight() - 1)
313 startseq = getVisibleAlignmentHeight() - height;
315 int[] oldseqvalues = updateStartEndSeq(startseq, startseq + height - 1);
317 int[] old = new int[] { oldresvalues[0], oldseqvalues[0] };
318 int[] newresseq = new int[] { startRes, startSeq };
319 changeSupport.firePropertyChange(STARTRESANDSEQ, old, newresseq);
323 * Get start residue of viewport
325 public int getStartRes()
331 * Get end residue of viewport
333 public int getEndRes()
339 * Get start sequence of viewport
341 public int getStartSeq()
347 * Get end sequence of viewport
349 public int getEndSeq()
355 * Set viewport width in residues, without changing startRes. Use in
356 * preference to calculating endRes from the width, to avoid out by one
357 * errors! Fires a property change event.
362 public void setViewportWidth(int w)
364 setStartEndRes(startRes, startRes + w - 1);
368 * Set viewport height in residues, without changing startSeq. Use in
369 * preference to calculating endSeq from the height, to avoid out by one
370 * errors! Fires a property change event.
373 * height in sequences
375 public void setViewportHeight(int h)
377 setStartEndSeq(startSeq, startSeq + h - 1);
381 * Set viewport horizontal start position and width. Use in preference to
382 * calculating endRes from the width, to avoid out by one errors! Fires a
383 * property change event.
390 public void setViewportStartAndWidth(int start, int w)
399 * if not wrapped, don't leave white space at the right margin
403 if ((w <= getVisibleAlignmentWidth())
404 && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1))
406 vpstart = getVisibleAlignmentWidth() - w;
410 setStartEndRes(vpstart, vpstart + w - 1);
414 * Set viewport vertical start position and height. Use in preference to
415 * calculating endSeq from the height, to avoid out by one errors! Fires a
416 * property change event.
421 * height in sequences
423 public void setViewportStartAndHeight(int start, int h)
430 else if ((h <= getVisibleAlignmentHeight())
431 && (vpstart + h - 1 > getVisibleAlignmentHeight() - 1))
432 // viewport height is less than the full alignment and we are running off
435 vpstart = getVisibleAlignmentHeight() - h;
437 setStartEndSeq(vpstart, vpstart + h - 1);
441 * Get width of viewport in residues
443 * @return width of viewport
445 public int getViewportWidth()
447 return (endRes - startRes + 1);
451 * Get height of viewport in residues
453 * @return height of viewport
455 public int getViewportHeight()
457 return (endSeq - startSeq + 1);
461 * Scroll the viewport range vertically. Fires a property change event.
464 * true if scrolling up, false if down
466 * @return true if the scroll is valid
468 public boolean scrollUp(boolean up)
471 * if in unwrapped mode, scroll up or down one sequence row;
472 * if in wrapped mode, scroll by one visible width of columns
486 setStartSeq(startSeq - 1);
497 if (endSeq >= getVisibleAlignmentHeight() - 1)
501 setStartSeq(startSeq + 1);
508 * Scroll the viewport range horizontally. Fires a property change event.
511 * true if scrolling right, false if left
513 * @return true if the scroll is valid
515 public boolean scrollRight(boolean right)
524 setStartRes(startRes - 1);
528 if (endRes >= getVisibleAlignmentWidth() - 1)
533 setStartRes(startRes + 1);
540 * Scroll a wrapped alignment so that the specified residue is in the first
541 * repeat of the wrapped view. Fires a property change event. Answers true if
542 * the startRes changed, else false.
545 * residue position to scroll to
548 public boolean scrollToWrappedVisible(int res)
550 int oldStartRes = startRes;
551 int width = getViewportWidth();
553 if (res >= oldStartRes && res < oldStartRes + width)
558 boolean up = res < oldStartRes;
559 int widthsToScroll = Math.abs((res - oldStartRes) / width);
565 int residuesToScroll = width * widthsToScroll;
566 int newStartRes = up ? oldStartRes - residuesToScroll : oldStartRes
573 setStartRes(newStartRes);
579 * Scroll so that (x,y) is visible. Fires a property change event.
582 * x position in alignment
584 * y position in alignment
586 public void scrollToVisible(int x, int y)
597 HiddenColumns hidden = al.getHiddenColumns();
598 while (x < hidden.adjustForHiddenColumns(startRes))
600 if (!scrollRight(false))
605 while (x > hidden.adjustForHiddenColumns(endRes))
607 if (!scrollRight(true))
615 * Adjust sequence position for page up. Fires a property change event.
621 setStartRes(Math.max(0, getStartRes() - getViewportWidth()));
625 setViewportStartAndHeight(startSeq - (endSeq - startSeq),
626 getViewportHeight());
631 * Adjust sequence position for page down. Fires a property change event.
633 public void pageDown()
638 * if height is more than width (i.e. not all sequences fit on screen),
639 * increase page down to height
641 int newStart = getStartRes()
642 + Math.max(getViewportHeight(), getViewportWidth());
645 * don't page down beyond end of alignment, or if not all
646 * sequences fit in the visible height
648 if (newStart < getVisibleAlignmentWidth())
650 setStartRes(newStart);
655 setViewportStartAndHeight(endSeq, getViewportHeight());
659 public void setWrappedMode(boolean wrapped)
661 wrappedMode = wrapped;
664 public boolean isWrappedMode()
670 * Answers the vertical scroll position (0..) to set, given the visible column
671 * that is at top left.
675 * viewport width 40 columns (0-39, 40-79, 80-119...)
676 * column 0 returns scroll position 0
677 * columns 1-40 return scroll position 1
678 * columns 41-80 return scroll position 2
682 * @param topLeftColumn
686 public int getWrappedScrollPosition(final int topLeftColumn)
688 int w = getViewportWidth();
691 * visible whole widths
693 int scroll = topLeftColumn / w;
696 * add 1 for a part width if there is one
698 scroll += topLeftColumn % w > 0 ? 1 : 0;
704 * Answers the maximum wrapped vertical scroll value, given the column
705 * position (0..) to show at top left of the visible region.
707 * @param topLeftColumn
710 public int getWrappedMaxScroll(int topLeftColumn)
712 int scrollPosition = getWrappedScrollPosition(topLeftColumn);
715 * how many more widths could be drawn after this one?
717 int columnsRemaining = getVisibleAlignmentWidth() - topLeftColumn;
718 int width = getViewportWidth();
719 int widthsRemaining = columnsRemaining / width
720 + (columnsRemaining % width > 0 ? 1 : 0) - 1;
721 int maxScroll = scrollPosition + widthsRemaining;