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