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