JAL-2491 Unit test updates
[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 width = getViewportWidth();
149     setStartEndRes(res - width + 1, res);
150   }
151
152   /**
153    * Set the first sequence visible in the viewport. Fires a property change
154    * event.
155    * 
156    * @param seq
157    *          sequence position
158    */
159   public void setStartSeq(int seq)
160   {
161     int height = getViewportHeight();
162     setStartEndSeq(seq, seq + height - 1);
163   }
164
165   /**
166    * Set start and end sequences at the same time. This method only fires one
167    * event for the two changes, and should be used in preference to separate
168    * calls to setStartSeq and setEndSeq.
169    * 
170    * @param start
171    *          the start sequence
172    * @param end
173    *          the end sequence
174    */
175   public void setStartEndSeq(int start, int end)
176   {
177     int oldstartseq = this.startSeq;
178     if (start > al.getHeight() - 1)
179     {
180       startSeq = al.getHeight() - 1;
181     }
182     else if (start < 0)
183     {
184       startSeq = 0;
185     }
186     else
187     {
188       startSeq = start;
189     }
190
191     int oldendseq = this.endSeq;
192     if (end >= al.getHeight())
193     {
194       endSeq = al.getHeight() - 1;
195     }
196     else if (end < 0)
197     {
198       endSeq = 0;
199     }
200     else
201     {
202       endSeq = end;
203     }
204
205     changeSupport.firePropertyChange("startseq", oldstartseq, startSeq);
206     if (oldstartseq == startSeq)
207     {
208       // event won't be fired if start positions are the same
209       // fire in case the end positions changed
210       changeSupport.firePropertyChange("endseq", oldendseq, endSeq);
211     }
212   }
213
214   /**
215    * Set the last sequence visible in the viewport. Fires a property change
216    * event.
217    * 
218    * @param seq
219    *          sequence position
220    */
221   public void setEndSeq(int seq)
222   {
223     int height = getViewportHeight();
224     setStartEndSeq(seq - height + 1, seq);
225   }
226
227   /**
228    * Get start residue of viewport
229    */
230   public int getStartRes()
231   {
232     return startRes;
233   }
234
235   /**
236    * Get end residue of viewport
237    */
238   public int getEndRes()
239   {
240     return endRes;
241   }
242
243   /**
244    * Get start sequence of viewport
245    */
246   public int getStartSeq()
247   {
248     return startSeq;
249   }
250
251   /**
252    * Get end sequence of viewport
253    */
254   public int getEndSeq()
255   {
256     return endSeq;
257   }
258
259   /**
260    * Set viewport width in residues, without changing startRes. Use in
261    * preference to calculating endRes from the width, to avoid out by one
262    * errors! Fires a property change event.
263    * 
264    * @param w
265    *          width in residues
266    */
267   public void setViewportWidth(int w)
268   {
269     setStartEndRes(startRes, startRes + w - 1);
270   }
271
272   /**
273    * Set viewport height in residues, without changing startSeq. Use in
274    * preference to calculating endSeq from the height, to avoid out by one
275    * errors! Fires a property change event.
276    * 
277    * @param h
278    *          height in sequences
279    */
280   public void setViewportHeight(int h)
281   {
282     setStartEndSeq(startSeq, startSeq + h - 1);
283   }
284
285   /**
286    * Set viewport horizontal start position and width. Use in preference to
287    * calculating endRes from the width, to avoid out by one errors! Fires a
288    * property change event.
289    * 
290    * @param start
291    *          start residue
292    * @param w
293    *          width in residues
294    */
295   public void setViewportStartAndWidth(int start, int w)
296   {
297     int vpstart = start;
298     if (vpstart < 0)
299     {
300       vpstart = 0;
301     }
302     else if (vpstart + w - 1 > al.getWidth() - 1)
303     {
304       vpstart = al.getWidth() - 1;
305     }
306     setStartEndRes(vpstart, vpstart + w - 1);
307   }
308
309   /**
310    * Set viewport vertical start position and height. Use in preference to
311    * calculating endSeq from the height, to avoid out by one errors! Fires a
312    * property change event.
313    * 
314    * @param start
315    *          start sequence
316    * @param h
317    *          height in sequences
318    */
319   public void setViewportStartAndHeight(int start, int h)
320   {
321     int vpstart = start;
322     if (vpstart < 0)
323     {
324       vpstart = 0;
325     }
326     else if (vpstart + h - 1 > al.getHeight() - 1)
327     {
328       vpstart = al.getHeight() - h;
329     }
330     setStartEndSeq(vpstart, vpstart + h - 1);
331   }
332
333   /**
334    * Get width of viewport in residues
335    * 
336    * @return width of viewport
337    */
338   public int getViewportWidth()
339   {
340     return (endRes - startRes + 1);
341   }
342
343   /**
344    * Get height of viewport in residues
345    * 
346    * @return height of viewport
347    */
348   public int getViewportHeight()
349   {
350     return (endSeq - startSeq + 1);
351   }
352
353   /**
354    * Scroll the viewport range vertically. Fires a property change event.
355    * 
356    * @param up
357    *          true if scrolling up, false if down
358    * 
359    * @return true if the scroll is valid
360    */
361   public boolean scrollUp(boolean up)
362   {
363     if (up)
364     {
365       if (startSeq < 1)
366       {
367         return false;
368       }
369
370       setStartSeq(startSeq - 1);
371     }
372     else
373     {
374       if (endSeq >= al.getHeight() - 1)
375       {
376         return false;
377       }
378
379       setStartSeq(startSeq + 1);
380     }
381     return true;
382   }
383
384   /**
385    * Scroll the viewport range horizontally. Fires a property change event.
386    * 
387    * @param right
388    *          true if scrolling right, false if left
389    * 
390    * @return true if the scroll is valid
391    */
392   public boolean scrollRight(boolean right)
393   {
394     if (!right)
395     {
396       if (startRes < 1)
397       {
398         return false;
399       }
400
401       setStartRes(startRes - 1);
402     }
403     else
404     {
405       if (endRes > al.getWidth() - 1)
406       {
407         return false;
408       }
409
410       setStartRes(startRes + 1);
411     }
412
413     return true;
414   }
415
416   /**
417    * Scroll a wrapped alignment so that the specified residue is visible. Fires
418    * a property change event.
419    * 
420    * @param res
421    *          residue position to scroll to
422    */
423   public void scrollToWrappedVisible(int res)
424   {
425     // get the start residue of the wrapped row which res is in
426     // and set that as our start residue
427     int width = getViewportWidth();
428     setStartRes((res / width) * width);
429   }
430
431   /**
432    * Scroll so that (x,y) is visible. Fires a property change event.
433    * 
434    * @param x
435    *          x position in alignment
436    * @param y
437    *          y position in alignment
438    * @param av
439    *          viewport to be visible in. Here until hidden columns JAL-2388
440    *          merged, then use alignment to get hidden cols
441    */
442   public void scrollToVisible(int x, int y, AlignViewportI av)
443   {
444     while (y < startSeq)
445     {
446       scrollUp(true);
447     }
448     while (y > endSeq)
449     {
450       scrollUp(false);
451     }
452
453     while (x < av.getColumnSelection().adjustForHiddenColumns(startRes))
454     {
455       if (!scrollRight(false))
456       {
457         break;
458       }
459     }
460     while (x > av.getColumnSelection().adjustForHiddenColumns(endRes))
461     {
462       if (!scrollRight(true))
463       {
464         break;
465       }
466     }
467   }
468   
469   /**
470    * Adjust sequence position for page up. Fires a property change event.
471    */
472   public void pageUp()
473   {
474     setViewportStartAndHeight(2 * startSeq - endSeq, getViewportHeight());
475   }
476   
477   /**
478    * Adjust sequence position for page down. Fires a property change event.
479    */
480   public void pageDown()
481   {
482     setViewportStartAndHeight(endSeq, getViewportHeight());
483   }
484 }