Merge branch 'develop' into task/JAL-3130_Java_11_investigations-getdown_src
[jalview.git] / test / jalview / gui / SeqPanelTest.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.assertNull;
25 import static org.testng.Assert.assertTrue;
26
27 import jalview.api.AlignViewportI;
28 import jalview.bin.Cache;
29 import jalview.bin.Jalview;
30 import jalview.commands.EditCommand;
31 import jalview.commands.EditCommand.Action;
32 import jalview.commands.EditCommand.Edit;
33 import jalview.datamodel.Alignment;
34 import jalview.datamodel.AlignmentAnnotation;
35 import jalview.datamodel.AlignmentI;
36 import jalview.datamodel.Sequence;
37 import jalview.datamodel.SequenceI;
38 import jalview.gui.SeqPanel.MousePos;
39 import jalview.io.DataSourceType;
40 import jalview.io.FileLoader;
41 import jalview.util.MessageManager;
42
43 import java.awt.event.InputEvent;import java.awt.EventQueue;
44 import java.awt.event.MouseEvent;
45 import java.lang.reflect.InvocationTargetException;
46
47 import javax.swing.JLabel;
48
49 import org.testng.annotations.AfterMethod;
50 import org.testng.annotations.BeforeClass;
51 import org.testng.annotations.Test;
52
53 import junit.extensions.PA;
54
55 public class SeqPanelTest
56 {
57   AlignFrame af;
58
59   @BeforeClass(alwaysRun = true)
60   public void setUpJvOptionPane()
61   {
62     JvOptionPane.setInteractiveMode(false);
63     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
64   }
65   @Test(groups = "Functional")
66   public void testSetStatusReturnsNearestResiduePosition()
67   {
68     SequenceI seq1 = new Sequence("Seq1", "AACDE");
69     SequenceI seq2 = new Sequence("Seq2", "AA--E");
70     AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
71     AlignFrame alignFrame = new AlignFrame(al, al.getWidth(),
72             al.getHeight());
73     AlignmentI visAl = alignFrame.getViewport().getAlignment();
74
75     // Test either side of gap
76     assertEquals(
77             alignFrame.alignPanel.getSeqPanel().setStatusMessage(
78                     visAl.getSequenceAt(1), 1, 1), 2);
79     assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
80             "Sequence 2 ID: Seq2 Residue: ALA (2)");
81     assertEquals(
82             alignFrame.alignPanel.getSeqPanel().setStatusMessage(
83                     visAl.getSequenceAt(1), 4, 1), 3);
84     assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
85             "Sequence 2 ID: Seq2 Residue: GLU (3)");
86     // no status message at a gap, returns next residue position to the right
87     assertEquals(
88             alignFrame.alignPanel.getSeqPanel().setStatusMessage(
89                     visAl.getSequenceAt(1), 2, 1), 3);
90     assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
91             "Sequence 2 ID: Seq2");
92     assertEquals(
93             alignFrame.alignPanel.getSeqPanel().setStatusMessage(
94                     visAl.getSequenceAt(1), 3, 1), 3);
95     assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
96             "Sequence 2 ID: Seq2");
97   }
98
99   @Test(groups = "Functional")
100   public void testAmbiguousAminoAcidGetsStatusMessage()
101   {
102     SequenceI seq1 = new Sequence("Seq1", "ABCDE");
103     SequenceI seq2 = new Sequence("Seq2", "AB--E");
104     AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
105     AlignFrame alignFrame = new AlignFrame(al, al.getWidth(),
106             al.getHeight());
107     AlignmentI visAl = alignFrame.getViewport().getAlignment();
108
109     assertEquals(
110             alignFrame.alignPanel.getSeqPanel().setStatusMessage(
111                     visAl.getSequenceAt(1), 1, 1), 2);
112     assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
113             "Sequence 2 ID: Seq2 Residue: B (2)");
114   }
115
116   @Test(groups = "Functional")
117   public void testGetEditStatusMessage()
118   {
119     assertNull(SeqPanel.getEditStatusMessage(null));
120
121     EditCommand edit = new EditCommand(); // empty
122     assertNull(SeqPanel.getEditStatusMessage(edit));
123
124     SequenceI[] seqs = new SequenceI[] { new Sequence("a", "b") };
125     
126     // 1 gap
127     edit.addEdit(edit.new Edit(Action.INSERT_GAP, seqs, 1, 1, '-'));
128     String expected = MessageManager.formatMessage("label.insert_gap", "1");
129     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
130
131     // 3 more gaps makes +4
132     edit.addEdit(edit.new Edit(Action.INSERT_GAP, seqs, 1, 3, '-'));
133     expected = MessageManager.formatMessage("label.insert_gaps", "4");
134     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
135
136     // 2 deletes makes + 2
137     edit.addEdit(edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-'));
138     expected = MessageManager.formatMessage("label.insert_gaps", "2");
139     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
140
141     // 2 more deletes makes 0 - no text
142     edit.addEdit(edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-'));
143     assertNull(SeqPanel.getEditStatusMessage(edit));
144
145     // 1 more delete makes 1 delete
146     edit.addEdit(edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-'));
147     expected = MessageManager.formatMessage("label.delete_gap", "1");
148     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
149
150     // 1 more delete makes 2 deletes
151     edit.addEdit(edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-'));
152     expected = MessageManager.formatMessage("label.delete_gaps", "2");
153     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
154   }
155
156   /**
157    * Tests that simulate 'locked editing', where an inserted gap is balanced by
158    * a gap deletion in the selection group, and vice versa
159    */
160   @Test(groups = "Functional")
161   public void testGetEditStatusMessage_lockedEditing()
162   {
163     EditCommand edit = new EditCommand(); // empty
164     SequenceI[] seqs = new SequenceI[] { new Sequence("a", "b") };
165     
166     // 1 gap inserted, balanced by 1 delete
167     Edit e1 = edit.new Edit(Action.INSERT_GAP, seqs, 1, 1, '-');
168     edit.addEdit(e1);
169     Edit e2 = edit.new Edit(Action.DELETE_GAP, seqs, 5, 1, '-');
170     e2.setSystemGenerated(true);
171     edit.addEdit(e2);
172     String expected = MessageManager.formatMessage("label.insert_gap", "1");
173     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
174   
175     // 2 more gaps makes +3
176     Edit e3 = edit.new Edit(Action.INSERT_GAP, seqs, 1, 2, '-');
177     edit.addEdit(e3);
178     Edit e4 = edit.new Edit(Action.DELETE_GAP, seqs, 5, 2, '-');
179     e4.setSystemGenerated(true);
180     edit.addEdit(e4);
181     expected = MessageManager.formatMessage("label.insert_gaps", "3");
182     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
183   
184     // 2 deletes makes + 1
185     Edit e5 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-');
186     edit.addEdit(e5);
187     Edit e6 = edit.new Edit(Action.INSERT_GAP, seqs, 5, 2, '-');
188     e6.setSystemGenerated(true);
189     edit.addEdit(e6);
190     expected = MessageManager.formatMessage("label.insert_gap", "1");
191     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
192   
193     // 1 more delete makes 0 - no text
194     Edit e7 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
195     edit.addEdit(e7);
196     Edit e8 = edit.new Edit(Action.INSERT_GAP, seqs, 5, 1, '-');
197     e8.setSystemGenerated(true);
198     edit.addEdit(e8);
199     expected = MessageManager.formatMessage("label.insert_gaps", "2");
200     assertNull(SeqPanel.getEditStatusMessage(edit));
201   
202     // 1 more delete makes 1 delete
203     Edit e9 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
204     edit.addEdit(e9);
205     Edit e10 = edit.new Edit(Action.INSERT_GAP, seqs, 5, 1, '-');
206     e10.setSystemGenerated(true);
207     edit.addEdit(e10);
208     expected = MessageManager.formatMessage("label.delete_gap", "1");
209     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
210   
211     // 2 more deletes makes 3 deletes
212     Edit e11 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-');
213     edit.addEdit(e11);
214     Edit e12 = edit.new Edit(Action.INSERT_GAP, seqs, 5, 2, '-');
215     e12.setSystemGenerated(true);
216     edit.addEdit(e12);
217     expected = MessageManager.formatMessage("label.delete_gaps", "3");
218     assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
219   }
220
221   public void testFindMousePosition_unwrapped()
222   {
223     String seqData = ">Seq1\nAACDE\n>Seq2\nAA--E\n";
224     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(seqData,
225             DataSourceType.PASTE);
226     AlignViewportI av = alignFrame.getViewport();
227     av.setShowAnnotation(true);
228     av.setWrapAlignment(false);
229     final int charHeight = av.getCharHeight();
230     final int charWidth = av.getCharWidth();
231     // sanity checks:
232     assertTrue(charHeight > 0);
233     assertTrue(charWidth > 0);
234     assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
235
236     SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
237     int x = 0;
238     int y = 0;
239
240     /*
241      * mouse at top left of unwrapped panel
242      */
243     MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
244             0, 0, 0, false, 0);
245     MousePos pos = testee.findMousePosition(evt);
246     assertEquals(pos.column, 0);
247     assertEquals(pos.seqIndex, 0);
248     assertEquals(pos.annotationIndex, -1);
249   }
250
251   @AfterMethod(alwaysRun = true)
252   public void tearDown()
253   {
254     Desktop.instance.closeAll_actionPerformed(null);
255   }
256
257   @Test(groups = "Functional")
258   public void testFindMousePosition_wrapped_annotations()
259   {
260     Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "true");
261     Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
262     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
263             "examples/uniref50.fa", DataSourceType.FILE);
264     AlignViewportI av = alignFrame.getViewport();
265     av.setScaleAboveWrapped(false);
266     av.setScaleLeftWrapped(false);
267     av.setScaleRightWrapped(false);
268
269     alignFrame.alignPanel.updateLayout();
270
271     final int charHeight = av.getCharHeight();
272     final int charWidth = av.getCharWidth();
273     final int alignmentHeight = av.getAlignment().getHeight();
274     
275     // sanity checks:
276     assertTrue(charHeight > 0);
277     assertTrue(charWidth > 0);
278     assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
279   
280     SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
281     int x = 0;
282     int y = 0;
283   
284     /*
285      * mouse at top left of wrapped panel; there is a gap of charHeight
286      * above the alignment
287      */
288     MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
289             0, 0, 0, false, 0);
290     MousePos pos = testee.findMousePosition(evt);
291     assertEquals(pos.column, 0);
292     assertEquals(pos.seqIndex, -1); // above sequences
293     assertEquals(pos.annotationIndex, -1);
294
295     /*
296      * cursor at bottom of gap above
297      */
298     y = charHeight - 1;
299     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
300             false, 0);
301     pos = testee.findMousePosition(evt);
302     assertEquals(pos.seqIndex, -1);
303     assertEquals(pos.annotationIndex, -1);
304
305     /*
306      * cursor over top of first sequence
307      */
308     y = charHeight;
309     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
310             false, 0);
311     pos = testee.findMousePosition(evt);
312     assertEquals(pos.seqIndex, 0);
313     assertEquals(pos.annotationIndex, -1);
314
315     /*
316      * cursor at bottom of first sequence
317      */
318     y = 2 * charHeight - 1;
319     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
320             false, 0);
321     pos = testee.findMousePosition(evt);
322     assertEquals(pos.seqIndex, 0);
323     assertEquals(pos.annotationIndex, -1);
324
325     /*
326      * cursor at top of second sequence
327      */
328     y = 2 * charHeight;
329     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
330             false, 0);
331     pos = testee.findMousePosition(evt);
332     assertEquals(pos.seqIndex, 1);
333     assertEquals(pos.annotationIndex, -1);
334
335     /*
336      * cursor at bottom of second sequence
337      */
338     y = 3 * charHeight - 1;
339     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
340             false, 0);
341     pos = testee.findMousePosition(evt);
342     assertEquals(pos.seqIndex, 1);
343     assertEquals(pos.annotationIndex, -1);
344
345     /*
346      * cursor at bottom of last sequence
347      */
348     y = charHeight * (1 + alignmentHeight) - 1;
349     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
350             false, 0);
351     pos = testee.findMousePosition(evt);
352     assertEquals(pos.seqIndex, alignmentHeight - 1);
353     assertEquals(pos.annotationIndex, -1);
354
355     /*
356      * cursor below sequences, in 3-pixel gap above annotations
357      * method reports index of nearest sequence above
358      */
359     y += 1;
360     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
361             false, 0);
362     pos = testee.findMousePosition(evt);
363     assertEquals(pos.seqIndex, alignmentHeight - 1);
364     assertEquals(pos.annotationIndex, -1);
365
366     /*
367      * cursor still in the gap above annotations, now at the bottom of it
368      */
369     y += SeqCanvas.SEQS_ANNOTATION_GAP - 1; // 3-1 = 2
370     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
371             false, 0);
372     pos = testee.findMousePosition(evt);
373     assertEquals(pos.seqIndex, alignmentHeight - 1);
374     assertEquals(pos.annotationIndex, -1);
375
376     AlignmentAnnotation[] annotationRows = av.getAlignment()
377             .getAlignmentAnnotation();
378     for (int n = 0; n < annotationRows.length; n++)
379     {
380       /*
381        * cursor at the top of the n'th annotation  
382        */
383       y += 1;
384       evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
385               false, 0);
386       pos = testee.findMousePosition(evt);
387       assertEquals(pos.seqIndex, alignmentHeight - 1);
388       assertEquals(pos.annotationIndex, n); // over n'th annotation
389
390       /*
391        * cursor at the bottom of the n'th annotation  
392        */
393       y += annotationRows[n].height - 1;
394       evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
395               false, 0);
396       pos = testee.findMousePosition(evt);
397       assertEquals(pos.seqIndex, alignmentHeight - 1);
398       assertEquals(pos.annotationIndex, n);
399     }
400
401     /*
402      * cursor in gap between wrapped widths  
403      */
404     y += 1;
405     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
406             false, 0);
407     pos = testee.findMousePosition(evt);
408     assertEquals(pos.seqIndex, -1);
409     assertEquals(pos.annotationIndex, -1);
410
411     /*
412      * cursor at bottom of gap between wrapped widths  
413      */
414     y += charHeight - 1;
415     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
416             false, 0);
417     pos = testee.findMousePosition(evt);
418     assertEquals(pos.seqIndex, -1);
419     assertEquals(pos.annotationIndex, -1);
420
421     /*
422      * cursor at top of first sequence, second wrapped width  
423      */
424     y += 1;
425     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
426             false, 0);
427     pos = testee.findMousePosition(evt);
428     assertEquals(pos.seqIndex, 0);
429     assertEquals(pos.annotationIndex, -1);
430   }
431
432   @Test(groups = "Functional")
433   public void testFindMousePosition_wrapped_scaleAbove()
434   {
435     Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "true");
436     Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
437     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
438             "examples/uniref50.fa", DataSourceType.FILE);
439     AlignViewportI av = alignFrame.getViewport();
440     av.setScaleAboveWrapped(true);
441     av.setScaleLeftWrapped(false);
442     av.setScaleRightWrapped(false);
443     alignFrame.alignPanel.updateLayout();
444
445     final int charHeight = av.getCharHeight();
446     final int charWidth = av.getCharWidth();
447     final int alignmentHeight = av.getAlignment().getHeight();
448     
449     // sanity checks:
450     assertTrue(charHeight > 0);
451     assertTrue(charWidth > 0);
452     assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
453   
454     SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
455     int x = 0;
456     int y = 0;
457   
458     /*
459      * mouse at top left of wrapped panel; there is a gap of charHeight
460      * above the alignment
461      */
462     MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
463             0, 0, 0, false, 0);
464     MousePos pos = testee.findMousePosition(evt);
465     assertEquals(pos.column, 0);
466     assertEquals(pos.seqIndex, -1); // above sequences
467     assertEquals(pos.annotationIndex, -1);
468   
469     /*
470      * cursor at bottom of gap above
471      * two charHeights including scale panel
472      */
473     y = 2 * charHeight - 1;
474     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
475             false, 0);
476     pos = testee.findMousePosition(evt);
477     assertEquals(pos.seqIndex, -1);
478     assertEquals(pos.annotationIndex, -1);
479   
480     /*
481      * cursor over top of first sequence
482      */
483     y += 1;
484     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
485             false, 0);
486     pos = testee.findMousePosition(evt);
487     assertEquals(pos.seqIndex, 0);
488     assertEquals(pos.annotationIndex, -1);
489   
490     /*
491      * cursor at bottom of first sequence
492      */
493     y += charHeight - 1;
494     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
495             false, 0);
496     pos = testee.findMousePosition(evt);
497     assertEquals(pos.seqIndex, 0);
498     assertEquals(pos.annotationIndex, -1);
499   
500     /*
501      * cursor at top of second sequence
502      */
503     y += 1;
504     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
505             false, 0);
506     pos = testee.findMousePosition(evt);
507     assertEquals(pos.seqIndex, 1);
508     assertEquals(pos.annotationIndex, -1);
509   
510     /*
511      * cursor at bottom of second sequence
512      */
513     y += charHeight - 1;
514     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
515             false, 0);
516     pos = testee.findMousePosition(evt);
517     assertEquals(pos.seqIndex, 1);
518     assertEquals(pos.annotationIndex, -1);
519   
520     /*
521      * cursor at bottom of last sequence
522      * (scale + gap + sequences)
523      */
524     y = charHeight * (2 + alignmentHeight) - 1;
525     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
526             false, 0);
527     pos = testee.findMousePosition(evt);
528     assertEquals(pos.seqIndex, alignmentHeight - 1);
529     assertEquals(pos.annotationIndex, -1);
530   
531     /*
532      * cursor below sequences, in 3-pixel gap above annotations
533      */
534     y += 1;
535     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
536             false, 0);
537     pos = testee.findMousePosition(evt);
538     assertEquals(pos.seqIndex, alignmentHeight - 1);
539     assertEquals(pos.annotationIndex, -1);
540   
541     /*
542      * cursor still in the gap above annotations, now at the bottom of it
543      * method reports index of nearest sequence above  
544      */
545     y += SeqCanvas.SEQS_ANNOTATION_GAP - 1; // 3-1 = 2
546     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
547             false, 0);
548     pos = testee.findMousePosition(evt);
549     assertEquals(pos.seqIndex, alignmentHeight - 1);
550     assertEquals(pos.annotationIndex, -1);
551   
552     AlignmentAnnotation[] annotationRows = av.getAlignment().getAlignmentAnnotation();
553     for (int n = 0; n < annotationRows.length; n++)
554     {
555       /*
556        * cursor at the top of the n'th annotation  
557        */
558       y += 1;
559       evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
560               false, 0);
561       pos = testee.findMousePosition(evt);
562       assertEquals(pos.seqIndex, alignmentHeight - 1);
563       assertEquals(pos.annotationIndex, n); // over n'th annotation
564
565       /*
566        * cursor at the bottom of the n'th annotation  
567        */
568       y += annotationRows[n].height - 1;
569       evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
570               false, 0);
571       pos = testee.findMousePosition(evt);
572       assertEquals(pos.seqIndex, alignmentHeight - 1);
573       assertEquals(pos.annotationIndex, n);
574     }
575   
576     /*
577      * cursor in gap between wrapped widths  
578      */
579     y += 1;
580     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
581             false, 0);
582     pos = testee.findMousePosition(evt);
583     assertEquals(pos.seqIndex, -1);
584     assertEquals(pos.annotationIndex, -1);
585   
586     /*
587      * cursor at bottom of gap between wrapped widths  
588      */
589     y += charHeight - 1;
590     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
591             false, 0);
592     pos = testee.findMousePosition(evt);
593     assertEquals(pos.seqIndex, -1);
594     assertEquals(pos.annotationIndex, -1);
595   
596     /*
597      * cursor at top of scale, second wrapped width  
598      */
599     y += 1;
600     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
601             false, 0);
602     pos = testee.findMousePosition(evt);
603     assertEquals(pos.seqIndex, -1);
604     assertEquals(pos.annotationIndex, -1);
605
606     /*
607      * cursor at bottom of scale, second wrapped width  
608      */
609     y += charHeight - 1;
610     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
611             false, 0);
612     pos = testee.findMousePosition(evt);
613     assertEquals(pos.seqIndex, -1);
614     assertEquals(pos.annotationIndex, -1);
615
616     /*
617      * cursor at top of first sequence, second wrapped width  
618      */
619     y += 1;
620     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
621             false, 0);
622     pos = testee.findMousePosition(evt);
623     assertEquals(pos.seqIndex, 0);
624     assertEquals(pos.annotationIndex, -1);
625   }
626
627   @Test(groups = "Functional")
628   public void testFindMousePosition_wrapped_noAnnotations()
629   {
630     Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "false");
631     Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
632     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
633             "examples/uniref50.fa", DataSourceType.FILE);
634     AlignViewportI av = alignFrame.getViewport();
635     av.setScaleAboveWrapped(false);
636     av.setScaleLeftWrapped(false);
637     av.setScaleRightWrapped(false);
638     alignFrame.alignPanel.updateLayout();
639
640     final int charHeight = av.getCharHeight();
641     final int charWidth = av.getCharWidth();
642     final int alignmentHeight = av.getAlignment().getHeight();
643     
644     // sanity checks:
645     assertTrue(charHeight > 0);
646     assertTrue(charWidth > 0);
647     assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
648   
649     SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
650     int x = 0;
651     int y = 0;
652   
653     /*
654      * mouse at top left of wrapped panel; there is a gap of charHeight
655      * above the alignment
656      */
657     MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
658             0, 0, 0, false, 0);
659     MousePos pos = testee.findMousePosition(evt);
660     assertEquals(pos.column, 0);
661     assertEquals(pos.seqIndex, -1); // above sequences
662     assertEquals(pos.annotationIndex, -1);
663   
664     /*
665      * cursor over top of first sequence
666      */
667     y = charHeight;
668     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
669             false, 0);
670     pos = testee.findMousePosition(evt);
671     assertEquals(pos.seqIndex, 0);
672     assertEquals(pos.annotationIndex, -1);
673
674     /*
675      * cursor at bottom of last sequence
676      */
677     y = charHeight * (1 + alignmentHeight) - 1;
678     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
679             false, 0);
680     pos = testee.findMousePosition(evt);
681     assertEquals(pos.seqIndex, alignmentHeight - 1);
682     assertEquals(pos.annotationIndex, -1);
683   
684     /*
685      * cursor below sequences, at top of charHeight gap between widths
686      */
687     y += 1;
688     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
689             false, 0);
690     pos = testee.findMousePosition(evt);
691     assertEquals(pos.seqIndex, -1);
692     assertEquals(pos.annotationIndex, -1);
693   
694     /*
695      * cursor below sequences, at top of charHeight gap between widths
696      */
697     y += charHeight - 1;
698     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
699             false, 0);
700     pos = testee.findMousePosition(evt);
701     assertEquals(pos.seqIndex, -1);
702     assertEquals(pos.annotationIndex, -1);
703   
704     /*
705      * cursor at the top of the first sequence, second width  
706      */
707     y += 1;
708     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
709             false, 0);
710     pos = testee.findMousePosition(evt);
711     assertEquals(pos.seqIndex, 0);
712     assertEquals(pos.annotationIndex, -1);
713   }
714
715   @Test(groups = "Functional")
716   public void testFindColumn_unwrapped()
717   {
718     Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "false");
719     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
720             "examples/uniref50.fa", DataSourceType.FILE);
721     SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
722     int x = 0;
723     final int charWidth = alignFrame.getViewport().getCharWidth();
724     assertTrue(charWidth > 0); // sanity check
725     assertEquals(alignFrame.getViewport().getRanges().getStartRes(), 0);
726
727     /*
728      * mouse at top left of unwrapped panel
729      */
730     MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
731             0, 0, 0, false, 0);
732     assertEquals(testee.findColumn(evt), 0);
733     
734     /*
735      * not quite one charWidth across
736      */
737     x = charWidth-1;
738     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
739             0, 0, 0, false, 0);
740     assertEquals(testee.findColumn(evt), 0);
741
742     /*
743      * one charWidth across
744      */
745     x = charWidth;
746     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
747             false, 0);
748     assertEquals(testee.findColumn(evt), 1);
749
750     /*
751      * two charWidths across
752      */
753     x = 2 * charWidth;
754     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
755             false, 0);
756     assertEquals(testee.findColumn(evt), 2);
757
758     /*
759      * limited to last column of seqcanvas
760      */
761     x = 20000;
762     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
763             false, 0);
764     SeqCanvas seqCanvas = alignFrame.alignPanel.getSeqPanel().seqCanvas;
765     int w = seqCanvas.getWidth();
766     // limited to number of whole columns, base 0
767     int expected = w / charWidth - 1;
768     assertEquals(testee.findColumn(evt), expected);
769
770     /*
771      * hide columns 5-10 (base 1)
772      */
773     alignFrame.getViewport().hideColumns(4, 9);
774     x = 5 * charWidth + 2;
775     // x is in 6th visible column, absolute column 12, or 11 base 0
776     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
777             false, 0);
778     assertEquals(testee.findColumn(evt), 11);
779   }
780
781   @Test(groups = "Functional")
782   public void testFindColumn_wrapped()
783   {
784     Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
785     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
786             "examples/uniref50.fa", DataSourceType.FILE);
787     AlignViewport av = alignFrame.getViewport();
788     av.setScaleAboveWrapped(false);
789     av.setScaleLeftWrapped(false);
790     av.setScaleRightWrapped(false);
791     alignFrame.alignPanel.updateLayout();
792     SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
793     int x = 0;
794     final int charWidth = av.getCharWidth();
795     assertTrue(charWidth > 0); // sanity check
796     assertEquals(av.getRanges().getStartRes(), 0);
797   
798     /*
799      * mouse at top left of wrapped panel, no West (left) scale
800      */
801     MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
802             0, 0, 0, false, 0);
803     assertEquals(testee.findColumn(evt), 0);
804     
805     /*
806      * not quite one charWidth across
807      */
808     x = charWidth-1;
809     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
810             0, 0, 0, false, 0);
811     assertEquals(testee.findColumn(evt), 0);
812   
813     /*
814      * one charWidth across
815      */
816     x = charWidth;
817     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
818             false, 0);
819     assertEquals(testee.findColumn(evt), 1);
820
821     /*
822      * x over scale left (before drawn columns) results in -1
823      */
824     av.setScaleLeftWrapped(true);
825     alignFrame.alignPanel.updateLayout();
826     SeqCanvas seqCanvas = testee.seqCanvas;
827     int labelWidth = (int) PA.getValue(seqCanvas, "labelWidthWest");
828     assertTrue(labelWidth > 0);
829     x = labelWidth - 1;
830     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
831             false, 0);
832     assertEquals(testee.findColumn(evt), -1);
833
834     x = labelWidth;
835     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
836             false, 0);
837     assertEquals(testee.findColumn(evt), 0);
838
839     /*
840      * x over right edge of last residue (including scale left)
841      */
842     int residuesWide = av.getRanges().getViewportWidth();
843     assertTrue(residuesWide > 0);
844     x = labelWidth + charWidth * residuesWide - 1;
845     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
846             false, 0);
847     assertEquals(testee.findColumn(evt), residuesWide - 1);
848
849     /*
850      * x over scale right (beyond drawn columns) results in -1
851      */
852     av.setScaleRightWrapped(true);
853     alignFrame.alignPanel.updateLayout();
854     labelWidth = (int) PA.getValue(seqCanvas, "labelWidthEast");
855     assertTrue(labelWidth > 0);
856     int residuesWide2 = av.getRanges().getViewportWidth();
857     assertTrue(residuesWide2 > 0);
858     assertTrue(residuesWide2 < residuesWide); // available width reduced
859     x += 1; // just over left edge of scale right
860     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
861             false, 0);
862     assertEquals(testee.findColumn(evt), -1);
863     
864     // todo add startRes offset, hidden columns
865
866   }
867   @BeforeClass(alwaysRun = true)
868   public static void setUpBeforeClass() throws Exception
869   {
870     /*
871      * use read-only test properties file
872      */
873     Cache.loadProperties("test/jalview/io/testProps.jvprops");
874     Jalview.main(new String[] { "-nonews" });
875   }
876
877   /**
878    * waits for Swing event dispatch queue to empty
879    */
880   synchronized void waitForSwing()
881   {
882     try
883     {
884       EventQueue.invokeAndWait(new Runnable()
885       {
886         @Override
887         public void run()
888         {
889         }
890       });
891     } catch (InterruptedException | InvocationTargetException e)
892     {
893       e.printStackTrace();
894     }
895   }
896 }