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