c2bcf3f5b8539c5e96f9daf211f5d394f4638fa1
[jalview.git] / src / jalview / viewmodel / ViewportRanges.java
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  * Slightly less embryonic class which: Supplies and updates viewport properties
28  * relating to position such as: start and end residues and sequences; ideally
29  * will serve hidden columns/rows too. Intention also to support calculations
30  * for positioning, scrolling etc. such as finding the middle of the viewport,
31  * checking for scrolls off screen
32  */
33 public class ViewportRanges extends ViewportProperties
34 {
35   public static final String STARTRES = "startres";
36
37   public static final String ENDRES = "endres";
38
39   public static final String STARTSEQ = "startseq";
40
41   public static final String ENDSEQ = "endseq";
42
43   // start residue of viewport
44   private int startRes;
45
46   // end residue of viewport
47   private int endRes;
48
49   // start sequence of viewport
50   private int startSeq;
51
52   // end sequence of viewport
53   private int endSeq;
54
55   // alignment
56   private AlignmentI al;
57
58   /**
59    * Constructor
60    * 
61    * @param alignment
62    *          the viewport's alignment
63    */
64   public ViewportRanges(AlignmentI alignment)
65   {
66     // initial values of viewport settings
67     this.startRes = 0;
68     this.endRes = alignment.getWidth() - 1;
69     this.startSeq = 0;
70     this.endSeq = alignment.getHeight() - 1;
71     this.al = alignment;
72   }
73
74   /**
75    * Get alignment width in cols, including hidden cols
76    */
77   public int getAbsoluteAlignmentWidth()
78   {
79     return al.getWidth();
80   }
81
82   /**
83    * Get alignment height in rows, including hidden rows
84    */
85   public int getAbsoluteAlignmentHeight()
86   {
87     return al.getHeight() + al.getHiddenSequences().getSize();
88   }
89
90   /**
91    * Get alignment width in cols, excluding hidden cols
92    */
93   public int getVisibleAlignmentWidth()
94   {
95     return al.getWidth() - al.getHiddenColumns().getSize();
96   }
97
98   /**
99    * Get alignment height in rows, excluding hidden rows
100    */
101   public int getVisibleAlignmentHeight()
102   {
103     return al.getHeight();
104   }
105
106   /**
107    * Set first residue visible in the viewport, and retain the current width.
108    * Fires a property change event.
109    * 
110    * @param res
111    *          residue position
112    */
113   public void setStartRes(int res)
114   {
115     int width = getViewportWidth();
116     setStartEndRes(res, res + width - 1);
117   }
118
119   /**
120    * Set start and end residues at the same time. This method only fires one
121    * event for the two changes, and should be used in preference to separate
122    * calls to setStartRes and setEndRes.
123    * 
124    * @param start
125    *          the start residue
126    * @param end
127    *          the end residue
128    */
129   public void setStartEndRes(int start, int end)
130   {
131     int oldstartres = this.startRes;
132     if (start > getVisibleAlignmentWidth() - 1)
133     {
134       startRes = Math.max(getVisibleAlignmentWidth() - 1, 0);
135     }
136     else if (start < 0)
137     {
138       startRes = 0;
139     }
140     else
141     {
142       startRes = start;
143     }
144
145     int oldendres = this.endRes;
146     if (end < 0)
147     {
148       endRes = 0;
149     }
150     else if (end > getVisibleAlignmentWidth() - 1)
151     {
152       endRes = Math.max(getVisibleAlignmentWidth() - 1, 0);
153     }
154     else
155     {
156       endRes = end;
157     }
158
159     changeSupport.firePropertyChange(STARTRES, oldstartres, startRes);
160     if (oldstartres == startRes)
161     {
162       // event won't be fired if start positions are same
163       // fire an event for the end positions in case they changed
164       changeSupport.firePropertyChange(ENDRES, oldendres, endRes);
165     }
166   }
167
168   /**
169    * Set last residue visible in the viewport. Fires a property change event.
170    * 
171    * @param res
172    *          residue position
173    */
174   public void setEndRes(int res)
175   {
176     int startres = res;
177     int width = getViewportWidth();
178     if (startres + width - 1 > getVisibleAlignmentWidth() - 1)
179     {
180       startres = getVisibleAlignmentWidth() - width;
181     }
182     setStartEndRes(startres - width + 1, startres);
183   }
184
185   /**
186    * Set the first sequence visible in the viewport, maintaining the height. If
187    * the viewport would extend past the last sequence, sets the viewport so it
188    * sits at the bottom of the alignment. Fires a property change event.
189    * 
190    * @param seq
191    *          sequence position
192    */
193   public void setStartSeq(int seq)
194   {
195     int startseq = seq;
196     int height = getViewportHeight();
197     if (startseq + height - 1 > getVisibleAlignmentHeight() - 1)
198     {
199       startseq = getVisibleAlignmentHeight() - height;
200     }
201     setStartEndSeq(startseq, startseq + height - 1);
202   }
203
204   /**
205    * Set start and end sequences at the same time. The viewport height may
206    * change. This method only fires one event for the two changes, and should be
207    * used in preference to separate calls to setStartSeq and setEndSeq.
208    * 
209    * @param start
210    *          the start sequence
211    * @param end
212    *          the end sequence
213    */
214   public void setStartEndSeq(int start, int end)
215   {
216     int oldstartseq = this.startSeq;
217     if (start > getVisibleAlignmentHeight() - 1)
218     {
219       startSeq = Math.max(getVisibleAlignmentHeight() - 1, 0);
220     }
221     else if (start < 0)
222     {
223       startSeq = 0;
224     }
225     else
226     {
227       startSeq = start;
228     }
229
230     int oldendseq = this.endSeq;
231     if (end >= getVisibleAlignmentHeight())
232     {
233       endSeq = Math.max(getVisibleAlignmentHeight() - 1, 0);
234     }
235     else if (end < 0)
236     {
237       endSeq = 0;
238     }
239     else
240     {
241       endSeq = end;
242     }
243
244     changeSupport.firePropertyChange(STARTSEQ, oldstartseq, startSeq);
245     if (oldstartseq == startSeq)
246     {
247       // event won't be fired if start positions are the same
248       // fire in case the end positions changed
249       changeSupport.firePropertyChange(ENDSEQ, oldendseq, endSeq);
250     }
251   }
252
253   /**
254    * Set the last sequence visible in the viewport. Fires a property change
255    * event.
256    * 
257    * @param seq
258    *          sequence position
259    */
260   public void setEndSeq(int seq)
261   {
262     int height = getViewportHeight();
263     setStartEndSeq(seq - height + 1, seq);
264   }
265
266   /**
267    * Get start residue of viewport
268    */
269   public int getStartRes()
270   {
271     return startRes;
272   }
273
274   /**
275    * Get end residue of viewport
276    */
277   public int getEndRes()
278   {
279     return endRes;
280   }
281
282   /**
283    * Get start sequence of viewport
284    */
285   public int getStartSeq()
286   {
287     return startSeq;
288   }
289
290   /**
291    * Get end sequence of viewport
292    */
293   public int getEndSeq()
294   {
295     return endSeq;
296   }
297
298   /**
299    * Set viewport width in residues, without changing startRes. Use in
300    * preference to calculating endRes from the width, to avoid out by one
301    * errors! Fires a property change event.
302    * 
303    * @param w
304    *          width in residues
305    */
306   public void setViewportWidth(int w)
307   {
308     setStartEndRes(startRes, startRes + w - 1);
309   }
310
311   /**
312    * Set viewport height in residues, without changing startSeq. Use in
313    * preference to calculating endSeq from the height, to avoid out by one
314    * errors! Fires a property change event.
315    * 
316    * @param h
317    *          height in sequences
318    */
319   public void setViewportHeight(int h)
320   {
321     setStartEndSeq(startSeq, startSeq + h - 1);
322   }
323
324   /**
325    * Set viewport horizontal start position and width. Use in preference to
326    * calculating endRes from the width, to avoid out by one errors! Fires a
327    * property change event.
328    * 
329    * @param start
330    *          start residue
331    * @param w
332    *          width in residues
333    */
334   public void setViewportStartAndWidth(int start, int w)
335   {
336     int vpstart = start;
337     if (vpstart < 0)
338     {
339       vpstart = 0;
340     }
341     else if ((w <= getVisibleAlignmentWidth())
342             && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1))
343     // viewport width is less than the full alignment and we are running off the
344     // RHS edge
345     {
346       vpstart = getVisibleAlignmentWidth() - w;
347     }
348     setStartEndRes(vpstart, vpstart + w - 1);
349   }
350
351   /**
352    * Set viewport vertical start position and height. Use in preference to
353    * calculating endSeq from the height, to avoid out by one errors! Fires a
354    * property change event.
355    * 
356    * @param start
357    *          start sequence
358    * @param h
359    *          height in sequences
360    */
361   public void setViewportStartAndHeight(int start, int h)
362   {
363     int vpstart = start;
364     if (vpstart < 0)
365     {
366       vpstart = 0;
367     }
368     else if ((h <= getVisibleAlignmentHeight())
369             && (vpstart + h - 1 > getVisibleAlignmentHeight() - 1))
370     // viewport height is less than the full alignment and we are running off
371     // the bottom
372     {
373       vpstart = getVisibleAlignmentHeight() - h;
374     }
375     setStartEndSeq(vpstart, vpstart + h - 1);
376   }
377
378   /**
379    * Get width of viewport in residues
380    * 
381    * @return width of viewport
382    */
383   public int getViewportWidth()
384   {
385     return (endRes - startRes + 1);
386   }
387
388   /**
389    * Get height of viewport in residues
390    * 
391    * @return height of viewport
392    */
393   public int getViewportHeight()
394   {
395     return (endSeq - startSeq + 1);
396   }
397
398   /**
399    * Scroll the viewport range vertically. Fires a property change event.
400    * 
401    * @param up
402    *          true if scrolling up, false if down
403    * 
404    * @return true if the scroll is valid
405    */
406   public boolean scrollUp(boolean up)
407   {
408     if (up)
409     {
410       if (startSeq < 1)
411       {
412         return false;
413       }
414
415       setStartSeq(startSeq - 1);
416     }
417     else
418     {
419       if (endSeq >= getVisibleAlignmentHeight() - 1)
420       {
421         return false;
422       }
423
424       setStartSeq(startSeq + 1);
425     }
426     return true;
427   }
428
429   /**
430    * Scroll the viewport range horizontally. Fires a property change event.
431    * 
432    * @param right
433    *          true if scrolling right, false if left
434    * 
435    * @return true if the scroll is valid
436    */
437   public boolean scrollRight(boolean right)
438   {
439     if (!right)
440     {
441       if (startRes < 1)
442       {
443         return false;
444       }
445
446       setStartRes(startRes - 1);
447     }
448     else
449     {
450       if (endRes >= getVisibleAlignmentWidth() - 1)
451       {
452         return false;
453       }
454
455       setStartRes(startRes + 1);
456     }
457
458     return true;
459   }
460
461   /**
462    * Scroll a wrapped alignment so that the specified residue is visible. Fires
463    * a property change event.
464    * 
465    * @param res
466    *          residue position to scroll to
467    */
468   public void scrollToWrappedVisible(int res)
469   {
470     // get the start residue of the wrapped row which res is in
471     // and set that as our start residue
472     int width = getViewportWidth();
473     setStartRes((res / width) * width);
474   }
475
476   /**
477    * Scroll so that (x,y) is visible. Fires a property change event.
478    * 
479    * @param x
480    *          x position in alignment
481    * @param y
482    *          y position in alignment
483    */
484   public void scrollToVisible(int x, int y)
485   {
486     while (y < startSeq)
487     {
488       scrollUp(true);
489     }
490     while (y > endSeq)
491     {
492       scrollUp(false);
493     }
494
495     HiddenColumns hidden = al.getHiddenColumns();
496     while (x < hidden.adjustForHiddenColumns(startRes))
497     {
498       if (!scrollRight(false))
499       {
500         break;
501       }
502     }
503     while (x > hidden.adjustForHiddenColumns(endRes))
504     {
505       if (!scrollRight(true))
506       {
507         break;
508       }
509     }
510   }
511   
512   /**
513    * Adjust sequence position for page up. Fires a property change event.
514    */
515   public void pageUp()
516   {
517     setViewportStartAndHeight(2 * startSeq - endSeq, getViewportHeight());
518   }
519   
520   /**
521    * Adjust sequence position for page down. Fires a property change event.
522    */
523   public void pageDown()
524   {
525     setViewportStartAndHeight(endSeq, getViewportHeight());
526   }
527 }