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