JAL-2388 Corrected overview panel behaviour, updated tests
[jalview.git] / src / jalview / viewmodel / OverviewDimensions.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.ColumnSelection;
25 import jalview.datamodel.HiddenSequences;
26
27 import java.awt.Graphics;
28
29 public class OverviewDimensions
30 {
31   // Default width and height values
32   private static final int DEFAULT_GRAPH_HEIGHT = 20;
33
34   private static final int MAX_WIDTH = 400;
35
36   private static final int MIN_WIDTH = 120;
37
38   private static final int MIN_SEQ_HEIGHT = 40;
39
40   private static final int MAX_SEQ_HEIGHT = 300;
41
42   private AlignViewportI av;
43
44   private ViewportPositionProps posProps;
45
46   // width of the overview panel
47   private int width;
48
49   // height of sequences part of the overview panel
50   private int sequencesHeight;
51
52   // height of the graphs part of the overview panel
53   private int graphHeight = DEFAULT_GRAPH_HEIGHT;
54
55   // dimensions of box outlining current extent of view in alignment panel
56   // location of left side of box
57   private int boxX = -1;
58
59   // location of bottom of box
60   private int boxY = -1;
61
62   // width of box
63   private int boxWidth = -1;
64
65   // height of box
66   private int boxHeight = -1;
67
68   private int scrollCol = -1;
69
70   private int scrollRow = -1;
71
72   public OverviewDimensions(AlignViewportI avi, boolean showAnnotationPanel)
73   {
74     this.av = avi;
75     this.posProps = av.getPosProps();
76
77     // scale the initial size of overviewpanel to shape of alignment
78     float initialScale = (float) posProps.getAlignmentWidthInCols()
79             / (float) posProps.getAlignmentHeightInRows();
80
81     if (!showAnnotationPanel)
82     {
83       graphHeight = 0;
84     }
85
86     if (posProps.getAlignmentWidthInCols() > posProps
87             .getAlignmentHeightInRows())
88     {
89       // wider
90       width = MAX_WIDTH;
91       sequencesHeight = (int) (MAX_WIDTH / initialScale);
92       if (sequencesHeight < MIN_SEQ_HEIGHT)
93       {
94         sequencesHeight = MIN_SEQ_HEIGHT;
95       }
96     }
97     else
98     {
99       // taller
100       width = (int) (MAX_WIDTH * initialScale);
101       sequencesHeight = MAX_SEQ_HEIGHT;
102
103       if (width < MIN_WIDTH)
104       {
105         width = MIN_WIDTH;
106       }
107     }
108   }
109
110   /**
111    * Check box dimensions and scroll positions and correct if necessary
112    */
113   public void updateViewportFromMouse(int x, int y)
114   {
115     int alwidth = av.getAlignment().getWidth();
116     int alheight = av.getAlignment().getAbsoluteHeight();
117
118     HiddenSequences hiddenSeqs = av.getAlignment().getHiddenSequences();
119     ColumnSelection hiddenCols = av.getColumnSelection();
120
121     if (x < 0)
122     {
123       x = 0;
124     }
125     else if (x >= alwidth)
126     {
127       x = alwidth - 1;
128     }
129
130     if (y < 0)
131     {
132       y = 0;
133     }
134     else if (y >= alheight)
135     {
136       y = alheight - 1;
137     }
138
139     //
140     // Convert x value to residue position
141     //
142
143     // need to determine where scrollCol should be, given x
144     // to do this also need to know width of viewport, and some hidden column
145     // correction
146
147     // convert x to residues - this is an absolute position
148     int xAsRes = Math.round((float) x * alwidth / width);
149
150     // get viewport width in residues
151     int vpwidth = av.getPosProps().getEndRes()
152             - av.getPosProps().getStartRes() + 1;
153
154     // get where x should be when accounting for hidden cols
155     // if x is in a hidden col region, shift to left - but we still need
156     // absolute position
157     // so convert back after getting visible region position
158     xAsRes = hiddenCols.adjustForHiddenColumns(hiddenCols
159             .findColumnPosition(xAsRes));
160
161     // get where end res should be by adding the viewport width on
162     int endRes = xAsRes + vpwidth;
163
164     // check in case we went off the edge of the alignment
165     if (endRes > alwidth)
166     {
167       // went past the end of the alignment, adjust backwards
168       endRes = alwidth;
169       // recalc xAsRes backwards from endRes
170       xAsRes = endRes - vpwidth;
171     }
172
173     //
174     // Convert y value to sequence position
175     //
176
177     // convert y to residues
178     int yAsSeq = Math.round((float) y * alheight / sequencesHeight);
179
180     // get viewport height in sequences
181     int vpheight = av.getPosProps().getEndSeq()
182             - av.getPosProps().getStartSeq();
183
184     // get where y should be when accounting for hidden rows
185     // if y is in a hidden row region, shift up - but we still need absolute
186     // position,
187     // so convert back after getting visible region position
188     yAsSeq = hiddenSeqs.adjustForHiddenSeqs(hiddenSeqs
189             .findIndexWithoutHiddenSeqs(yAsSeq));
190
191     // get where end seq should be by adding the viewport height on
192     int endSeq = yAsSeq + vpheight;
193
194     // check in case we went off the edge of the alignment
195     if (endSeq > alheight)
196     {
197       // went past the end of the alignment, adjust backwards
198       endSeq = alheight;
199       // recalc yAsSeq backwards from endSeq
200       yAsSeq = endSeq - vpheight;
201     }
202
203     // convert absolute positions back to visible alignment positions for
204     // viewport scrolling
205     scrollCol = hiddenCols.findColumnPosition(xAsRes);
206     scrollRow = hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq);
207   }
208
209   /**
210    * Update the overview panel box when the associated alignment panel is
211    * changed
212    * 
213    */
214   public void setBoxPosition()
215   {
216     int alwidth = av.getAlignment().getWidth();
217     int alheight = av.getAlignment().getAbsoluteHeight();
218
219     int startRes = av.getPosProps().getAbsoluteStartRes();
220     int endRes = av.getPosProps().getAbsoluteEndRes();
221
222     int startSeq = av.getPosProps().getAbsoluteStartSeq();
223     int endSeq = av.getPosProps().getAbsoluteEndSeq();
224
225     boxX = Math.round((float) startRes * width / alwidth);
226     boxY = Math.round((float) startSeq * sequencesHeight / alheight);
227
228     boxWidth = Math
229             .round((float) (endRes - startRes + 1) * width / alwidth);
230     boxHeight = Math.round((float) (endSeq - startSeq) * sequencesHeight
231             / alheight);
232   }
233
234   /**
235    * Draw the overview panel's viewport box on a graphics object
236    * 
237    * @param g
238    *          the graphics object to draw on
239    */
240   public void drawBox(Graphics g)
241   {
242     g.drawRect(boxX, boxY, boxWidth, boxHeight);
243     g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
244   }
245
246   // don't like this, scroll vals are separate from setting code
247   public int getScrollCol()
248   {
249     return scrollCol;
250   }
251
252   public int getScrollRow()
253   {
254     return scrollRow;
255   }
256
257   // TODO should be removed, when unit test has mock Graphics object available
258   // to check boxX/boxY
259   public int getBoxX()
260   {
261     return boxX;
262   }
263
264   // TODO should be removed, when unit test has mock Graphics object available
265   // to check boxX/boxY
266   public int getBoxY()
267   {
268     return boxY;
269   }
270
271   // TODO should be removed, when unit test has mock Graphics object available
272   public int getBoxWidth()
273   {
274     return boxWidth;
275   }
276
277   // TODO should be removed, when unit test has mock Graphics object available
278   public int getBoxHeight()
279   {
280     return boxHeight;
281   }
282
283   public void setBoxX(int x)
284   {
285     boxX = x;
286   }
287
288   public void setBoxY(int y)
289   {
290     boxY = y;
291   }
292
293   public void setWidth(int w)
294   {
295     width = w;
296   }
297
298   public void setHeight(int h)
299   {
300     sequencesHeight = h - graphHeight;
301   }
302
303   public int getWidth()
304   {
305     return width;
306   }
307
308   public int getHeight()
309   {
310     return sequencesHeight + graphHeight;
311   }
312
313   public int getSequencesHeight()
314   {
315     return sequencesHeight;
316   }
317
318   public int getGraphHeight()
319   {
320     return graphHeight;
321   }
322 }