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