de33b52a724761a4c558befe9b5b0682e3be0d21
[jalview.git] / test / jalview / gui / SeqCanvasTest.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.gui;
22
23 import jalview.bin.Cache;
24 import jalview.bin.Jalview;
25 import jalview.datamodel.AlignmentI;
26 import jalview.io.DataSourceType;
27 import jalview.datamodel.SearchResults;
28 import jalview.datamodel.SearchResultsI;
29 import jalview.io.FileLoader;
30 import jalview.util.Platform;
31 import jalview.viewmodel.ViewportRanges;
32
33 import java.awt.Font;
34 import java.awt.FontMetrics;
35
36 import org.testng.annotations.BeforeClass;
37 import org.testng.annotations.Test;
38 import static org.testng.Assert.assertEquals;
39 import static org.testng.Assert.assertFalse;
40 import static org.testng.Assert.assertNotNull;
41 import static org.testng.Assert.assertNull;
42 import static org.testng.Assert.assertTrue;
43
44 import junit.extensions.PA;
45
46 public class SeqCanvasTest
47 {
48   @BeforeClass(alwaysRun = true)
49   public void setUp()
50   {
51     Cache.loadProperties(null);
52     Cache.initLogger();
53     Desktop.getInstance().setVisible(false);
54   }
55
56   /**
57    * Test the method that computes wrapped width in residues, height of wrapped
58    * widths in pixels, and the number of widths visible
59    */
60   @Test(groups = "Functional")
61   public void testCalculateWrappedGeometry_noAnnotations()
62   {
63     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
64             "examples/uniref50.fa", DataSourceType.FILE);
65     
66     AlignViewport av = af.getViewport();
67     AlignmentI al = av.getAlignment();
68     assertEquals(al.getWidth(), 157);
69     assertEquals(al.getHeight(), 15);
70     av.getRanges().setStartEndSeq(0, 14);
71
72     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
73
74     av.setWrapAlignment(true);
75     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
76     int charHeight = av.getCharHeight();
77     int charWidth = av.getCharWidth();
78     assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
79     assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
80
81     /*
82      * first with scales above, left, right
83      */
84     av.setShowAnnotation(false);
85     av.setScaleAboveWrapped(true);
86     av.setScaleLeftWrapped(true);
87     av.setScaleRightWrapped(true);
88     FontMetrics fm = testee.getFontMetrics(av.getFont());
89     int labelWidth = fm.stringWidth("000") + charWidth;
90     assertEquals(labelWidth,
91             !Platform.isWin() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
92
93     /*
94      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
95      * take the whole multiple of character widths
96      */
97     int canvasWidth = 400;
98     int canvasHeight = 300;
99     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
100     int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
101     assertEquals(wrappedWidth, residueColumns);
102     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
103     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
104     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
105             2 * charHeight);
106     int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
107     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
108     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
109
110     /*
111      * repeat height is 17 * (2 + 15) = 289
112      * make canvas height 2 * 289 + 3 * charHeight so just enough to
113      * draw 2 widths and the first sequence of a third
114      */
115     canvasHeight = charHeight * (17 * 2 + 3);
116     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
117     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
118
119     /*
120      * reduce canvas height by 1 pixel 
121      * - should not be enough height to draw 3 widths
122      */
123     canvasHeight -= 1;
124     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
125     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
126
127     /*
128      * turn off scale above - can now fit in 2 and a bit widths
129      */
130     av.setScaleAboveWrapped(false);
131     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
132     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
133
134     /*
135      * reduce height to enough for 2 widths and not quite a third
136      * i.e. two repeating heights + spacer + sequence - 1 pixel
137      */
138     canvasHeight = charHeight * (16 * 2 + 2) - 1;
139     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
140     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
141
142     /*
143      * make canvas width enough for scales and 20 residues
144      */
145     canvasWidth = 2 * labelWidth + 20 * charWidth;
146     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
147             canvasHeight);
148     assertEquals(wrappedWidth, 20);
149
150     /*
151      * reduce width by 1 pixel - rounds down to 19 residues
152      */
153     canvasWidth -= 1;
154     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
155             canvasHeight);
156     assertEquals(wrappedWidth, 19);
157
158     /*
159      * turn off West scale - adds labelWidth (39) to available for residues
160      * which with the 11 remainder makes 50 which is 4 more charWidths rem 2
161      */
162     av.setScaleLeftWrapped(false);
163     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
164             canvasHeight);
165     assertEquals(wrappedWidth, 23);
166
167     /*
168      * add 10 pixels to width to fit in another whole residue column
169      */
170     canvasWidth += 9;
171     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
172             canvasHeight);
173     assertEquals(wrappedWidth, 23);
174     canvasWidth += 1;
175     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
176             canvasHeight);
177     assertEquals(wrappedWidth, 24);
178
179     /*
180      * turn off East scale to gain 39 more pixels (3 columns remainder 3)
181      */
182     av.setScaleRightWrapped(false);
183     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
184             canvasHeight);
185     assertEquals(wrappedWidth, 27);
186
187     /*
188      * add 9 pixels to width to gain a residue column
189      */
190     canvasWidth += 8;
191     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
192             canvasHeight);
193     assertEquals(wrappedWidth, 27); // 8px not enough
194     canvasWidth += 1;
195     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
196             canvasHeight);
197     assertEquals(wrappedWidth, 28); // 9px is enough
198
199     /*
200      * now West but not East scale - lose 39 pixels or 4 columns
201      */
202     av.setScaleLeftWrapped(true);
203     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
204             canvasHeight);
205     assertEquals(wrappedWidth, 24);
206
207     /*
208      * adding 3 pixels to width regains one column
209      */
210     canvasWidth += 2;
211     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
212             canvasHeight);
213     assertEquals(wrappedWidth, !Platform.isWin() ? 24 : 25); // 2px not enough
214     canvasWidth += 1;
215     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
216             canvasHeight);
217     assertEquals(wrappedWidth, 25); // 3px is enough
218
219     /*
220      * turn off scales left and right, make width exactly 157 columns
221      */
222     av.setScaleLeftWrapped(false);
223     canvasWidth = al.getWidth() * charWidth;
224     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
225     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
226   }
227
228   /**
229    * Test the method that computes wrapped width in residues, height of wrapped
230    * widths in pixels, and the number of widths visible
231    */
232   @Test(groups = "Functional")
233   public void testCalculateWrappedGeometry_withAnnotations()
234   {
235     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
236             "examples/uniref50.fa", DataSourceType.FILE);
237     AlignViewport av = af.getViewport();
238     AlignmentI al = av.getAlignment();
239     assertEquals(al.getWidth(), 157);
240     assertEquals(al.getHeight(), 15);
241   
242     av.setWrapAlignment(true);
243     av.getRanges().setStartEndSeq(0, 14);
244     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
245     int charHeight = av.getCharHeight();
246     int charWidth = av.getCharWidth();
247
248     assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
249     assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
250
251     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
252   
253     /*
254      * first with scales above, left, right
255      */
256     av.setShowAnnotation(true);
257     av.setScaleAboveWrapped(true);
258     av.setScaleLeftWrapped(true);
259     av.setScaleRightWrapped(true);
260
261     FontMetrics fm = testee.getFontMetrics(av.getFont());
262     int labelWidth = fm.stringWidth("000") + charWidth;
263     assertEquals(labelWidth,
264             !Platform.isWin() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
265
266     int annotationHeight = testee.getAnnotationHeight();
267
268     /*
269      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
270      * take the whole multiple of character widths
271      */
272     int canvasWidth = 400;
273     int canvasHeight = 300;
274     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
275     int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
276     assertEquals(wrappedWidth, residueColumns);
277     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
278     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
279     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
280             2 * charHeight);
281     int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
282     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())
283             + SeqCanvas.SEQS_ANNOTATION_GAP + annotationHeight);
284     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
285   
286     /*
287      * repeat height is 17 * (2 + 15) = 289 + 3 + annotationHeight = 510
288      * make canvas height 2 of these plus 3 charHeights 
289      * so just enough to draw 2 widths, gap + scale + the first sequence of a third
290      */
291     canvasHeight = charHeight * (17 * 2 + 3)
292             + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP);
293     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
294     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
295   
296     /*
297      * reduce canvas height by 1 pixel - should not be enough height
298      * to draw 3 widths
299      */
300     canvasHeight -= 1;
301     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
302     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
303   
304     /*
305      * turn off scale above - can now fit in 2 and a bit widths
306      */
307     av.setScaleAboveWrapped(false);
308     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
309     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
310   
311     /*
312      * reduce height to enough for 2 widths and not quite a third
313      * i.e. two repeating heights + spacer + sequence - 1 pixel
314      */
315     canvasHeight = charHeight * (16 * 2 + 2)
316             + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP) - 1;
317     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
318     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
319
320     /*
321      * add 1 pixel to height - should now get 3 widths drawn
322      */
323     canvasHeight += 1;
324     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
325     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
326   }
327
328   /**
329    * Test simulates loading an unwrapped alignment, shrinking it vertically so
330    * not all sequences are visible, then changing to wrapped mode. The ranges
331    * endSeq should be unchanged, but the vertical repeat height should include
332    * all sequences.
333    */
334   @Test(groups = "Functional")
335   public void testCalculateWrappedGeometry_fromScrolled()
336   {
337     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
338             "examples/uniref50.fa", DataSourceType.FILE);
339     // 
340     System.out.println("ap dim = " + af.alignPanel.getSize());
341     System.out.println("seqpan dim = " + af.alignPanel.getSeqPanel().getSize());
342     System.out.println("seqcan dim = " + af.alignPanel.getSeqPanel().seqCanvas.getSize());
343
344
345     AlignViewport av = af.getViewport();
346     AlignmentI al = av.getAlignment();
347     assertEquals(al.getWidth(), 157);
348     assertEquals(al.getHeight(), 15);
349
350     ViewportRanges ranges = av.getRanges();
351     System.out.println(ranges + " before setting to 3");
352     ranges.setStartEndSeq(0, 3);
353     int endSeq = ranges.getEndSeq();
354     System.out.println(ranges + " after setting to 3");
355     av.setShowAnnotation(false);
356     av.setScaleAboveWrapped(true);
357
358     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
359     av.setWrapAlignment(true);
360     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
361     int charHeight = av.getCharHeight();
362     int charWidth = av.getCharWidth();
363     // Windows h=19, w=11.
364     assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
365     assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
366     
367     int canvasWidth = 400;
368     int canvasHeight = 300;
369     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
370     System.out.println(ranges);
371     assertEquals(ranges.getEndSeq(), endSeq); // unchanged
372     int repeatingHeight = (int) PA.getValue(testee,
373             "wrappedRepeatHeightPx");
374     int h = charHeight * (2 + al.getHeight());
375     assertEquals(repeatingHeight, h);
376   }
377   @Test(groups = "Functional")
378   public void testClear_HighlightAndSelection()
379   {
380     AlignViewport av = af.getViewport();
381     SearchResultsI highlight = new SearchResults();
382     highlight.addResult(
383             av.getAlignment().getSequenceAt(1).getDatasetSequence(), 50,
384             80);
385     af.alignPanel.highlightSearchResults(highlight);
386     af.avc.markHighlightedColumns(false, false, false);
387     assertNotNull(av.getSearchResults(),
388             "No highlight was created on alignment");
389     assertFalse(av.getColumnSelection().isEmpty(),
390             "No selection was created from highlight");
391     af.deselectAllSequenceMenuItem_actionPerformed(null);
392     assertTrue(av.getColumnSelection().isEmpty(),
393             "No Selection should be present after deselecting all.");
394     assertNull(av.getSearchResults(),
395             "No higlighted search results should be present after deselecting all.");
396   }
397 }