JAL-4281 store/restore ID width and manual adjustment flag in Jalview projects
[jalview.git] / test / jalview / gui / AlignmentPanelTest.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 static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertNotEquals;
25 import static org.testng.Assert.assertNotNull;
26 import static org.testng.Assert.assertTrue;
27
28 import java.awt.Dimension;
29 import java.awt.Font;
30 import java.awt.FontMetrics;
31 import java.lang.reflect.InvocationTargetException;
32
33 import javax.swing.SwingUtilities;
34
35 import org.testng.annotations.BeforeMethod;
36 import org.testng.annotations.Test;
37
38 import jalview.api.AlignViewportI;
39 import jalview.bin.Cache;
40 import jalview.bin.Jalview;
41 import jalview.datamodel.AlignmentAnnotation;
42 import jalview.datamodel.SequenceI;
43 import jalview.io.DataSourceType;
44 import jalview.io.FileLoader;
45 import jalview.viewmodel.ViewportRanges;
46
47 public class AlignmentPanelTest
48 {
49   AlignFrame af;
50
51   @BeforeMethod(alwaysRun = true)
52   public void setUp() throws InvocationTargetException, InterruptedException
53   {
54     Jalview.main(
55             new String[]
56             { "-nonews", "-props", "test/jalview/testProps.jvprops" });
57
58     Cache.applicationProperties.setProperty("SHOW_IDENTITY",
59             Boolean.TRUE.toString());
60     af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
61             DataSourceType.FILE);
62
63     /*
64      * ensure the panel has been repainted and so ViewportRanges set
65      */
66     SwingUtilities.invokeAndWait(new Runnable()
67     {
68       @Override
69       public void run()
70       {
71         af.repaint();
72       }
73     });
74
75     /*
76      * wait for Consensus thread to complete
77      */
78     do
79     {
80       try
81       {
82         Thread.sleep(50);
83       } catch (InterruptedException x)
84       {
85       }
86     } while (af.getViewport().getCalcManager().isWorking());
87   }
88
89   /**
90    * Test side effect that end residue is set correctly by setScrollValues, with
91    * or without hidden columns
92    */
93   @Test(groups = "Functional")
94   public void testSetScrollValues()
95   {
96     ViewportRanges ranges = af.getViewport().getRanges();
97     af.alignPanel.setScrollValues(0, 0);
98
99     int oldres = ranges.getEndRes();
100     af.alignPanel.setScrollValues(-1, 5);
101
102     // setting -ve x value does not change residue
103     assertEquals(ranges.getEndRes(), oldres);
104
105     af.alignPanel.setScrollValues(0, 5);
106
107     // setting 0 as x value does not change residue
108     assertEquals(ranges.getEndRes(), oldres);
109
110     af.alignPanel.setScrollValues(5, 5);
111     // setting x value to 5 extends endRes by 5 residues
112     assertEquals(ranges.getEndRes(), oldres + 5);
113
114     // scroll to position after hidden columns sets endres to oldres (width) +
115     // position
116     int scrollpos = 60;
117     af.getViewport().hideColumns(30, 50);
118     af.alignPanel.setScrollValues(scrollpos, 5);
119     assertEquals(ranges.getEndRes(), oldres + scrollpos);
120
121     // scroll to position within hidden columns, still sets endres to oldres +
122     // position
123     // not sure if this is actually correct behaviour but this is what Jalview
124     // currently does
125     scrollpos = 40;
126     af.getViewport().showAllHiddenColumns();
127     af.getViewport().hideColumns(30, 50);
128     af.alignPanel.setScrollValues(scrollpos, 5);
129     assertEquals(ranges.getEndRes(), oldres + scrollpos);
130
131     // scroll to position within <width> distance of the end of the alignment
132     // endRes should be set to width of alignment - 1
133     scrollpos = 130;
134     af.getViewport().showAllHiddenColumns();
135     af.alignPanel.setScrollValues(scrollpos, 5);
136     assertEquals(ranges.getEndRes(),
137             af.getViewport().getAlignment().getWidth() - 1);
138
139     // now hide some columns, and scroll to position within <width>
140     // distance of the end of the alignment
141     // endRes should be set to width of alignment - 1 - the number of hidden
142     // columns
143     af.getViewport().hideColumns(30, 50);
144     af.alignPanel.setScrollValues(scrollpos, 5);
145     assertEquals(ranges.getEndRes(),
146             af.getViewport().getAlignment().getWidth() - 1 - 21); // 21 is the
147                                                                   // number of
148                                                                   // hidden
149                                                                   // columns
150   }
151
152   /**
153    * Test that update layout reverts to original (unwrapped) values for endRes
154    * when switching from wrapped back to unwrapped mode (JAL-2739)
155    */
156   @Test(groups = "Functional")
157   public void testUpdateLayout_endRes()
158   {
159     // get details of original alignment dimensions
160     ViewportRanges ranges = af.getViewport().getRanges();
161     int endres = ranges.getEndRes();
162
163     // wrap
164     af.alignPanel.getAlignViewport().setWrapAlignment(true);
165     af.alignPanel.updateLayout();
166
167     // endRes has changed
168     assertNotEquals(ranges.getEndRes(), endres);
169
170     // unwrap
171     af.alignPanel.getAlignViewport().setWrapAlignment(false);
172     af.alignPanel.updateLayout();
173
174     // endRes back to original value
175     assertEquals(ranges.getEndRes(), endres);
176   }
177
178   /**
179    * Test the variant of calculateIdWidth that only recomputes the width if it
180    * is not already saved in the viewport (initial value is -1)
181    */
182   @Test(groups = "Functional")
183   public void testCalculateIdWidth_noArgs()
184   {
185     AlignViewportI av = af.alignPanel.getAlignViewport();
186     av.setShowJVSuffix(true);
187     av.setFont(new Font("Courier", Font.PLAIN, 15), true);
188
189     av.setIdWidth(0);
190     Dimension d = af.alignPanel.calculateIdWidth();
191     assertEquals(d.width, 0);
192     assertEquals(d.height, 0);
193
194     av.setIdWidth(99);
195     d = af.alignPanel.calculateIdWidth();
196     assertEquals(d.width, 99);
197     assertEquals(d.height, 0);
198
199     /*
200      * note 4 pixels padding are added to the longest sequence name width
201      */
202     av.setIdWidth(-1); // force recalculation
203     d = af.alignPanel.calculateIdWidth();
204     assertEquals(d.width, 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
205     assertEquals(d.height, 12);
206     assertEquals(d.width, av.getIdWidth());
207   }
208
209   /**
210    * Test the variant of calculateIdWidth that computes the longest of any
211    * sequence name or annotation label width
212    */
213   @Test(groups = "Functional")
214   public void testCalculateIdWidth_withMaxWidth()
215   {
216     AlignViewportI av = af.alignPanel.getAlignViewport();
217     av.setShowJVSuffix(true);
218     av.setFont(new Font("Courier", Font.PLAIN, 15), true);
219     av.setShowAnnotation(false);
220     av.setIdWidth(18);
221
222     /*
223      * note 4 pixels 'padding' are added to the longest seq name/annotation label
224      */
225     Dimension d = af.alignPanel.calculateIdWidth(2000);
226     assertEquals(d.width, 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
227     assertEquals(d.height, 12); // fixed value (not used?)
228     assertEquals(av.getIdWidth(), 18); // not changed by this method
229
230     /*
231      * make the longest sequence name longer
232      */
233     SequenceI seq = af.viewport.getAlignment()
234             .findSequenceMatch("Q93Z60_ARATH")[0];
235     seq.setName(seq.getName() + "MMMMM");
236     d = af.alignPanel.calculateIdWidth(2000);
237     assertEquals(d.width, 211); // 4 + pixel width of "Q93Z60_ARATHMMMMM/1-118"
238     assertEquals(d.height, 12);
239     assertEquals(av.getIdWidth(), 18); // unchanged
240
241     /*
242      * make the longest annotation name even longer
243      * note this is checked even if annotations are not shown
244      */
245     AlignmentAnnotation aa = av.getAlignment().getAlignmentAnnotation()[0];
246     aa.label = "THIS IS A VERY LONG LABEL INDEED";
247     FontMetrics fmfor = af.alignPanel
248             .getFontMetrics(af.alignPanel.getAlabels().getFont());
249     // Assumption ID_WIDTH_PADDING == 4
250     int expwidth = 4 + fmfor.stringWidth(aa.label);
251     d = af.alignPanel.calculateIdWidth(2000);
252     assertEquals(d.width, expwidth); // 228 == ID_WIDTH_PADDING + pixel width of
253                                      // "THIS IS A VERY LONG LABEL INDEED"
254     assertEquals(d.height, 12);
255
256     /*
257      * override with maxwidth
258      * note the 4 pixels padding is added to this value
259      */
260     d = af.alignPanel.calculateIdWidth(213);
261     assertEquals(d.width, 217);
262     assertEquals(d.height, 12);
263   }
264
265   @Test(groups = { "Functional", "Not-bamboo" })
266   public void testGetVisibleWidth()
267   {
268     /*
269      * width for onscreen rendering is IDPanel width
270      */
271     int w = af.alignPanel.getVisibleIdWidth(true);
272     assertEquals(w, af.alignPanel.getIdPanel().getWidth());
273     assertEquals(w, 115);
274
275     /*
276      * width for offscreen rendering is the same
277      * if no fixed id width is specified in preferences
278      */
279     Cache.setProperty("FIGURE_AUTOIDWIDTH", Boolean.FALSE.toString());
280     Cache.removeProperty("FIGURE_FIXEDIDWIDTH");
281     assertEquals(w, af.alignPanel.getVisibleIdWidth(false));
282
283     /*
284      * preference for fixed id width - note 4 pixels padding is added
285      */
286     Cache.setProperty("FIGURE_FIXEDIDWIDTH", "120");
287     assertEquals(124, af.alignPanel.getVisibleIdWidth(false));
288
289     /*
290      * preference for auto id width overrides fixed width
291      */
292     Cache.setProperty("FIGURE_AUTOIDWIDTH", Boolean.TRUE.toString());
293     assertEquals(115, af.alignPanel.getVisibleIdWidth(false));
294   }
295   @Test(groups = { "Functional", "Not-bamboo" })
296   public void testresetIdWidth()
297   {
298     /*
299      * width for onscreen rendering is IDPanel width
300      */
301     int w = af.alignPanel.getVisibleIdWidth(true);
302     assertEquals(w, af.alignPanel.getIdPanel().getWidth());
303     assertEquals(w, 115);
304
305     // manually adjust 
306     af.viewport.setIdWidth(200);
307     w = af.alignPanel.calculateIdWidth().width;
308     assertTrue(af.alignPanel.getIdPanel().getIdCanvas().isManuallyAdjusted());
309     assertEquals(w, af.alignPanel.getIdPanel().getWidth());
310
311     af.viewport.setIdWidth(-1);
312     af.alignPanel.getIdPanel().getIdCanvas().setManuallyAdjusted(false);
313     w = af.alignPanel.calculateIdWidth().width;
314     
315     assertEquals(w, af.alignPanel.getIdPanel().getWidth());
316
317     assertNotEquals(w,115);
318   }
319   @Test(groups = "Functional")
320   public void testSetOverviewTitle()
321   {
322     OverviewPanel ov1 = this.af.openOverviewPanel(true);
323     String alignFrameTitle = af.getTitle();
324     assertEquals(ov1.getTitle(), "Overview " + alignFrameTitle);
325
326     /*
327      * on New View, existing overview should get " Original" added to title
328      * and new view's overview should get " View 1" added
329      */
330     af.newView_actionPerformed(null);
331     assertEquals(ov1.getTitle(),
332             "Overview " + alignFrameTitle + " Original");
333     OverviewPanel ov2 = this.af.openOverviewPanel(true);
334     assertEquals(ov2.getTitle(), "Overview " + alignFrameTitle + " View 1");
335   }
336
337   @Test(groups = "Functional")
338   public void testSetOverviewTitle_automaticOverview()
339   {
340     Cache.setProperty("SHOW_OVERVIEW", "true");
341     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
342             "examples/uniref50.fa", DataSourceType.FILE);
343     OverviewPanel ov1 = alignFrame.alignPanel.getOverviewPanel();
344     assertNotNull(ov1);
345     String alignFrameTitle = alignFrame.getTitle();
346     assertEquals(ov1.getTitle(), "Overview " + alignFrameTitle);
347
348     /*
349      * on New View, existing overview should get " Original" added to title
350      * and new view's automatic overview should have " View 1" added
351      */
352     alignFrame.newView_actionPerformed(null);
353     assertEquals(ov1.getTitle(),
354             "Overview " + alignFrameTitle + " Original");
355     OverviewPanel ov2 = alignFrame.alignPanel.getOverviewPanel();
356     assertNotNull(ov2);
357     assertEquals(ov2.getTitle(), "Overview " + alignFrameTitle + " View 1");
358   }
359 }