JAL-2491 Cursor scrolling
[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    * 
85    * @param res
86    *          residue position
87    */
88   public void setStartRes(int res)
89   {
90     int width = getViewportWidth();
91     setStartEndRes(res, res + width - 1);
92   }
93
94   /**
95    * Set start and end residues at the same time. This method only fires one
96    * event for the two changes, and should be used in preference to separate
97    * calls to setStartRes and setEndRes.
98    * 
99    * @param start
100    *          the start residue
101    * @param end
102    *          the end residue
103    */
104   public void setStartEndRes(int start, int end)
105   {
106     int oldres = this.startRes;
107     if (start > al.getWidth() - 1)
108     {
109       startRes = al.getWidth() - 1;
110     }
111     else if (start < 0)
112     {
113       startRes = 0;
114     }
115     else
116     {
117       startRes = start;
118     }
119
120     if (end < 0)
121     {
122       endRes = 0;
123     }
124     else
125     {
126       endRes = end;
127     }
128
129     changeSupport.firePropertyChange("startres", oldres, start);
130   }
131
132   /**
133    * Set last residue visible in the viewport
134    * 
135    * @param res
136    *          residue position
137    */
138   public void setEndRes(int res)
139   {
140     int width = getViewportWidth();
141     setStartEndRes(res - width + 1, res);
142   }
143
144   /**
145    * Set the first sequence visible in the viewport
146    * 
147    * @param seq
148    *          sequence position
149    */
150   public void setStartSeq(int seq)
151   {
152     int height = getViewportHeight();
153     setStartEndSeq(seq, seq + height - 1);
154   }
155
156   /**
157    * Set start and end sequences at the same time. This method only fires one
158    * event for the two changes, and should be used in preference to separate
159    * calls to setStartSeq and setEndSeq.
160    * 
161    * @param start
162    *          the start sequence
163    * @param end
164    *          the end sequence
165    */
166   public void setStartEndSeq(int start, int end)
167   {
168     int oldseq = this.startSeq;
169     if (start > al.getHeight() - 1)
170     {
171       startSeq = al.getHeight() - 1;
172     }
173     else if (start < 0)
174     {
175       startSeq = 0;
176     }
177     else
178     {
179       startSeq = start;
180     }
181
182     if (end >= al.getHeight())
183     {
184       endSeq = al.getHeight() - 1;
185     }
186     else if (end < 0)
187     {
188       endSeq = 0;
189     }
190     else
191     {
192       endSeq = end;
193     }
194
195     changeSupport.firePropertyChange("startseq", oldseq, start);
196   }
197
198   /**
199    * Set the last sequence visible in the viewport
200    * 
201    * @param seq
202    *          sequence position
203    */
204   public void setEndSeq(int seq)
205   {
206     int height = getViewportHeight();
207     setStartEndSeq(seq - height + 1, seq);
208   }
209
210   /**
211    * Get start residue of viewport
212    */
213   public int getStartRes()
214   {
215     return startRes;
216   }
217
218   /**
219    * Get end residue of viewport
220    */
221   public int getEndRes()
222   {
223     return endRes;
224   }
225
226   /**
227    * Get start sequence of viewport
228    */
229   public int getStartSeq()
230   {
231     return startSeq;
232   }
233
234   /**
235    * Get end sequence of viewport
236    */
237   public int getEndSeq()
238   {
239     return endSeq;
240   }
241
242   /**
243    * Set viewport width in residues, without changing startRes. Use in
244    * preference to calculating endRes from the width, to avoid out by one
245    * errors!
246    * 
247    * @param w
248    *          width in residues
249    */
250   public void setViewportWidth(int w)
251   {
252     setStartEndRes(startRes, startRes + w - 1);
253   }
254
255   /**
256    * Set viewport height in residues, without changing startSeq. Use in
257    * preference to calculating endSeq from the height, to avoid out by one
258    * errors!
259    * 
260    * @param h
261    *          height in sequences
262    */
263   public void setViewportHeight(int h)
264   {
265     setStartEndSeq(startSeq, startSeq + h - 1);
266   }
267
268   /**
269    * Set viewport horizontal start position and width. Use in preference to
270    * calculating endRes from the width, to avoid out by one errors!
271    * 
272    * @param start
273    *          start residue
274    * @param w
275    *          width in residues
276    */
277   public void setViewportStartAndWidth(int start, int w)
278   {
279     setStartEndRes(start, start + w - 1);
280   }
281
282   /**
283    * Set viewport vertical start position and height. Use in preference to
284    * calculating endSeq from the height, to avoid out by one errors!
285    * 
286    * @param start
287    *          start sequence
288    * @param h
289    *          height in sequences
290    */
291   public void setViewportStartAndHeight(int start, int h)
292   {
293     setStartEndSeq(start, start + h - 1);
294   }
295
296   /**
297    * Get width of viewport in residues
298    * 
299    * @return width of viewport
300    */
301   public int getViewportWidth()
302   {
303     return (endRes - startRes + 1);
304   }
305
306   /**
307    * Get height of viewport in residues
308    * 
309    * @return height of viewport
310    */
311   public int getViewportHeight()
312   {
313     return (endSeq - startSeq + 1);
314   }
315
316   /**
317    * Scroll the viewport range vertically
318    * 
319    * @param up
320    *          true if scrolling up, false if down
321    * 
322    * @return true if the scroll is valid
323    */
324   public boolean scrollUp(boolean up)
325   {
326     if (up)
327     {
328       if (startSeq < 1)
329       {
330         return false;
331       }
332
333       setStartSeq(startSeq - 1);
334     }
335     else
336     {
337       if (endSeq >= al.getHeight() - 1)
338       {
339         return false;
340       }
341
342       setStartSeq(startSeq + 1);
343     }
344     return true;
345   }
346
347   /**
348    * Scroll the viewport range horizontally
349    * 
350    * @param right
351    *          true if scrolling right, false if left
352    * 
353    * @return true if the scroll is valid
354    */
355   public boolean scrollRight(boolean right)
356   {
357     if (!right)
358     {
359       if (startRes < 1)
360       {
361         return false;
362       }
363
364       setStartRes(startRes - 1);
365     }
366     else
367     {
368       if (endRes > al.getWidth() - 1)
369       {
370         return false;
371       }
372
373       setStartRes(startRes + 1);
374     }
375
376     return true;
377   }
378
379   /**
380    * Scroll a wrapped alignment so that the specified residue is visible
381    * 
382    * @param res
383    *          residue position to scroll to
384    */
385   public void scrollToWrappedVisible(int res)
386   {
387     // get the start residue of the wrapped row which res is in
388     // and set that as our start residue
389     int width = getViewportWidth();
390     setStartRes((res / width) * width);
391   }
392
393   // pass av here until hidden columns JAL-2388 merged, then use alignment
394   // instead
395   public void scrollToVisible(int x, int y, AlignViewportI av)
396   {
397     while (y < startSeq)
398     {
399       scrollUp(true);
400     }
401     while (y + 1 > endSeq)
402     {
403       scrollUp(false);
404     }
405
406     while (x < av.getColumnSelection().adjustForHiddenColumns(startRes))
407     {
408       if (!scrollRight(false))
409       {
410         break;
411       }
412     }
413     while (x > av.getColumnSelection().adjustForHiddenColumns(endRes))
414     {
415       if (!scrollRight(true))
416       {
417         break;
418       }
419     }
420   }
421
422 }