bdbc3c978757c9da76292a3b607ef198ec05e4d1
[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     alignFrame.alignPanel.paintAlignment(false, false);
269     waitForSwing(); // for Swing thread
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.paintAlignment(false, false);
444     waitForSwing();
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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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.paintAlignment(false, false);
640     waitForSwing();
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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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, MouseEvent.MOUSE_MOVED, 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.applicationProperties.setProperty("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     /*
730      * mouse at top left of unwrapped panel
731      */
732     MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
733             0, 0, 0, false, 0);
734     assertEquals(testee.findColumn(evt), 0);
735     
736     /*
737      * not quite one charWidth across
738      */
739     x = charWidth-1;
740     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
741             0, 0, 0, false, 0);
742     assertEquals(testee.findColumn(evt), 0);
743
744     /*
745      * one charWidth across
746      */
747     x = charWidth;
748     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
749             false, 0);
750     assertEquals(testee.findColumn(evt), 1);
751
752     /*
753      * two charWidths across
754      */
755     x = 2 * charWidth;
756     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
757             false, 0);
758     assertEquals(testee.findColumn(evt), 2);
759
760     /*
761      * limited to last column of seqcanvas
762      */
763     x = 20000;
764     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
765             false, 0);
766     SeqCanvas seqCanvas = alignFrame.alignPanel.getSeqPanel().seqCanvas;
767     int w = seqCanvas.getWidth();
768     // limited to number of whole columns, base 0
769     int expected = w / charWidth - 1;
770     assertEquals(testee.findColumn(evt), expected);
771
772     /*
773      * hide columns 5-10 (base 1)
774      */
775     alignFrame.getViewport().hideColumns(4, 9);
776     x = 5 * charWidth + 2;
777     // x is in 6th visible column, absolute column 12, or 11 base 0
778     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
779             false, 0);
780     assertEquals(testee.findColumn(evt), 11);
781   }
782
783   @Test(groups = "Functional")
784   public void testFindColumn_wrapped()
785   {
786     Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
787     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
788             "examples/uniref50.fa", DataSourceType.FILE);
789     AlignViewport av = alignFrame.getViewport();
790     av.setScaleAboveWrapped(false);
791     av.setScaleLeftWrapped(false);
792     av.setScaleRightWrapped(false);
793     alignFrame.alignPanel.paintAlignment(false, false);
794     // need to wait for repaint to finish!
795     waitForSwing();
796     SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
797     int x = 0;
798     final int charWidth = av.getCharWidth();
799     assertTrue(charWidth > 0); // sanity check
800     assertEquals(av.getRanges().getStartRes(), 0);
801   
802     /*
803      * mouse at top left of wrapped panel, no West (left) scale
804      */
805     MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
806             0, 0, 0, false, 0);
807     assertEquals(testee.findColumn(evt), 0);
808     
809     /*
810      * not quite one charWidth across
811      */
812     x = charWidth-1;
813     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
814             0, 0, 0, false, 0);
815     assertEquals(testee.findColumn(evt), 0);
816   
817     /*
818      * one charWidth across
819      */
820     x = charWidth;
821     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
822             false, 0);
823     assertEquals(testee.findColumn(evt), 1);
824
825     /*
826      * x over scale left (before drawn columns) results in -1
827      */
828     av.setScaleLeftWrapped(true);
829     alignFrame.alignPanel.paintAlignment(false, false);
830     waitForSwing();
831     SeqCanvas seqCanvas = testee.seqCanvas;
832     int labelWidth = (int) PA.getValue(seqCanvas, "labelWidthWest");
833     assertTrue(labelWidth > 0);
834     x = labelWidth - 1;
835     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
836             false, 0);
837     assertEquals(testee.findColumn(evt), -1);
838
839     x = labelWidth;
840     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
841             false, 0);
842     assertEquals(testee.findColumn(evt), 0);
843
844     /*
845      * x over right edge of last residue (including scale left)
846      */
847     int residuesWide = av.getRanges().getViewportWidth();
848     assertTrue(residuesWide > 0);
849     x = labelWidth + charWidth * residuesWide - 1;
850     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
851             false, 0);
852     assertEquals(testee.findColumn(evt), residuesWide - 1);
853
854     /*
855      * x over scale right (beyond drawn columns) results in -1
856      */
857     av.setScaleRightWrapped(true);
858     alignFrame.alignPanel.paintAlignment(false, false);
859     waitForSwing();
860     labelWidth = (int) PA.getValue(seqCanvas, "labelWidthEast");
861     assertTrue(labelWidth > 0);
862     int residuesWide2 = av.getRanges().getViewportWidth();
863     assertTrue(residuesWide2 > 0);
864     assertTrue(residuesWide2 < residuesWide); // available width reduced
865     x += 1; // just over left edge of scale right
866     evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
867             false, 0);
868     assertEquals(testee.findColumn(evt), -1);
869     
870     // todo add startRes offset, hidden columns
871
872   }
873   @BeforeClass(alwaysRun = true)
874   public static void setUpBeforeClass() throws Exception
875   {
876     /*
877      * use read-only test properties file
878      */
879     Cache.loadProperties("test/jalview/io/testProps.jvprops");
880     Jalview.main(new String[] { "-nonews" });
881   }
882
883   /**
884    * waits for Swing event dispatch queue to empty
885    */
886   synchronized void waitForSwing()
887   {
888     try
889     {
890       EventQueue.invokeAndWait(new Runnable()
891       {
892         @Override
893         public void run()
894         {
895         }
896       });
897     } catch (InterruptedException | InvocationTargetException e)
898     {
899       e.printStackTrace();
900     }
901   }
902 }