JAL-1807 still testing
[jalviewjs.git] / unused / appletgui / SeqPanel.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)\r
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors\r
4  * \r
5  * This file is part of Jalview.\r
6  * \r
7  * Jalview is free software: you can redistribute it and/or\r
8  * modify it under the terms of the GNU General Public License \r
9  * as published by the Free Software Foundation, either version 3\r
10  * of the License, or (at your option) any later version.\r
11  *  \r
12  * Jalview is distributed in the hope that it will be useful, but \r
13  * WITHOUT ANY WARRANTY; without even the implied warranty \r
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
15  * PURPOSE.  See the GNU General Public License for more details.\r
16  * \r
17  * You should have received a copy of the GNU General Public License\r
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
19  * The Jalview Authors are detailed in the 'AUTHORS' file.\r
20  */\r
21 package jalview.appletgui;\r
22 \r
23 import jalview.api.AlignViewportI;\r
24 import jalview.commands.EditCommand;\r
25 import jalview.commands.EditCommand.Action;\r
26 import jalview.datamodel.ColumnSelection;\r
27 import jalview.datamodel.SearchResults;\r
28 import jalview.datamodel.SearchResults.Match;\r
29 import jalview.datamodel.Sequence;\r
30 import jalview.datamodel.SequenceFeature;\r
31 import jalview.datamodel.SequenceGroup;\r
32 import jalview.datamodel.SequenceI;\r
33 import jalview.schemes.ResidueProperties;\r
34 import jalview.structure.SelectionListener;\r
35 import jalview.structure.SelectionSource;\r
36 import jalview.structure.SequenceListener;\r
37 import jalview.structure.StructureSelectionManager;\r
38 import jalview.structure.VamsasSource;\r
39 import jalview.util.Comparison;\r
40 import jalview.util.MappingUtils;\r
41 import jalview.util.MessageManager;\r
42 import jalview.viewmodel.AlignmentViewport;\r
43 \r
44 import java.awt.BorderLayout;\r
45 import java.awt.Font;\r
46 import java.awt.FontMetrics;\r
47 import java.awt.Point;\r
48 import java.awt.event.InputEvent;\r
49 import java.awt.event.MouseEvent;\r
50 import java.awt.event.MouseListener;\r
51 import java.awt.event.MouseMotionListener;\r
52 import java.util.List;\r
53 import java.util.Vector;\r
54 \r
55 import javax.swing.JPanel;\r
56 \r
57 public class SeqPanel extends JPanel implements MouseMotionListener,\r
58         MouseListener, SequenceListener, SelectionListener\r
59 {\r
60 \r
61   public SeqCanvas seqCanvas;\r
62 \r
63   public AlignmentPanel ap;\r
64 \r
65   protected int lastres;\r
66 \r
67   protected int startseq;\r
68 \r
69   protected AlignViewport av;\r
70 \r
71   // if character is inserted or deleted, we will need to recalculate the\r
72   // conservation\r
73   boolean seqEditOccurred = false;\r
74 \r
75   ScrollThread scrollThread = null;\r
76 \r
77   boolean mouseDragging = false;\r
78 \r
79   boolean editingSeqs = false;\r
80 \r
81   boolean groupEditing = false;\r
82 \r
83   int oldSeq = -1;\r
84 \r
85   boolean changeEndSeq = false;\r
86 \r
87   boolean changeStartSeq = false;\r
88 \r
89   boolean changeEndRes = false;\r
90 \r
91   boolean changeStartRes = false;\r
92 \r
93   SequenceGroup stretchGroup = null;\r
94 \r
95   StringBuffer keyboardNo1;\r
96 \r
97   StringBuffer keyboardNo2;\r
98 \r
99   boolean mouseWheelPressed = false;\r
100 \r
101   Point lastMousePress;\r
102 \r
103   EditCommand editCommand;\r
104 \r
105   StructureSelectionManager ssm;\r
106 \r
107   public SeqPanel(AlignViewport avp, AlignmentPanel p)\r
108   {\r
109     this.av = avp;\r
110 \r
111     seqCanvas = new SeqCanvas(avp);\r
112     setLayout(new BorderLayout());\r
113     add(seqCanvas);\r
114 \r
115     ap = p;\r
116 \r
117     seqCanvas.addMouseMotionListener(this);\r
118     seqCanvas.addMouseListener(this);\r
119     ssm = StructureSelectionManager.getStructureSelectionManager(av.applet);\r
120     ssm.addStructureViewerListener(this);\r
121     ssm.addSelectionListener(this);\r
122 \r
123     seqCanvas.repaint();\r
124   }\r
125 \r
126   void endEditing()\r
127   {\r
128     if (editCommand != null && editCommand.getSize() > 0)\r
129     {\r
130       ap.alignFrame.addHistoryItem(editCommand);\r
131       av.firePropertyChange("alignment", null, av.getAlignment()\r
132               .getSequences());\r
133     }\r
134 \r
135     startseq = -1;\r
136     lastres = -1;\r
137     editingSeqs = false;\r
138     groupEditing = false;\r
139     keyboardNo1 = null;\r
140     keyboardNo2 = null;\r
141     editCommand = null;\r
142   }\r
143 \r
144   void setCursorRow()\r
145   {\r
146     seqCanvas.cursorY = getKeyboardNo1() - 1;\r
147     scrollToVisible();\r
148   }\r
149 \r
150   void setCursorColumn()\r
151   {\r
152     seqCanvas.cursorX = getKeyboardNo1() - 1;\r
153     scrollToVisible();\r
154   }\r
155 \r
156   void setCursorRowAndColumn()\r
157   {\r
158     if (keyboardNo2 == null)\r
159     {\r
160       keyboardNo2 = new StringBuffer();\r
161     }\r
162     else\r
163     {\r
164       seqCanvas.cursorX = getKeyboardNo1() - 1;\r
165       seqCanvas.cursorY = getKeyboardNo2() - 1;\r
166       scrollToVisible();\r
167     }\r
168   }\r
169 \r
170   void setCursorPosition()\r
171   {\r
172     SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);\r
173 \r
174     seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;\r
175     scrollToVisible();\r
176   }\r
177 \r
178   void moveCursor(int dx, int dy)\r
179   {\r
180     seqCanvas.cursorX += dx;\r
181     seqCanvas.cursorY += dy;\r
182     if (av.hasHiddenColumns()\r
183             && !av.getColumnSelection().isVisible(seqCanvas.cursorX))\r
184     {\r
185       int original = seqCanvas.cursorX - dx;\r
186       int maxWidth = av.getAlignment().getWidth();\r
187 \r
188       while (!av.getColumnSelection().isVisible(seqCanvas.cursorX)\r
189               && seqCanvas.cursorX < maxWidth && seqCanvas.cursorX > 0)\r
190       {\r
191         seqCanvas.cursorX += dx;\r
192       }\r
193 \r
194       if (seqCanvas.cursorX >= maxWidth\r
195               || !av.getColumnSelection().isVisible(seqCanvas.cursorX))\r
196       {\r
197         seqCanvas.cursorX = original;\r
198       }\r
199     }\r
200     scrollToVisible();\r
201   }\r
202 \r
203   void scrollToVisible()\r
204   {\r
205     if (seqCanvas.cursorX < 0)\r
206     {\r
207       seqCanvas.cursorX = 0;\r
208     }\r
209     else if (seqCanvas.cursorX > av.getAlignment().getWidth() - 1)\r
210     {\r
211       seqCanvas.cursorX = av.getAlignment().getWidth() - 1;\r
212     }\r
213 \r
214     if (seqCanvas.cursorY < 0)\r
215     {\r
216       seqCanvas.cursorY = 0;\r
217     }\r
218     else if (seqCanvas.cursorY > av.getAlignment().getHeight() - 1)\r
219     {\r
220       seqCanvas.cursorY = av.getAlignment().getHeight() - 1;\r
221     }\r
222 \r
223     endEditing();\r
224     if (av.getWrapAlignment())\r
225     {\r
226       ap.scrollToWrappedVisible(seqCanvas.cursorX);\r
227     }\r
228     else\r
229     {\r
230       while (seqCanvas.cursorY < av.startSeq)\r
231       {\r
232         ap.scrollUp(true);\r
233       }\r
234       while (seqCanvas.cursorY + 1 > av.endSeq)\r
235       {\r
236         ap.scrollUp(false);\r
237       }\r
238       while (seqCanvas.cursorX < av.getColumnSelection()\r
239               .adjustForHiddenColumns(av.startRes))\r
240       {\r
241 \r
242         if (!ap.scrollRight(false))\r
243         {\r
244           break;\r
245         }\r
246       }\r
247       while (seqCanvas.cursorX > av.getColumnSelection()\r
248               .adjustForHiddenColumns(av.endRes))\r
249       {\r
250         if (!ap.scrollRight(true))\r
251         {\r
252           break;\r
253         }\r
254       }\r
255     }\r
256     setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),\r
257             seqCanvas.cursorX, seqCanvas.cursorY);\r
258 \r
259     seqCanvas.repaint();\r
260   }\r
261 \r
262   void setSelectionAreaAtCursor(boolean topLeft)\r
263   {\r
264     SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);\r
265 \r
266     if (av.getSelectionGroup() != null)\r
267     {\r
268       SequenceGroup sg = av.getSelectionGroup();\r
269       // Find the top and bottom of this group\r
270       int min = av.getAlignment().getHeight(), max = 0;\r
271       for (int i = 0; i < sg.getSize(); i++)\r
272       {\r
273         int index = av.getAlignment().findIndex(sg.getSequenceAt(i));\r
274         if (index > max)\r
275         {\r
276           max = index;\r
277         }\r
278         if (index < min)\r
279         {\r
280           min = index;\r
281         }\r
282       }\r
283 \r
284       max++;\r
285 \r
286       if (topLeft)\r
287       {\r
288         sg.setStartRes(seqCanvas.cursorX);\r
289         if (sg.getEndRes() < seqCanvas.cursorX)\r
290         {\r
291           sg.setEndRes(seqCanvas.cursorX);\r
292         }\r
293 \r
294         min = seqCanvas.cursorY;\r
295       }\r
296       else\r
297       {\r
298         sg.setEndRes(seqCanvas.cursorX);\r
299         if (sg.getStartRes() > seqCanvas.cursorX)\r
300         {\r
301           sg.setStartRes(seqCanvas.cursorX);\r
302         }\r
303 \r
304         max = seqCanvas.cursorY + 1;\r
305       }\r
306 \r
307       if (min > max)\r
308       {\r
309         // Only the user can do this\r
310         av.setSelectionGroup(null);\r
311       }\r
312       else\r
313       {\r
314         // Now add any sequences between min and max\r
315         sg.clear();\r
316         for (int i = min; i < max; i++)\r
317         {\r
318           sg.addSequence(av.getAlignment().getSequenceAt(i), false);\r
319         }\r
320       }\r
321     }\r
322 \r
323     if (av.getSelectionGroup() == null)\r
324     {\r
325       SequenceGroup sg = new SequenceGroup();\r
326       sg.setStartRes(seqCanvas.cursorX);\r
327       sg.setEndRes(seqCanvas.cursorX);\r
328       sg.addSequence(sequence, false);\r
329       av.setSelectionGroup(sg);\r
330     }\r
331     ap.paintAlignment(false);\r
332     av.sendSelection();\r
333   }\r
334 \r
335   void insertGapAtCursor(boolean group)\r
336   {\r
337     groupEditing = group;\r
338     startseq = seqCanvas.cursorY;\r
339     lastres = seqCanvas.cursorX;\r
340     editSequence(true, seqCanvas.cursorX + getKeyboardNo1());\r
341     endEditing();\r
342   }\r
343 \r
344   void deleteGapAtCursor(boolean group)\r
345   {\r
346     groupEditing = group;\r
347     startseq = seqCanvas.cursorY;\r
348     lastres = seqCanvas.cursorX + getKeyboardNo1();\r
349     editSequence(false, seqCanvas.cursorX);\r
350     endEditing();\r
351   }\r
352 \r
353   void numberPressed(char value)\r
354   {\r
355     if (keyboardNo1 == null)\r
356     {\r
357       keyboardNo1 = new StringBuffer();\r
358     }\r
359 \r
360     if (keyboardNo2 != null)\r
361     {\r
362       keyboardNo2.append(value);\r
363     }\r
364     else\r
365     {\r
366       keyboardNo1.append(value);\r
367     }\r
368   }\r
369 \r
370   int getKeyboardNo1()\r
371   {\r
372     try\r
373     {\r
374       if (keyboardNo1 != null)\r
375       {\r
376         int value = Integer.parseInt(keyboardNo1.toString());\r
377         keyboardNo1 = null;\r
378         return value;\r
379       }\r
380     } catch (Exception x)\r
381     {\r
382     }\r
383     keyboardNo1 = null;\r
384     return 1;\r
385   }\r
386 \r
387   int getKeyboardNo2()\r
388   {\r
389     try\r
390     {\r
391       if (keyboardNo2 != null)\r
392       {\r
393         int value = Integer.parseInt(keyboardNo2.toString());\r
394         keyboardNo2 = null;\r
395         return value;\r
396       }\r
397     } catch (Exception x)\r
398     {\r
399     }\r
400     keyboardNo2 = null;\r
401     return 1;\r
402   }\r
403 \r
404   /**\r
405    * Set status message in alignment panel\r
406    * \r
407    * @param sequence\r
408    *          aligned sequence object\r
409    * @param res\r
410    *          alignment column\r
411    * @param seq\r
412    *          index of sequence in alignment\r
413    * @return position of res in sequence\r
414    */\r
415   void setStatusMessage(SequenceI sequence, int res, int seq)\r
416   {\r
417     // TODO remove duplication of identical gui method\r
418     StringBuilder text = new StringBuilder(32);\r
419     String seqno = seq == -1 ? "" : " " + (seq + 1);\r
420     text.append("Sequence" + seqno + " ID: " + sequence.getName());\r
421 \r
422     String residue = null;\r
423     /*\r
424      * Try to translate the display character to residue name (null for gap).\r
425      */\r
426     final String displayChar = String.valueOf(sequence.getCharAt(res));\r
427     if (av.getAlignment().isNucleotide())\r
428     {\r
429       residue = ResidueProperties.nucleotideName.get(displayChar);\r
430       if (residue != null)\r
431       {\r
432         text.append(" Nucleotide: ").append(residue);\r
433       }\r
434     }\r
435     else\r
436     {\r
437       residue = "X".equalsIgnoreCase(displayChar) ? "X"\r
438               : ResidueProperties.aa2Triplet.get(displayChar);\r
439       if (residue != null)\r
440       {\r
441         text.append(" Residue: ").append(residue);\r
442       }\r
443     }\r
444 \r
445     int pos = -1;\r
446     if (residue != null)\r
447     {\r
448       pos = sequence.findPosition(res);\r
449       text.append(" (").append(Integer.toString(pos)).append(")");\r
450     }\r
451     // Object obj = null;\r
452     // if (av.getAlignment().isNucleotide())\r
453     // {\r
454     // obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)\r
455     // + "");\r
456     // if (obj != null)\r
457     // {\r
458     // text.append(" Nucleotide: ");\r
459     // }\r
460     // }\r
461     // else\r
462     // {\r
463     // obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");\r
464     // if (obj != null)\r
465     // {\r
466     // text.append("  Residue: ");\r
467     // }\r
468     // }\r
469     //\r
470     // if (obj != null)\r
471     // {\r
472     //\r
473     // if (obj != "")\r
474     // {\r
475     // text.append(obj + " (" + sequence.findPosition(res) + ")");\r
476     // }\r
477     // }\r
478 \r
479     ap.alignFrame.statusBar.setText(text.toString());\r
480 \r
481   }\r
482 \r
483   /**\r
484    * Set the status bar message to highlight the first matched position in\r
485    * search results.\r
486    * \r
487    * @param results\r
488    */\r
489   private void setStatusMessage(SearchResults results)\r
490   {\r
491     List<Match> matches = results.getResults();\r
492     if (!matches.isEmpty())\r
493     {\r
494       Match m = matches.get(0);\r
495       SequenceI seq = m.getSequence();\r
496       int sequenceIndex = this.av.getAlignment().findIndex(seq);\r
497 \r
498       /*\r
499        * Convert position in sequence (base 1) to sequence character array index\r
500        * (base 0)\r
501        */\r
502       int start = m.getStart() - 1;\r
503       setStatusMessage(seq, start, sequenceIndex);\r
504     }\r
505   }\r
506 \r
507   public void mousePressed(MouseEvent evt)\r
508   {\r
509     lastMousePress = evt.getPoint();\r
510 \r
511     // For now, ignore the mouseWheel font resizing on Macs\r
512     // As the Button2_mask always seems to be true\r
513     if ((evt.getModifiers() & InputEvent.BUTTON2_MASK) == InputEvent.BUTTON2_MASK\r
514             && !av.MAC)\r
515     {\r
516       mouseWheelPressed = true;\r
517       return;\r
518     }\r
519 \r
520     if (evt.isShiftDown() || evt.isControlDown() || evt.isAltDown())\r
521     {\r
522       if (evt.isControlDown() || evt.isAltDown())\r
523       {\r
524         groupEditing = true;\r
525       }\r
526       editingSeqs = true;\r
527     }\r
528     else\r
529     {\r
530       doMousePressedDefineMode(evt);\r
531       return;\r
532     }\r
533 \r
534     int seq = findSeq(evt);\r
535     int res = findRes(evt);\r
536 \r
537     if (seq < 0 || res < 0)\r
538     {\r
539       return;\r
540     }\r
541 \r
542     if ((seq < av.getAlignment().getHeight())\r
543             && (res < av.getAlignment().getSequenceAt(seq).getLength()))\r
544     {\r
545       startseq = seq;\r
546       lastres = res;\r
547     }\r
548     else\r
549     {\r
550       startseq = -1;\r
551       lastres = -1;\r
552     }\r
553 \r
554     return;\r
555   }\r
556 \r
557   public void mouseClicked(MouseEvent evt)\r
558   {\r
559     SequenceI sequence = av.getAlignment().getSequenceAt(findSeq(evt));\r
560     if (evt.getClickCount() > 1)\r
561     {\r
562       if (av.getSelectionGroup() != null\r
563               && av.getSelectionGroup().getSize() == 1\r
564               && av.getSelectionGroup().getEndRes()\r
565                       - av.getSelectionGroup().getStartRes() < 2)\r
566       {\r
567         av.setSelectionGroup(null);\r
568       }\r
569 \r
570       SequenceFeature[] features = findFeaturesAtRes(sequence,\r
571               sequence.findPosition(findRes(evt)));\r
572 \r
573       if (features != null && features.length > 0)\r
574       {\r
575         SearchResults highlight = new SearchResults();\r
576         highlight.addResult(sequence, features[0].getBegin(),\r
577                 features[0].getEnd());\r
578         seqCanvas.highlightSearchResults(highlight);\r
579       }\r
580       if (features != null && features.length > 0)\r
581       {\r
582         seqCanvas.getFeatureRenderer().amendFeatures(new SequenceI[]\r
583         { sequence }, features, false, ap);\r
584 \r
585         seqCanvas.highlightSearchResults(null);\r
586       }\r
587     }\r
588   }\r
589 \r
590   public void mouseReleased(MouseEvent evt)\r
591   {\r
592     mouseDragging = false;\r
593     mouseWheelPressed = false;\r
594     ap.paintAlignment(true);\r
595 \r
596     if (!editingSeqs)\r
597     {\r
598       doMouseReleasedDefineMode(evt);\r
599       return;\r
600     }\r
601 \r
602     endEditing();\r
603 \r
604   }\r
605 \r
606   int startWrapBlock = -1;\r
607 \r
608   int wrappedBlock = -1;\r
609 \r
610   int findRes(MouseEvent evt)\r
611   {\r
612     int res = 0;\r
613     int x = evt.getX();\r
614 \r
615     if (av.getWrapAlignment())\r
616     {\r
617 \r
618       int hgap = av.getCharHeight();\r
619       if (av.getScaleAboveWrapped())\r
620       {\r
621         hgap += av.getCharHeight();\r
622       }\r
623 \r
624       int cHeight = av.getAlignment().getHeight() * av.getCharHeight()\r
625               + hgap\r
626               + seqCanvas.getAnnotationHeight();\r
627 \r
628       int y = evt.getY();\r
629       y -= hgap;\r
630       x -= seqCanvas.LABEL_WEST;\r
631 \r
632       int cwidth = seqCanvas.getWrappedCanvasWidth(getSize().width);\r
633       if (cwidth < 1)\r
634       {\r
635         return 0;\r
636       }\r
637 \r
638       wrappedBlock = y / cHeight;\r
639       wrappedBlock += av.getStartRes() / cwidth;\r
640 \r
641       res = wrappedBlock * cwidth + x / av.getCharWidth();\r
642 \r
643     }\r
644     else\r
645     {\r
646       res = (x / av.getCharWidth()) + av.getStartRes();\r
647     }\r
648 \r
649     if (av.hasHiddenColumns())\r
650     {\r
651       res = av.getColumnSelection().adjustForHiddenColumns(res);\r
652     }\r
653 \r
654     return res;\r
655 \r
656   }\r
657 \r
658   int findSeq(MouseEvent evt)\r
659   {\r
660     final int sqnum = findAlRow(evt);\r
661     return (sqnum < 0) ? 0 : sqnum;\r
662   }\r
663 \r
664   /**\r
665    * \r
666    * @param evt\r
667    * @return row in alignment that was selected (or -1 for column selection)\r
668    */\r
669   private int findAlRow(MouseEvent evt)\r
670   {\r
671     int seq = 0;\r
672     int y = evt.getY();\r
673 \r
674     if (av.getWrapAlignment())\r
675     {\r
676       int hgap = av.getCharHeight();\r
677       if (av.getScaleAboveWrapped())\r
678       {\r
679         hgap += av.getCharHeight();\r
680       }\r
681 \r
682       int cHeight = av.getAlignment().getHeight() * av.getCharHeight()\r
683               + hgap\r
684               + seqCanvas.getAnnotationHeight();\r
685 \r
686       y -= hgap;\r
687 \r
688       seq = Math.min((y % cHeight) / av.getCharHeight(), av.getAlignment()\r
689               .getHeight() - 1);\r
690       if (seq < 0)\r
691       {\r
692         seq = -1;\r
693       }\r
694     }\r
695     else\r
696     {\r
697       seq = Math.min((y / av.getCharHeight()) + av.getStartSeq(), av\r
698               .getAlignment().getHeight() - 1);\r
699       if (seq < 0)\r
700       {\r
701         seq = -1;\r
702       }\r
703     }\r
704 \r
705     return seq;\r
706   }\r
707 \r
708   public void doMousePressed(MouseEvent evt)\r
709   {\r
710 \r
711     int seq = findSeq(evt);\r
712     int res = findRes(evt);\r
713 \r
714     if (seq < av.getAlignment().getHeight()\r
715             && res < av.getAlignment().getSequenceAt(seq).getLength())\r
716     {\r
717       // char resstr = align.getSequenceAt(seq).getSequence().charAt(res);\r
718       // Find the residue's position in the sequence (res is the position\r
719       // in the alignment\r
720 \r
721       startseq = seq;\r
722       lastres = res;\r
723     }\r
724     else\r
725     {\r
726       startseq = -1;\r
727       lastres = -1;\r
728     }\r
729 \r
730     return;\r
731   }\r
732 \r
733   String lastMessage;\r
734 \r
735   public void mouseOverSequence(SequenceI sequence, int index, int pos)\r
736   {\r
737     String tmp = sequence.hashCode() + index + "";\r
738     if (lastMessage == null || !lastMessage.equals(tmp))\r
739     {\r
740       ssm.mouseOverSequence(sequence, index, pos, av);\r
741     }\r
742 \r
743     lastMessage = tmp;\r
744   }\r
745 \r
746   public void highlightSequence(SearchResults results)\r
747   {\r
748     if (av.isFollowHighlight())\r
749     {\r
750       if (ap.scrollToPosition(results, true))\r
751       {\r
752         ap.alignFrame.repaint();\r
753       }\r
754     }\r
755     setStatusMessage(results);\r
756     seqCanvas.highlightSearchResults(results);\r
757 \r
758   }\r
759 \r
760   @Override\r
761   public VamsasSource getVamsasSource()\r
762   {\r
763     return this.ap == null ? null : this.ap.av;\r
764   }\r
765 \r
766   public void updateColours(SequenceI seq, int index)\r
767   {\r
768     System.out.println("update the seqPanel colours");\r
769     // repaint();\r
770   }\r
771 \r
772   public void mouseMoved(MouseEvent evt)\r
773   {\r
774     int res = findRes(evt);\r
775     int seq = findSeq(evt);\r
776 \r
777     if (seq >= av.getAlignment().getHeight() || seq < 0 || res < 0)\r
778     {\r
779       if (tooltip != null)\r
780       {\r
781         tooltip.setTip("");\r
782       }\r
783       return;\r
784     }\r
785 \r
786     SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
787     if (res > sequence.getLength())\r
788     {\r
789       if (tooltip != null)\r
790       {\r
791         tooltip.setTip("");\r
792       }\r
793       return;\r
794     }\r
795 \r
796     int respos = sequence.findPosition(res);\r
797     if (ssm != null)\r
798     {\r
799       mouseOverSequence(sequence, res, respos);\r
800     }\r
801 \r
802     StringBuilder text = new StringBuilder();\r
803     text.append("Sequence ").append(Integer.toString(seq + 1))\r
804             .append(" ID: ").append(sequence.getName());\r
805 \r
806     String obj = null;\r
807     final String ch = String.valueOf(sequence.getCharAt(res));\r
808     if (av.getAlignment().isNucleotide())\r
809     {\r
810       obj = ResidueProperties.nucleotideName.get(ch);\r
811       if (obj != null)\r
812       {\r
813         text.append(" Nucleotide: ").append(obj);\r
814       }\r
815     }\r
816     else\r
817     {\r
818       obj = "X".equalsIgnoreCase(ch) ? "X"\r
819               : ResidueProperties.aa2Triplet.get(ch);\r
820       if (obj != null)\r
821       {\r
822         text.append(" Residue: ").append(obj);\r
823       }\r
824     }\r
825 \r
826     if (obj != null)\r
827     {\r
828       text.append(" (").append(Integer.toString(respos)).append(")");\r
829     }\r
830 \r
831     ap.alignFrame.statusBar.setText(text.toString());\r
832 \r
833     StringBuilder tooltipText = new StringBuilder();\r
834     SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence);\r
835     if (groups != null)\r
836     {\r
837       for (int g = 0; g < groups.length; g++)\r
838       {\r
839         if (groups[g].getStartRes() <= res && groups[g].getEndRes() >= res)\r
840         {\r
841           if (!groups[g].getName().startsWith("JTreeGroup")\r
842                   && !groups[g].getName().startsWith("JGroup"))\r
843           {\r
844             tooltipText.append(groups[g].getName()).append(" ");\r
845           }\r
846           if (groups[g].getDescription() != null)\r
847           {\r
848             tooltipText.append(groups[g].getDescription());\r
849           }\r
850           tooltipText.append("\n");\r
851         }\r
852       }\r
853     }\r
854 \r
855     // use aa to see if the mouse pointer is on a\r
856     SequenceFeature[] allFeatures = findFeaturesAtRes(sequence,\r
857             sequence.findPosition(res));\r
858 \r
859     int index = 0;\r
860     while (index < allFeatures.length)\r
861     {\r
862       SequenceFeature sf = allFeatures[index];\r
863 \r
864       tooltipText.append(sf.getType() + " " + sf.begin + ":" + sf.end);\r
865 \r
866       if (sf.getDescription() != null)\r
867       {\r
868         tooltipText.append(" " + sf.getDescription());\r
869       }\r
870 \r
871       if (sf.getValue("status") != null)\r
872       {\r
873         String status = sf.getValue("status").toString();\r
874         if (status.length() > 0)\r
875         {\r
876           tooltipText.append(" (" + sf.getValue("status") + ")");\r
877         }\r
878       }\r
879       tooltipText.append("\n");\r
880 \r
881       index++;\r
882     }\r
883 \r
884     if (tooltip == null)\r
885     {\r
886       tooltip = new Tooltip(tooltipText.toString(), seqCanvas);\r
887     }\r
888     else\r
889     {\r
890       tooltip.setTip(tooltipText.toString());\r
891     }\r
892   }\r
893 \r
894   SequenceFeature[] findFeaturesAtRes(SequenceI sequence, int res)\r
895   {\r
896     Vector tmp = new Vector();\r
897     SequenceFeature[] features = sequence.getSequenceFeatures();\r
898     if (features != null)\r
899     {\r
900       for (int i = 0; i < features.length; i++)\r
901       {\r
902         if (av.getFeaturesDisplayed() == null\r
903                 || !av.getFeaturesDisplayed().isVisible(features[i].getType()))\r
904         {\r
905           continue;\r
906         }\r
907 \r
908         if (features[i].featureGroup != null\r
909                 && !seqCanvas.fr.checkGroupVisibility(features[i].featureGroup,false))\r
910         {\r
911           continue;\r
912         }\r
913 \r
914         if ((features[i].getBegin() <= res)\r
915                 && (features[i].getEnd() >= res))\r
916         {\r
917           tmp.addElement(features[i]);\r
918         }\r
919       }\r
920     }\r
921 \r
922     features = new SequenceFeature[tmp.size()];\r
923     tmp.copyInto(features);\r
924 \r
925     return features;\r
926   }\r
927 \r
928   Tooltip tooltip;\r
929 \r
930   public void mouseDragged(MouseEvent evt)\r
931   {\r
932     if (mouseWheelPressed)\r
933     {\r
934       int oldWidth = av.getCharWidth();\r
935 \r
936       // Which is bigger, left-right or up-down?\r
937       if (Math.abs(evt.getY() - lastMousePress.y) > Math.abs(evt.getX()\r
938               - lastMousePress.x))\r
939       {\r
940         int fontSize = av.font.getSize();\r
941 \r
942         if (evt.getY() < lastMousePress.y && av.getCharHeight() > 1)\r
943         {\r
944           fontSize--;\r
945         }\r
946         else if (evt.getY() > lastMousePress.y)\r
947         {\r
948           fontSize++;\r
949         }\r
950 \r
951         if (fontSize < 1)\r
952         {\r
953           fontSize = 1;\r
954         }\r
955 \r
956         av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));\r
957         av.setCharWidth(oldWidth);\r
958       }\r
959       else\r
960       {\r
961         if (evt.getX() < lastMousePress.x && av.getCharWidth() > 1)\r
962         {\r
963           av.setCharWidth(av.getCharWidth() - 1);\r
964         }\r
965         else if (evt.getX() > lastMousePress.x)\r
966         {\r
967           av.setCharWidth(av.getCharWidth() + 1);\r
968         }\r
969 \r
970         if (av.getCharWidth() < 1)\r
971         {\r
972           av.setCharWidth(1);\r
973         }\r
974       }\r
975 \r
976       ap.fontChanged();\r
977 \r
978       FontMetrics fm = getFontMetrics(av.getFont());\r
979       av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();\r
980 \r
981       lastMousePress = evt.getPoint();\r
982 \r
983       ap.paintAlignment(false);\r
984       ap.annotationPanel.image = null;\r
985       return;\r
986     }\r
987 \r
988     if (!editingSeqs)\r
989     {\r
990       doMouseDraggedDefineMode(evt);\r
991       return;\r
992     }\r
993 \r
994     int res = findRes(evt);\r
995 \r
996     if (res < 0)\r
997     {\r
998       res = 0;\r
999     }\r
1000 \r
1001     if ((lastres == -1) || (lastres == res))\r
1002     {\r
1003       return;\r
1004     }\r
1005 \r
1006     if ((res < av.getAlignment().getWidth()) && (res < lastres))\r
1007     {\r
1008       // dragLeft, delete gap\r
1009       editSequence(false, res);\r
1010     }\r
1011     else\r
1012     {\r
1013       editSequence(true, res);\r
1014     }\r
1015 \r
1016     mouseDragging = true;\r
1017     if (scrollThread != null)\r
1018     {\r
1019       scrollThread.setEvent(evt);\r
1020     }\r
1021 \r
1022   }\r
1023 \r
1024   synchronized void editSequence(boolean insertGap, int startres)\r
1025   {\r
1026     int fixedLeft = -1;\r
1027     int fixedRight = -1;\r
1028     boolean fixedColumns = false;\r
1029     SequenceGroup sg = av.getSelectionGroup();\r
1030 \r
1031     SequenceI seq = av.getAlignment().getSequenceAt(startseq);\r
1032 \r
1033     if (!groupEditing && av.hasHiddenRows())\r
1034     {\r
1035       if (av.isHiddenRepSequence(seq))\r
1036       {\r
1037         sg = av.getRepresentedSequences(seq);\r
1038         groupEditing = true;\r
1039       }\r
1040     }\r
1041 \r
1042     StringBuffer message = new StringBuffer();\r
1043     if (groupEditing)\r
1044     {\r
1045       message.append(MessageManager.getString("action.edit_group")).append(\r
1046               ":");\r
1047       if (editCommand == null)\r
1048       {\r
1049         editCommand = new EditCommand(\r
1050                 MessageManager.getString("action.edit_group"));\r
1051       }\r
1052     }\r
1053     else\r
1054     {\r
1055       message.append(MessageManager.getString("label.edit_sequence"))\r
1056               .append(" " + seq.getName());\r
1057       String label = seq.getName();\r
1058       if (label.length() > 10)\r
1059       {\r
1060         label = label.substring(0, 10);\r
1061       }\r
1062       if (editCommand == null)\r
1063       {\r
1064         editCommand = new EditCommand(MessageManager.formatMessage(\r
1065                 "label.edit_params", new String[]\r
1066                 { label }));\r
1067       }\r
1068     }\r
1069 \r
1070     if (insertGap)\r
1071     {\r
1072       message.append(" insert ");\r
1073     }\r
1074     else\r
1075     {\r
1076       message.append(" delete ");\r
1077     }\r
1078 \r
1079     message.append(Math.abs(startres - lastres) + " gaps.");\r
1080     ap.alignFrame.statusBar.setText(message.toString());\r
1081 \r
1082     // Are we editing within a selection group?\r
1083     if (groupEditing\r
1084             || (sg != null && sg.getSequences(av.getHiddenRepSequences())\r
1085                     .contains(seq)))\r
1086     {\r
1087       fixedColumns = true;\r
1088 \r
1089       // sg might be null as the user may only see 1 sequence,\r
1090       // but the sequence represents a group\r
1091       if (sg == null)\r
1092       {\r
1093         if (!av.isHiddenRepSequence(seq))\r
1094         {\r
1095           endEditing();\r
1096           return;\r
1097         }\r
1098 \r
1099         sg = av.getRepresentedSequences(seq);\r
1100       }\r
1101 \r
1102       fixedLeft = sg.getStartRes();\r
1103       fixedRight = sg.getEndRes();\r
1104 \r
1105       if ((startres < fixedLeft && lastres >= fixedLeft)\r
1106               || (startres >= fixedLeft && lastres < fixedLeft)\r
1107               || (startres > fixedRight && lastres <= fixedRight)\r
1108               || (startres <= fixedRight && lastres > fixedRight))\r
1109       {\r
1110         endEditing();\r
1111         return;\r
1112       }\r
1113 \r
1114       if (fixedLeft > startres)\r
1115       {\r
1116         fixedRight = fixedLeft - 1;\r
1117         fixedLeft = 0;\r
1118       }\r
1119       else if (fixedRight < startres)\r
1120       {\r
1121         fixedLeft = fixedRight;\r
1122         fixedRight = -1;\r
1123       }\r
1124     }\r
1125 \r
1126     if (av.hasHiddenColumns())\r
1127     {\r
1128       fixedColumns = true;\r
1129       int y1 = av.getColumnSelection().getHiddenBoundaryLeft(startres);\r
1130       int y2 = av.getColumnSelection().getHiddenBoundaryRight(startres);\r
1131 \r
1132       if ((insertGap && startres > y1 && lastres < y1)\r
1133               || (!insertGap && startres < y2 && lastres > y2))\r
1134       {\r
1135         endEditing();\r
1136         return;\r
1137       }\r
1138 \r
1139       // System.out.print(y1+" "+y2+" "+fixedLeft+" "+fixedRight+"~~");\r
1140       // Selection spans a hidden region\r
1141       if (fixedLeft < y1 && (fixedRight > y2 || fixedRight == -1))\r
1142       {\r
1143         if (startres >= y2)\r
1144         {\r
1145           fixedLeft = y2;\r
1146         }\r
1147         else\r
1148         {\r
1149           fixedRight = y2 - 1;\r
1150         }\r
1151       }\r
1152     }\r
1153 \r
1154     if (groupEditing)\r
1155     {\r
1156       SequenceI[] groupSeqs = sg.getSequences(av.getHiddenRepSequences())\r
1157               .toArray(new SequenceI[0]);\r
1158 \r
1159       // drag to right\r
1160       if (insertGap)\r
1161       {\r
1162         // If the user has selected the whole sequence, and is dragging to\r
1163         // the right, we can still extend the alignment and selectionGroup\r
1164         if (sg.getStartRes() == 0 && sg.getEndRes() == fixedRight\r
1165                 && sg.getEndRes() == av.getAlignment().getWidth() - 1)\r
1166         {\r
1167           sg.setEndRes(av.getAlignment().getWidth() + startres - lastres);\r
1168           fixedRight = sg.getEndRes();\r
1169         }\r
1170 \r
1171         // Is it valid with fixed columns??\r
1172         // Find the next gap before the end\r
1173         // of the visible region boundary\r
1174         boolean blank = false;\r
1175         for (fixedRight = fixedRight; fixedRight > lastres; fixedRight--)\r
1176         {\r
1177           blank = true;\r
1178 \r
1179           for (SequenceI gs : groupSeqs)\r
1180           {\r
1181             for (int j = 0; j < startres - lastres; j++)\r
1182             {\r
1183               if (!Comparison.isGap(gs.getCharAt(fixedRight\r
1184                       - j)))\r
1185               {\r
1186                 blank = false;\r
1187                 break;\r
1188               }\r
1189             }\r
1190           }\r
1191           if (blank)\r
1192           {\r
1193             break;\r
1194           }\r
1195         }\r
1196 \r
1197         if (!blank)\r
1198         {\r
1199           if (sg.getSize() == av.getAlignment().getHeight())\r
1200           {\r
1201             if ((av.hasHiddenColumns() && startres < av\r
1202                     .getColumnSelection().getHiddenBoundaryRight(startres)))\r
1203             {\r
1204               endEditing();\r
1205               return;\r
1206             }\r
1207 \r
1208             int alWidth = av.getAlignment().getWidth();\r
1209             if (av.hasHiddenRows())\r
1210             {\r
1211               int hwidth = av.getAlignment().getHiddenSequences()\r
1212                       .getWidth();\r
1213               if (hwidth > alWidth)\r
1214               {\r
1215                 alWidth = hwidth;\r
1216               }\r
1217             }\r
1218             // We can still insert gaps if the selectionGroup\r
1219             // contains all the sequences\r
1220             sg.setEndRes(sg.getEndRes() + startres - lastres);\r
1221             fixedRight = alWidth + startres - lastres;\r
1222           }\r
1223           else\r
1224           {\r
1225             endEditing();\r
1226             return;\r
1227           }\r
1228         }\r
1229       }\r
1230 \r
1231       // drag to left\r
1232       else if (!insertGap)\r
1233       {\r
1234         // / Are we able to delete?\r
1235         // ie are all columns blank?\r
1236 \r
1237         for (SequenceI gs : groupSeqs)\r
1238         {\r
1239           for (int j = startres; j < lastres; j++)\r
1240           {\r
1241             if (gs.getLength() <= j)\r
1242             {\r
1243               continue;\r
1244             }\r
1245 \r
1246             if (!Comparison.isGap(gs.getCharAt(j)))\r
1247             {\r
1248               // Not a gap, block edit not valid\r
1249               endEditing();\r
1250               return;\r
1251             }\r
1252           }\r
1253         }\r
1254       }\r
1255 \r
1256       if (insertGap)\r
1257       {\r
1258         // dragging to the right\r
1259         if (fixedColumns && fixedRight != -1)\r
1260         {\r
1261           for (int j = lastres; j < startres; j++)\r
1262           {\r
1263             insertChar(j, groupSeqs, fixedRight);\r
1264           }\r
1265         }\r
1266         else\r
1267         {\r
1268           editCommand.appendEdit(Action.INSERT_GAP, groupSeqs, startres,\r
1269                   startres - lastres, av.getAlignment(), true);\r
1270         }\r
1271       }\r
1272       else\r
1273       {\r
1274         // dragging to the left\r
1275         if (fixedColumns && fixedRight != -1)\r
1276         {\r
1277           for (int j = lastres; j > startres; j--)\r
1278           {\r
1279             deleteChar(startres, groupSeqs, fixedRight);\r
1280           }\r
1281         }\r
1282         else\r
1283         {\r
1284           editCommand.appendEdit(Action.DELETE_GAP, groupSeqs, startres,\r
1285                   lastres - startres, av.getAlignment(), true);\r
1286         }\r
1287 \r
1288       }\r
1289     }\r
1290     else\r
1291     // ///Editing a single sequence///////////\r
1292     {\r
1293       if (insertGap)\r
1294       {\r
1295         // dragging to the right\r
1296         if (fixedColumns && fixedRight != -1)\r
1297         {\r
1298           for (int j = lastres; j < startres; j++)\r
1299           {\r
1300             insertChar(j, new SequenceI[]\r
1301             { seq }, fixedRight);\r
1302           }\r
1303         }\r
1304         else\r
1305         {\r
1306           editCommand.appendEdit(Action.INSERT_GAP, new SequenceI[]\r
1307           { seq }, lastres, startres - lastres, av.getAlignment(), true);\r
1308         }\r
1309       }\r
1310       else\r
1311       {\r
1312         // dragging to the left\r
1313         if (fixedColumns && fixedRight != -1)\r
1314         {\r
1315           for (int j = lastres; j > startres; j--)\r
1316           {\r
1317             if (!Comparison.isGap(seq.getCharAt(startres)))\r
1318             {\r
1319               endEditing();\r
1320               break;\r
1321             }\r
1322             deleteChar(startres, new SequenceI[]\r
1323             { seq }, fixedRight);\r
1324           }\r
1325         }\r
1326         else\r
1327         {\r
1328           // could be a keyboard edit trying to delete none gaps\r
1329           int max = 0;\r
1330           for (int m = startres; m < lastres; m++)\r
1331           {\r
1332             if (!Comparison.isGap(seq.getCharAt(m)))\r
1333             {\r
1334               break;\r
1335             }\r
1336             max++;\r
1337           }\r
1338 \r
1339           if (max > 0)\r
1340           {\r
1341             editCommand.appendEdit(Action.DELETE_GAP, new SequenceI[]\r
1342             { seq }, startres, max, av.getAlignment(), true);\r
1343           }\r
1344         }\r
1345       }\r
1346     }\r
1347 \r
1348     lastres = startres;\r
1349     seqCanvas.repaint();\r
1350   }\r
1351 \r
1352   void insertChar(int j, SequenceI[] seq, int fixedColumn)\r
1353   {\r
1354     int blankColumn = fixedColumn;\r
1355     for (int s = 0; s < seq.length; s++)\r
1356     {\r
1357       // Find the next gap before the end of the visible region boundary\r
1358       // If lastCol > j, theres a boundary after the gap insertion\r
1359 \r
1360       for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)\r
1361       {\r
1362         if (Comparison.isGap(seq[s].getCharAt(blankColumn)))\r
1363         {\r
1364           // Theres a space, so break and insert the gap\r
1365           break;\r
1366         }\r
1367       }\r
1368 \r
1369       if (blankColumn <= j)\r
1370       {\r
1371         blankColumn = fixedColumn;\r
1372         endEditing();\r
1373         return;\r
1374       }\r
1375     }\r
1376 \r
1377     editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1,\r
1378             av.getAlignment(), true);\r
1379 \r
1380     editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1, av.getAlignment(),\r
1381             true);\r
1382 \r
1383   }\r
1384 \r
1385   void deleteChar(int j, SequenceI[] seq, int fixedColumn)\r
1386   {\r
1387 \r
1388     editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1, av.getAlignment(),\r
1389             true);\r
1390 \r
1391     editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1,\r
1392             av.getAlignment(), true);\r
1393   }\r
1394 \r
1395   // ////////////////////////////////////////\r
1396   // ///Everything below this is for defining the boundary of the rubberband\r
1397   // ////////////////////////////////////////\r
1398   public void doMousePressedDefineMode(MouseEvent evt)\r
1399   {\r
1400     if (scrollThread != null)\r
1401     {\r
1402       scrollThread.running = false;\r
1403       scrollThread = null;\r
1404     }\r
1405 \r
1406     int res = findRes(evt);\r
1407     int seq = findSeq(evt);\r
1408     oldSeq = seq;\r
1409     startWrapBlock = wrappedBlock;\r
1410 \r
1411     if (seq == -1)\r
1412     {\r
1413       return;\r
1414     }\r
1415 \r
1416     SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
1417 \r
1418     if (sequence == null || res > sequence.getLength())\r
1419     {\r
1420       return;\r
1421     }\r
1422 \r
1423     stretchGroup = av.getSelectionGroup();\r
1424 \r
1425     if (stretchGroup == null)\r
1426     {\r
1427       stretchGroup = av.getAlignment().findGroup(sequence);\r
1428       if (stretchGroup != null && res > stretchGroup.getStartRes()\r
1429               && res < stretchGroup.getEndRes())\r
1430       {\r
1431         av.setSelectionGroup(stretchGroup);\r
1432       }\r
1433       else\r
1434       {\r
1435         stretchGroup = null;\r
1436       }\r
1437     }\r
1438 \r
1439     else if (!stretchGroup.getSequences(null).contains(sequence)\r
1440             || stretchGroup.getStartRes() > res\r
1441             || stretchGroup.getEndRes() < res)\r
1442     {\r
1443       stretchGroup = null;\r
1444 \r
1445       SequenceGroup[] allGroups = av.getAlignment().findAllGroups(sequence);\r
1446 \r
1447       if (allGroups != null)\r
1448       {\r
1449         for (int i = 0; i < allGroups.length; i++)\r
1450         {\r
1451           if (allGroups[i].getStartRes() <= res\r
1452                   && allGroups[i].getEndRes() >= res)\r
1453           {\r
1454             stretchGroup = allGroups[i];\r
1455             break;\r
1456           }\r
1457         }\r
1458       }\r
1459       av.setSelectionGroup(stretchGroup);\r
1460     }\r
1461 \r
1462     // DETECT RIGHT MOUSE BUTTON IN AWT\r
1463     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)\r
1464     {\r
1465       SequenceFeature[] allFeatures = findFeaturesAtRes(sequence,\r
1466               sequence.findPosition(res));\r
1467 \r
1468       Vector<String> links = null;\r
1469       if (allFeatures != null)\r
1470       {\r
1471         for (int i = 0; i < allFeatures.length; i++)\r
1472         {\r
1473           if (allFeatures[i].links != null)\r
1474           {\r
1475             if (links == null)\r
1476             {\r
1477               links = new Vector<String>();\r
1478             }\r
1479             for (int j = 0; j < allFeatures[i].links.size(); j++)\r
1480             {\r
1481               links.addElement(allFeatures[i].links.elementAt(j));\r
1482             }\r
1483           }\r
1484         }\r
1485       }\r
1486       APopupMenu popup = new APopupMenu(ap, null, links);\r
1487       this.add(popup);\r
1488       popup.show(this, evt.getX(), evt.getY());\r
1489       return;\r
1490     }\r
1491 \r
1492     if (av.cursorMode)\r
1493     {\r
1494       seqCanvas.cursorX = findRes(evt);\r
1495       seqCanvas.cursorY = findSeq(evt);\r
1496       seqCanvas.repaint();\r
1497       return;\r
1498     }\r
1499 \r
1500     // Only if left mouse button do we want to change group sizes\r
1501 \r
1502     if (stretchGroup == null)\r
1503     {\r
1504       // define a new group here\r
1505       SequenceGroup sg = new SequenceGroup();\r
1506       sg.setStartRes(res);\r
1507       sg.setEndRes(res);\r
1508       sg.addSequence(sequence, false);\r
1509       av.setSelectionGroup(sg);\r
1510       stretchGroup = sg;\r
1511 \r
1512       if (av.getConservationSelected())\r
1513       {\r
1514         SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(),\r
1515                 "Background");\r
1516       }\r
1517       if (av.getAbovePIDThreshold())\r
1518       {\r
1519         SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),\r
1520                 "Background");\r
1521       }\r
1522 \r
1523     }\r
1524   }\r
1525 \r
1526   public void doMouseReleasedDefineMode(MouseEvent evt)\r
1527   {\r
1528     if (stretchGroup == null)\r
1529     {\r
1530       return;\r
1531     }\r
1532 \r
1533     stretchGroup.recalcConservation(); // always do this - annotation has own\r
1534                                        // state\r
1535     if (stretchGroup.cs != null)\r
1536     {\r
1537       stretchGroup.cs.alignmentChanged(stretchGroup,\r
1538               av.getHiddenRepSequences());\r
1539 \r
1540       if (stretchGroup.cs.conservationApplied())\r
1541       {\r
1542         SliderPanel.setConservationSlider(ap, stretchGroup.cs,\r
1543                 stretchGroup.getName());\r
1544       }\r
1545       else\r
1546       {\r
1547         SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,\r
1548                 stretchGroup.getName());\r
1549       }\r
1550     }\r
1551     changeEndRes = false;\r
1552     changeStartRes = false;\r
1553     stretchGroup = null;\r
1554     PaintRefresher.Refresh(ap, av.getSequenceSetId());\r
1555     ap.paintAlignment(true);\r
1556     av.sendSelection();\r
1557   }\r
1558 \r
1559   public void doMouseDraggedDefineMode(MouseEvent evt)\r
1560   {\r
1561     int res = findRes(evt);\r
1562     int y = findSeq(evt);\r
1563 \r
1564     if (wrappedBlock != startWrapBlock)\r
1565     {\r
1566       return;\r
1567     }\r
1568 \r
1569     if (stretchGroup == null)\r
1570     {\r
1571       return;\r
1572     }\r
1573 \r
1574     mouseDragging = true;\r
1575 \r
1576     if (y > av.getAlignment().getHeight())\r
1577     {\r
1578       y = av.getAlignment().getHeight() - 1;\r
1579     }\r
1580 \r
1581     if (res >= av.getAlignment().getWidth())\r
1582     {\r
1583       res = av.getAlignment().getWidth() - 1;\r
1584     }\r
1585 \r
1586     if (stretchGroup.getEndRes() == res)\r
1587     {\r
1588       // Edit end res position of selected group\r
1589       changeEndRes = true;\r
1590     }\r
1591     else if (stretchGroup.getStartRes() == res)\r
1592     {\r
1593       // Edit start res position of selected group\r
1594       changeStartRes = true;\r
1595     }\r
1596 \r
1597     if (res < 0)\r
1598     {\r
1599       res = 0;\r
1600     }\r
1601 \r
1602     if (changeEndRes)\r
1603     {\r
1604       if (res > (stretchGroup.getStartRes() - 1))\r
1605       {\r
1606         stretchGroup.setEndRes(res);\r
1607       }\r
1608     }\r
1609     else if (changeStartRes)\r
1610     {\r
1611       if (res < (stretchGroup.getEndRes() + 1))\r
1612       {\r
1613         stretchGroup.setStartRes(res);\r
1614       }\r
1615     }\r
1616 \r
1617     int dragDirection = 0;\r
1618 \r
1619     if (y > oldSeq)\r
1620     {\r
1621       dragDirection = 1;\r
1622     }\r
1623     else if (y < oldSeq)\r
1624     {\r
1625       dragDirection = -1;\r
1626     }\r
1627 \r
1628     while ((y != oldSeq) && (oldSeq > -1)\r
1629             && (y < av.getAlignment().getHeight()))\r
1630     {\r
1631       // This routine ensures we don't skip any sequences, as the\r
1632       // selection is quite slow.\r
1633       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1634 \r
1635       oldSeq += dragDirection;\r
1636 \r
1637       if (oldSeq < 0)\r
1638       {\r
1639         break;\r
1640       }\r
1641 \r
1642       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1643 \r
1644       if (stretchGroup.getSequences(null).contains(nextSeq))\r
1645       {\r
1646         stretchGroup.deleteSequence(seq, false);\r
1647       }\r
1648       else\r
1649       {\r
1650         if (seq != null)\r
1651         {\r
1652           stretchGroup.addSequence(seq, false);\r
1653         }\r
1654 \r
1655         stretchGroup.addSequence(nextSeq, false);\r
1656       }\r
1657     }\r
1658 \r
1659     if (oldSeq < 0)\r
1660     {\r
1661       oldSeq = -1;\r
1662     }\r
1663 \r
1664     if (res > av.endRes || res < av.startRes || y < av.startSeq\r
1665             || y > av.endSeq)\r
1666     {\r
1667       mouseExited(evt);\r
1668     }\r
1669 \r
1670     if (scrollThread != null)\r
1671     {\r
1672       scrollThread.setEvent(evt);\r
1673     }\r
1674 \r
1675     seqCanvas.repaint();\r
1676   }\r
1677 \r
1678   public void mouseEntered(MouseEvent e)\r
1679   {\r
1680     if (oldSeq < 0)\r
1681     {\r
1682       oldSeq = 0;\r
1683     }\r
1684 \r
1685     if (scrollThread != null)\r
1686     {\r
1687       scrollThread.running = false;\r
1688       scrollThread = null;\r
1689     }\r
1690   }\r
1691 \r
1692   public void mouseExited(MouseEvent e)\r
1693   {\r
1694     if (av.getWrapAlignment())\r
1695     {\r
1696       return;\r
1697     }\r
1698 \r
1699     if (mouseDragging && scrollThread == null)\r
1700     {\r
1701       scrollThread = new ScrollThread();\r
1702     }\r
1703   }\r
1704 \r
1705   void scrollCanvas(MouseEvent evt)\r
1706   {\r
1707     if (evt == null)\r
1708     {\r
1709       if (scrollThread != null)\r
1710       {\r
1711         scrollThread.running = false;\r
1712         scrollThread = null;\r
1713       }\r
1714       mouseDragging = false;\r
1715     }\r
1716     else\r
1717     {\r
1718       if (scrollThread == null)\r
1719       {\r
1720         scrollThread = new ScrollThread();\r
1721       }\r
1722 \r
1723       mouseDragging = true;\r
1724       scrollThread.setEvent(evt);\r
1725     }\r
1726 \r
1727   }\r
1728 \r
1729   // this class allows scrolling off the bottom of the visible alignment\r
1730   class ScrollThread extends Thread\r
1731   {\r
1732     MouseEvent evt;\r
1733 \r
1734     boolean running = false;\r
1735 \r
1736     public ScrollThread()\r
1737     {\r
1738       start();\r
1739     }\r
1740 \r
1741     public void setEvent(MouseEvent e)\r
1742     {\r
1743       evt = e;\r
1744     }\r
1745 \r
1746     public void stopScrolling()\r
1747     {\r
1748       running = false;\r
1749     }\r
1750 \r
1751     public void run()\r
1752     {\r
1753       running = true;\r
1754       while (running)\r
1755       {\r
1756 \r
1757         if (evt != null)\r
1758         {\r
1759 \r
1760           if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)\r
1761           {\r
1762             running = ap.scrollUp(true);\r
1763           }\r
1764 \r
1765           if (mouseDragging && evt.getY() >= getSize().height\r
1766                   && av.getAlignment().getHeight() > av.getEndSeq())\r
1767           {\r
1768             running = ap.scrollUp(false);\r
1769           }\r
1770 \r
1771           if (mouseDragging && evt.getX() < 0)\r
1772           {\r
1773             running = ap.scrollRight(false);\r
1774           }\r
1775 \r
1776           else if (mouseDragging && evt.getX() >= getSize().width)\r
1777           {\r
1778             running = ap.scrollRight(true);\r
1779           }\r
1780         }\r
1781 \r
1782         try\r
1783         {\r
1784           Thread.sleep(75);\r
1785         } catch (Exception ex)\r
1786         {\r
1787         }\r
1788       }\r
1789     }\r
1790   }\r
1791 \r
1792   /**\r
1793    * modify current selection according to a received message.\r
1794    */\r
1795   public void selection(SequenceGroup seqsel, ColumnSelection colsel,\r
1796           SelectionSource source)\r
1797   {\r
1798     // TODO: fix this hack - source of messages is align viewport, but SeqPanel\r
1799     // handles selection messages...\r
1800     // TODO: extend config options to allow user to control if selections may be\r
1801     // shared between viewports.\r
1802     if (av != null\r
1803             && (av == source || !av.followSelection || (source instanceof AlignViewport && ((AlignmentViewport) source)\r
1804                     .getSequenceSetId().equals(av.getSequenceSetId()))))\r
1805     {\r
1806       return;\r
1807     }\r
1808 \r
1809     /*\r
1810      * Check for selection in a view of which this one is a dna/protein\r
1811      * complement.\r
1812      */\r
1813     if (selectionFromTranslation(seqsel, colsel, source))\r
1814     {\r
1815       return;\r
1816     }\r
1817 \r
1818     // do we want to thread this ? (contention with seqsel and colsel locks, I\r
1819     // suspect)\r
1820     // rules are: colsel is copied if there is a real intersection between\r
1821     // sequence selection\r
1822     boolean repaint = false, copycolsel = true;\r
1823     if (av.getSelectionGroup() == null || !av.isSelectionGroupChanged(true))\r
1824     {\r
1825       SequenceGroup sgroup = null;\r
1826       if (seqsel != null && seqsel.getSize() > 0)\r
1827       {\r
1828         if (av.getAlignment() == null)\r
1829         {\r
1830           System.out\r
1831                   .println("Selection message: alignviewport av SeqSetId="\r
1832                           + av.getSequenceSetId() + " ViewId="\r
1833                           + av.getViewId()\r
1834                           + " 's alignment is NULL! returning immediatly.");\r
1835           return;\r
1836         }\r
1837         sgroup = seqsel.intersect(av.getAlignment(),\r
1838                 (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);\r
1839         if ((sgroup == null || sgroup.getSize() == 0)\r
1840                 && (colsel == null || colsel.size() == 0))\r
1841         {\r
1842           // don't copy columns if the region didn't intersect.\r
1843           copycolsel = false;\r
1844         }\r
1845       }\r
1846       if (sgroup != null && sgroup.getSize() > 0)\r
1847       {\r
1848         av.setSelectionGroup(sgroup);\r
1849       }\r
1850       else\r
1851       {\r
1852         av.setSelectionGroup(null);\r
1853       }\r
1854       repaint = av.isSelectionGroupChanged(true);\r
1855     }\r
1856     if (copycolsel\r
1857             && (av.getColumnSelection() == null || !av\r
1858                     .isColSelChanged(true)))\r
1859     {\r
1860       // the current selection is unset or from a previous message\r
1861       // so import the new colsel.\r
1862       if (colsel == null || colsel.size() == 0)\r
1863       {\r
1864         if (av.getColumnSelection() != null)\r
1865         {\r
1866           av.getColumnSelection().clear();\r
1867         }\r
1868       }\r
1869       else\r
1870       {\r
1871         // TODO: shift colSel according to the intersecting sequences\r
1872         if (av.getColumnSelection() == null)\r
1873         {\r
1874           av.setColumnSelection(new ColumnSelection(colsel));\r
1875         }\r
1876         else\r
1877         {\r
1878           av.getColumnSelection().setElementsFrom(colsel);\r
1879         }\r
1880       }\r
1881       repaint |= av.isColSelChanged(true);\r
1882     }\r
1883     if (copycolsel\r
1884             && av.hasHiddenColumns()\r
1885             && (av.getColumnSelection() == null || av.getColumnSelection()\r
1886                     .getHiddenColumns() == null))\r
1887     {\r
1888       System.err.println("Bad things");\r
1889     }\r
1890     if (repaint)\r
1891     {\r
1892       ap.scalePanelHolder.repaint();\r
1893       ap.repaint();\r
1894     }\r
1895   }\r
1896 \r
1897   /**\r
1898    * scroll to the given row/column - or nearest visible location\r
1899    * \r
1900    * @param row\r
1901    * @param column\r
1902    */\r
1903   public void scrollTo(int row, int column)\r
1904   {\r
1905 \r
1906     row = row < 0 ? ap.av.startSeq : row;\r
1907     column = column < 0 ? ap.av.startRes : column;\r
1908     ap.scrollTo(column, column, row, true, true);\r
1909   }\r
1910 \r
1911   /**\r
1912    * scroll to the given row - or nearest visible location\r
1913    * \r
1914    * @param row\r
1915    */\r
1916   public void scrollToRow(int row)\r
1917   {\r
1918 \r
1919     row = row < 0 ? ap.av.startSeq : row;\r
1920     ap.scrollTo(ap.av.startRes, ap.av.startRes, row, true, true);\r
1921   }\r
1922 \r
1923   /**\r
1924    * scroll to the given column - or nearest visible location\r
1925    * \r
1926    * @param column\r
1927    */\r
1928   public void scrollToColumn(int column)\r
1929   {\r
1930 \r
1931     column = column < 0 ? ap.av.startRes : column;\r
1932     ap.scrollTo(column, column, ap.av.startSeq, true, true);\r
1933   }\r
1934 \r
1935   /**\r
1936    * If this panel is a cdna/protein translation view of the selection source,\r
1937    * tries to map the source selection to a local one, and returns true. Else\r
1938    * returns false.\r
1939    * \r
1940    * @param seqsel\r
1941    * @param colsel\r
1942    * @param source\r
1943    */\r
1944   protected boolean selectionFromTranslation(SequenceGroup seqsel,\r
1945           ColumnSelection colsel, SelectionSource source)\r
1946   {\r
1947     if (!(source instanceof AlignViewportI)) {\r
1948       return false;\r
1949     }\r
1950     final AlignViewportI sourceAv = (AlignViewportI) source;\r
1951     if (sourceAv.getCodingComplement() != av && av.getCodingComplement() != sourceAv)\r
1952     {\r
1953       return false;\r
1954     }\r
1955   \r
1956     /*\r
1957      * Map sequence selection\r
1958      */\r
1959     SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);\r
1960     av.setSelectionGroup(sg);\r
1961     av.isSelectionGroupChanged(true);\r
1962   \r
1963     /*\r
1964      * Map column selection\r
1965      */\r
1966     ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,\r
1967             av);\r
1968     av.setColumnSelection(cs);\r
1969     av.isColSelChanged(true);\r
1970   \r
1971     ap.scalePanelHolder.repaint();\r
1972     ap.repaint();\r
1973   \r
1974     return true;\r
1975   }\r
1976 \r
1977 }\r