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