SequenceListener highlights SearchResults, not just a single sequence
[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         if (features[i].featureGroup != null\r
208            && seqCanvas.fr.featureGroups!=null\r
209             && seqCanvas.fr.featureGroups.containsKey(features[i].featureGroup)\r
210             && !((Boolean)seqCanvas.fr.featureGroups.get(features[i].featureGroup)).booleanValue())\r
211           continue;\r
212 \r
213 \r
214         if ( (features[i].getBegin() <= res) &&\r
215             (features[i].getEnd() >= res))\r
216         {\r
217           tmp.addElement(features[i]);\r
218         }\r
219       }\r
220     }\r
221 \r
222     features = new SequenceFeature[tmp.size()];\r
223     tmp.copyInto(features);\r
224 \r
225     return features;\r
226   }\r
227 \r
228 \r
229   void endEditing()\r
230   {\r
231     if (editCommand != null && editCommand.getSize() > 0)\r
232     {\r
233       ap.alignFrame.addHistoryItem(editCommand);\r
234       av.firePropertyChange("alignment", null,\r
235                             av.getAlignment().getSequences());\r
236     }\r
237 \r
238     startseq = -1;\r
239     lastres = -1;\r
240     editingSeqs = false;\r
241     groupEditing = false;\r
242     keyboardNo1 = null;\r
243     keyboardNo2 = null;\r
244     editCommand = null;\r
245   }\r
246 \r
247   void setCursorRow()\r
248   {\r
249     seqCanvas.cursorY = getKeyboardNo1() - 1;\r
250     scrollToVisible();\r
251   }\r
252 \r
253   void setCursorColumn()\r
254   {\r
255     seqCanvas.cursorX = getKeyboardNo1() - 1;\r
256     scrollToVisible();\r
257   }\r
258 \r
259   void setCursorRowAndColumn()\r
260   {\r
261     if (keyboardNo2 == null)\r
262     {\r
263       keyboardNo2 = new StringBuffer();\r
264     }\r
265     else\r
266     {\r
267       seqCanvas.cursorX = getKeyboardNo1() - 1;\r
268       seqCanvas.cursorY = getKeyboardNo2() - 1;\r
269       scrollToVisible();\r
270     }\r
271   }\r
272 \r
273   void setCursorPosition()\r
274   {\r
275     SequenceI sequence =\r
276         (Sequence) av.getAlignment().getSequenceAt(seqCanvas.cursorY);\r
277 \r
278     seqCanvas.cursorX = sequence.findIndex(\r
279         getKeyboardNo1() - 1\r
280         );\r
281     scrollToVisible();\r
282   }\r
283 \r
284   void moveCursor(int dx, int dy)\r
285   {\r
286     seqCanvas.cursorX += dx;\r
287     seqCanvas.cursorY += dy;\r
288     if (av.hasHiddenColumns && !av.colSel.isVisible(seqCanvas.cursorX))\r
289     {\r
290       int original = seqCanvas.cursorX - dx;\r
291       int maxWidth = av.alignment.getWidth();\r
292 \r
293       while (!av.colSel.isVisible(seqCanvas.cursorX)\r
294              && seqCanvas.cursorX < maxWidth\r
295              && seqCanvas.cursorX > 0)\r
296       {\r
297         seqCanvas.cursorX += dx;\r
298       }\r
299 \r
300       if (seqCanvas.cursorX >= maxWidth\r
301           || !av.colSel.isVisible(seqCanvas.cursorX))\r
302       {\r
303         seqCanvas.cursorX = original;\r
304       }\r
305     }\r
306 \r
307     scrollToVisible();\r
308   }\r
309 \r
310   void scrollToVisible()\r
311   {\r
312     if (seqCanvas.cursorX < 0)\r
313     {\r
314       seqCanvas.cursorX = 0;\r
315     }\r
316     else if (seqCanvas.cursorX > av.alignment.getWidth() - 1)\r
317     {\r
318       seqCanvas.cursorX = av.alignment.getWidth() - 1;\r
319     }\r
320 \r
321     if (seqCanvas.cursorY < 0)\r
322     {\r
323       seqCanvas.cursorY = 0;\r
324     }\r
325     else if (seqCanvas.cursorY > av.alignment.getHeight() - 1)\r
326     {\r
327       seqCanvas.cursorY = av.alignment.getHeight() - 1;\r
328     }\r
329 \r
330     endEditing();\r
331     if (av.wrapAlignment)\r
332     {\r
333       ap.scrollToWrappedVisible(seqCanvas.cursorX);\r
334     }\r
335     else\r
336     {\r
337       while (seqCanvas.cursorY < av.startSeq)\r
338       {\r
339         ap.scrollUp(true);\r
340       }\r
341       while (seqCanvas.cursorY + 1 > av.endSeq)\r
342       {\r
343         ap.scrollUp(false);\r
344       }\r
345       if (!av.wrapAlignment)\r
346       {\r
347         while (seqCanvas.cursorX < av.colSel.adjustForHiddenColumns(av.startRes))\r
348         {\r
349           if (!ap.scrollRight(false))\r
350           {\r
351             break;\r
352           }\r
353         }\r
354         while (seqCanvas.cursorX > av.colSel.adjustForHiddenColumns(av.endRes))\r
355         {\r
356           if (!ap.scrollRight(true))\r
357           {\r
358             break;\r
359           }\r
360         }\r
361       }\r
362     }\r
363     setStatusMessage(av.alignment.getSequenceAt(seqCanvas.cursorY),\r
364                      seqCanvas.cursorX, seqCanvas.cursorY);\r
365 \r
366     seqCanvas.repaint();\r
367   }\r
368 \r
369   void setSelectionAreaAtCursor(boolean topLeft)\r
370   {\r
371     SequenceI sequence =\r
372         (Sequence) av.getAlignment().getSequenceAt(seqCanvas.cursorY);\r
373 \r
374     if (av.getSelectionGroup() != null)\r
375     {\r
376       SequenceGroup sg = av.selectionGroup;\r
377       //Find the top and bottom of this group\r
378       int min = av.alignment.getHeight(), max = 0;\r
379       for (int i = 0; i < sg.getSize(); i++)\r
380       {\r
381         int index = av.alignment.findIndex(sg.getSequenceAt(i));\r
382         if (index > max)\r
383         {\r
384           max = index;\r
385         }\r
386         if (index < min)\r
387         {\r
388           min = index;\r
389         }\r
390       }\r
391 \r
392       max++;\r
393 \r
394       if (topLeft)\r
395       {\r
396         sg.setStartRes(seqCanvas.cursorX);\r
397         if (sg.getEndRes() < seqCanvas.cursorX)\r
398         {\r
399           sg.setEndRes(seqCanvas.cursorX);\r
400         }\r
401 \r
402         min = seqCanvas.cursorY;\r
403       }\r
404       else\r
405       {\r
406         sg.setEndRes(seqCanvas.cursorX);\r
407         if (sg.getStartRes() > seqCanvas.cursorX)\r
408         {\r
409           sg.setStartRes(seqCanvas.cursorX);\r
410         }\r
411 \r
412         max = seqCanvas.cursorY + 1;\r
413       }\r
414 \r
415       if (min > max)\r
416       {\r
417         // Only the user can do this\r
418         av.setSelectionGroup(null);\r
419       }\r
420       else\r
421       {\r
422         // Now add any sequences between min and max\r
423         sg.getSequences(null).clear();\r
424         for (int i = min; i < max; i++)\r
425         {\r
426           sg.addSequence(av.alignment.getSequenceAt(i), false);\r
427         }\r
428       }\r
429     }\r
430 \r
431     if (av.getSelectionGroup() == null)\r
432     {\r
433       SequenceGroup sg = new SequenceGroup();\r
434       sg.setStartRes(seqCanvas.cursorX);\r
435       sg.setEndRes(seqCanvas.cursorX);\r
436       sg.addSequence(sequence, false);\r
437       av.setSelectionGroup(sg);\r
438     }\r
439 \r
440     ap.paintAlignment(false);\r
441   }\r
442 \r
443   void insertGapAtCursor(boolean group)\r
444   {\r
445     groupEditing = group;\r
446     startseq = seqCanvas.cursorY;\r
447     lastres = seqCanvas.cursorX;\r
448     editSequence(true, seqCanvas.cursorX + getKeyboardNo1());\r
449     endEditing();\r
450   }\r
451 \r
452   void deleteGapAtCursor(boolean group)\r
453   {\r
454     groupEditing = group;\r
455     startseq = seqCanvas.cursorY;\r
456     lastres = seqCanvas.cursorX + getKeyboardNo1();\r
457     editSequence(false, seqCanvas.cursorX);\r
458     endEditing();\r
459   }\r
460 \r
461   void numberPressed(char value)\r
462   {\r
463     if (keyboardNo1 == null)\r
464     {\r
465       keyboardNo1 = new StringBuffer();\r
466     }\r
467 \r
468     if (keyboardNo2 != null)\r
469     {\r
470       keyboardNo2.append(value);\r
471     }\r
472     else\r
473     {\r
474       keyboardNo1.append(value);\r
475     }\r
476   }\r
477 \r
478   int getKeyboardNo1()\r
479   {\r
480     if (keyboardNo1 == null)\r
481       return 1;\r
482     else\r
483     {\r
484       int value = Integer.parseInt(keyboardNo1.toString());\r
485       keyboardNo1 = null;\r
486       return value;\r
487     }\r
488   }\r
489 \r
490   int getKeyboardNo2()\r
491   {\r
492     if (keyboardNo2 == null)\r
493       return 1;\r
494     else\r
495     {\r
496       int value = Integer.parseInt(keyboardNo2.toString());\r
497       keyboardNo2 = null;\r
498       return value;\r
499     }\r
500   }\r
501 \r
502   /**\r
503    * DOCUMENT ME!\r
504    *\r
505    * @param evt DOCUMENT ME!\r
506    */\r
507   public void mouseReleased(MouseEvent evt)\r
508   {\r
509     mouseDragging = false;\r
510     mouseWheelPressed = false;\r
511 \r
512     if (!editingSeqs)\r
513     {\r
514       doMouseReleasedDefineMode(evt);\r
515       return;\r
516     }\r
517 \r
518     endEditing();\r
519   }\r
520 \r
521   /**\r
522    * DOCUMENT ME!\r
523    *\r
524    * @param evt DOCUMENT ME!\r
525    */\r
526   public void mousePressed(MouseEvent evt)\r
527   {\r
528     lastMousePress = evt.getPoint();\r
529 \r
530     if (javax.swing.SwingUtilities.isMiddleMouseButton(evt))\r
531     {\r
532       mouseWheelPressed = true;\r
533       return;\r
534     }\r
535 \r
536     if (evt.isShiftDown() || evt.isAltDown() ||\r
537         evt.isControlDown())\r
538     {\r
539       if (evt.isAltDown() || evt.isControlDown())\r
540       {\r
541         groupEditing = true;\r
542       }\r
543       editingSeqs = true;\r
544     }\r
545     else\r
546     {\r
547       doMousePressedDefineMode(evt);\r
548       return;\r
549     }\r
550 \r
551     int seq = findSeq(evt);\r
552     int res = findRes(evt);\r
553 \r
554     if (seq < 0 || res < 0)\r
555     {\r
556       return;\r
557     }\r
558 \r
559     if ( (seq < av.getAlignment().getHeight()) &&\r
560         (res < av.getAlignment().getSequenceAt(seq).getLength()))\r
561     {\r
562       startseq = seq;\r
563       lastres = res;\r
564     }\r
565     else\r
566     {\r
567       startseq = -1;\r
568       lastres = -1;\r
569     }\r
570 \r
571     return;\r
572   }\r
573 \r
574   String lastMessage;\r
575   public void mouseOverSequence(SequenceI sequence, int index)\r
576   {\r
577     String tmp = sequence.hashCode()+index+"";\r
578     if (lastMessage == null || !lastMessage.equals(tmp))\r
579       ssm.mouseOverSequence(sequence, index);\r
580 \r
581     lastMessage = tmp;\r
582   }\r
583 \r
584 \r
585   public void highlightSequence(SearchResults results)\r
586   {\r
587       seqCanvas.highlightSearchResults(results);\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       if (lastTooltip == null || !lastTooltip.equals(tooltipText.toString()))\r
770         setToolTipText(tooltipText.toString());\r
771 \r
772       lastTooltip = tooltipText.toString();\r
773     }\r
774 \r
775   }\r
776   String lastTooltip;\r
777 \r
778   void setStatusMessage(SequenceI sequence, int res, int seq)\r
779   {\r
780     StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " +\r
781                                          sequence.getName());\r
782 \r
783     Object obj = null;\r
784     if (av.alignment.isNucleotide())\r
785     {\r
786       obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res) +\r
787                                                  "");\r
788       if (obj != null)\r
789       {\r
790         text.append(" Nucleotide: ");\r
791       }\r
792     }\r
793     else\r
794     {\r
795       obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");\r
796       if (obj != null)\r
797       {\r
798         text.append("  Residue: ");\r
799       }\r
800     }\r
801 \r
802     if (obj != null)\r
803     {\r
804 \r
805       if (obj != "")\r
806       {\r
807         text.append(obj + " (" + sequence.findPosition(res) +\r
808                     ")");\r
809       }\r
810     }\r
811     ap.alignFrame.statusBar.setText(text.toString());\r
812 \r
813   }\r
814 \r
815   /**\r
816    * DOCUMENT ME!\r
817    *\r
818    * @param evt DOCUMENT ME!\r
819    */\r
820   public void mouseDragged(MouseEvent evt)\r
821   {\r
822     if (mouseWheelPressed)\r
823     {\r
824       int oldWidth = av.charWidth;\r
825 \r
826       //Which is bigger, left-right or up-down?\r
827       if (Math.abs(evt.getY() - lastMousePress.getY())\r
828           > Math.abs(evt.getX() - lastMousePress.getX()))\r
829       {\r
830         int fontSize = av.font.getSize();\r
831 \r
832         if (evt.getY() < lastMousePress.getY())\r
833         {\r
834           fontSize--;\r
835         }\r
836         else if (evt.getY() > lastMousePress.getY())\r
837         {\r
838           fontSize++;\r
839         }\r
840 \r
841         if (fontSize < 1)\r
842         {\r
843           fontSize = 1;\r
844         }\r
845 \r
846         av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));\r
847         av.charWidth = oldWidth;\r
848         ap.fontChanged();\r
849       }\r
850       else\r
851       {\r
852         if (evt.getX() < lastMousePress.getX() && av.charWidth > 1)\r
853         {\r
854           av.charWidth--;\r
855         }\r
856         else if (evt.getX() > lastMousePress.getX())\r
857         {\r
858           av.charWidth++;\r
859         }\r
860 \r
861         ap.paintAlignment(false);\r
862       }\r
863 \r
864       FontMetrics fm = getFontMetrics(av.getFont());\r
865       av.validCharWidth = fm.charWidth('M') <= av.charWidth;\r
866 \r
867       lastMousePress = evt.getPoint();\r
868 \r
869       return;\r
870     }\r
871 \r
872     if (!editingSeqs)\r
873     {\r
874       doMouseDraggedDefineMode(evt);\r
875       return;\r
876     }\r
877 \r
878     int res = findRes(evt);\r
879 \r
880     if (res < 0)\r
881     {\r
882       res = 0;\r
883     }\r
884 \r
885     if ( (lastres == -1) || (lastres == res))\r
886     {\r
887       return;\r
888     }\r
889 \r
890     if ( (res < av.getAlignment().getWidth()) && (res < lastres))\r
891     {\r
892       // dragLeft, delete gap\r
893       editSequence(false, res);\r
894     }\r
895     else\r
896     {\r
897       editSequence(true, res);\r
898     }\r
899 \r
900     mouseDragging = true;\r
901     if (scrollThread != null)\r
902     {\r
903       scrollThread.setEvent(evt);\r
904     }\r
905   }\r
906 \r
907 \r
908 \r
909   synchronized void editSequence(boolean insertGap, int startres)\r
910   {\r
911     int fixedLeft = -1;\r
912     int fixedRight = -1;\r
913     boolean fixedColumns = false;\r
914     SequenceGroup sg = av.getSelectionGroup();\r
915 \r
916     SequenceI seq = av.alignment.getSequenceAt(startseq);\r
917 \r
918     //No group, but the sequence may represent a group\r
919     if (!groupEditing && av.hasHiddenRows)\r
920     {\r
921       if (av.hiddenRepSequences != null\r
922           && av.hiddenRepSequences.containsKey(seq))\r
923       {\r
924         sg = (SequenceGroup) av.hiddenRepSequences.get(seq);\r
925         groupEditing = true;\r
926       }\r
927     }\r
928 \r
929     StringBuffer message = new StringBuffer();\r
930     if (groupEditing)\r
931     {\r
932       message.append("Edit group:");\r
933       if (editCommand == null)\r
934       {\r
935         editCommand = new EditCommand("Edit Group");\r
936       }\r
937     }\r
938     else\r
939     {\r
940       message.append("Edit sequence: " + seq.getName());\r
941       String label = seq.getName();\r
942       if (label.length() > 10)\r
943       {\r
944         label = label.substring(0, 10);\r
945       }\r
946       if (editCommand == null)\r
947       {\r
948         editCommand = new EditCommand("Edit " + label);\r
949       }\r
950     }\r
951 \r
952     if (insertGap)\r
953     {\r
954       message.append(" insert ");\r
955     }\r
956     else\r
957     {\r
958       message.append(" delete ");\r
959     }\r
960 \r
961     message.append(Math.abs(startres - lastres) + " gaps.");\r
962     ap.alignFrame.statusBar.setText(message.toString());\r
963 \r
964     //Are we editing within a selection group?\r
965     if (groupEditing\r
966         || (sg != null && sg.getSequences(av.hiddenRepSequences).contains(seq)))\r
967     {\r
968       fixedColumns = true;\r
969 \r
970       //sg might be null as the user may only see 1 sequence,\r
971       //but the sequence represents a group\r
972       if (sg == null)\r
973       {\r
974         if (av.hiddenRepSequences == null\r
975             || !av.hiddenRepSequences.containsKey(seq))\r
976         {\r
977           endEditing();\r
978           return;\r
979         }\r
980         sg = (SequenceGroup) av.hiddenRepSequences.get(seq);\r
981       }\r
982 \r
983       fixedLeft = sg.getStartRes();\r
984       fixedRight = sg.getEndRes();\r
985 \r
986       if ( (startres < fixedLeft && lastres >= fixedLeft)\r
987           || (startres >= fixedLeft && lastres < fixedLeft)\r
988           || (startres > fixedRight && lastres <= fixedRight)\r
989           || (startres <= fixedRight && lastres > fixedRight))\r
990       {\r
991         endEditing();\r
992         return;\r
993       }\r
994 \r
995       if (fixedLeft > startres)\r
996       {\r
997         fixedRight = fixedLeft - 1;\r
998         fixedLeft = 0;\r
999       }\r
1000       else if (fixedRight < startres)\r
1001       {\r
1002         fixedLeft = fixedRight;\r
1003         fixedRight = -1;\r
1004       }\r
1005     }\r
1006 \r
1007     if (av.hasHiddenColumns)\r
1008     {\r
1009       fixedColumns = true;\r
1010       int y1 = av.getColumnSelection().getHiddenBoundaryLeft(startres);\r
1011       int y2 = av.getColumnSelection().getHiddenBoundaryRight(startres);\r
1012 \r
1013       if ( (insertGap && startres > y1 && lastres < y1)\r
1014           || (!insertGap && startres < y2 && lastres > y2))\r
1015       {\r
1016         endEditing();\r
1017         return;\r
1018       }\r
1019 \r
1020       //System.out.print(y1+" "+y2+" "+fixedLeft+" "+fixedRight+"~~");\r
1021       //Selection spans a hidden region\r
1022       if (fixedLeft < y1 && (fixedRight > y2 || fixedRight == -1))\r
1023       {\r
1024         if (startres >= y2)\r
1025         {\r
1026           fixedLeft = y2;\r
1027         }\r
1028         else\r
1029         {\r
1030           fixedRight = y2 - 1;\r
1031         }\r
1032       }\r
1033     }\r
1034 \r
1035     if (groupEditing)\r
1036     {\r
1037       Vector vseqs = sg.getSequences(av.hiddenRepSequences);\r
1038       int g, groupSize = vseqs.size();\r
1039       SequenceI[] groupSeqs = new SequenceI[groupSize];\r
1040       for (g = 0; g < groupSeqs.length; g++)\r
1041       {\r
1042         groupSeqs[g] = (SequenceI) vseqs.elementAt(g);\r
1043       }\r
1044 \r
1045       // drag to right\r
1046       if (insertGap)\r
1047       {\r
1048         //If the user has selected the whole sequence, and is dragging to\r
1049         // the right, we can still extend the alignment and selectionGroup\r
1050         if (sg.getStartRes() == 0\r
1051             && sg.getEndRes() == fixedRight\r
1052             && sg.getEndRes() == av.alignment.getWidth() - 1\r
1053             )\r
1054         {\r
1055           sg.setEndRes(av.alignment.getWidth() + startres - lastres);\r
1056           fixedRight = sg.getEndRes();\r
1057         }\r
1058 \r
1059         // Is it valid with fixed columns??\r
1060         // Find the next gap before the end\r
1061         // of the visible region boundary\r
1062         boolean blank = false;\r
1063         for (fixedRight = fixedRight;\r
1064              fixedRight > lastres;\r
1065              fixedRight--)\r
1066         {\r
1067           blank = true;\r
1068 \r
1069           for (g = 0; g < groupSize; g++)\r
1070           {\r
1071             for (int j = 0; j < startres - lastres; j++)\r
1072             {\r
1073               if (!jalview.util.Comparison.isGap(\r
1074                   groupSeqs[g].getCharAt(fixedRight - j)))\r
1075               {\r
1076                 blank = false;\r
1077                 break;\r
1078               }\r
1079             }\r
1080           }\r
1081           if (blank)\r
1082           {\r
1083             break;\r
1084           }\r
1085         }\r
1086 \r
1087         if (!blank)\r
1088         {\r
1089           if (sg.getSize() == av.alignment.getHeight())\r
1090           {\r
1091             if ( (av.hasHiddenColumns\r
1092                   &&\r
1093                   startres < av.getColumnSelection().getHiddenBoundaryRight(startres)))\r
1094             {\r
1095               endEditing();\r
1096               return;\r
1097             }\r
1098 \r
1099             int alWidth = av.alignment.getWidth();\r
1100             if (av.hasHiddenRows)\r
1101             {\r
1102               int hwidth = av.alignment.getHiddenSequences().getWidth();\r
1103               if (hwidth > alWidth)\r
1104               {\r
1105                 alWidth = hwidth;\r
1106               }\r
1107             }\r
1108             //We can still insert gaps if the selectionGroup\r
1109             //contains all the sequences\r
1110             sg.setEndRes(sg.getEndRes() + startres - lastres);\r
1111             fixedRight = alWidth + startres - lastres;\r
1112           }\r
1113           else\r
1114           {\r
1115             endEditing();\r
1116             return;\r
1117           }\r
1118         }\r
1119       }\r
1120 \r
1121       // drag to left\r
1122       else if (!insertGap)\r
1123       {\r
1124         /// Are we able to delete?\r
1125         // ie are all columns blank?\r
1126 \r
1127         for (g = 0; g < groupSize; g++)\r
1128         {\r
1129           for (int j = startres; j < lastres; j++)\r
1130           {\r
1131             if (groupSeqs[g].getLength() <= j)\r
1132             {\r
1133               continue;\r
1134             }\r
1135 \r
1136             if (!jalview.util.Comparison.isGap(\r
1137                 groupSeqs[g].getCharAt(j)))\r
1138             {\r
1139               // Not a gap, block edit not valid\r
1140               endEditing();\r
1141               return;\r
1142             }\r
1143           }\r
1144         }\r
1145       }\r
1146 \r
1147       if (insertGap)\r
1148       {\r
1149         // dragging to the right\r
1150         if (fixedColumns && fixedRight != -1)\r
1151         {\r
1152           for (int j = lastres; j < startres; j++)\r
1153           {\r
1154             insertChar(j, groupSeqs, fixedRight);\r
1155           }\r
1156         }\r
1157         else\r
1158         {\r
1159           editCommand.appendEdit(EditCommand.INSERT_GAP,\r
1160                                  groupSeqs,\r
1161                                  startres, startres - lastres,\r
1162                                  av.alignment,\r
1163                                  true);\r
1164         }\r
1165       }\r
1166       else\r
1167       {\r
1168         // dragging to the left\r
1169         if (fixedColumns && fixedRight != -1)\r
1170         {\r
1171           for (int j = lastres; j > startres; j--)\r
1172           {\r
1173             deleteChar(startres, groupSeqs, fixedRight);\r
1174           }\r
1175         }\r
1176         else\r
1177         {\r
1178           editCommand.appendEdit(EditCommand.DELETE_GAP,\r
1179                                  groupSeqs,\r
1180                                  startres, lastres - startres,\r
1181                                  av.alignment,\r
1182                                  true);\r
1183         }\r
1184 \r
1185       }\r
1186     }\r
1187     else /////Editing a single sequence///////////\r
1188     {\r
1189       if (insertGap)\r
1190       {\r
1191         // dragging to the right\r
1192         if (fixedColumns && fixedRight != -1)\r
1193         {\r
1194           for (int j = lastres; j < startres; j++)\r
1195           {\r
1196             insertChar(j, new SequenceI[]\r
1197                        {seq}, fixedRight);\r
1198           }\r
1199         }\r
1200         else\r
1201         {\r
1202           editCommand.appendEdit(EditCommand.INSERT_GAP,\r
1203                                  new SequenceI[]\r
1204                                  {seq},\r
1205                                  lastres, startres - lastres,\r
1206                                  av.alignment,\r
1207                                  true);\r
1208         }\r
1209       }\r
1210       else\r
1211       {\r
1212         // dragging to the left\r
1213         if (fixedColumns && fixedRight != -1)\r
1214         {\r
1215           for (int j = lastres; j > startres; j--)\r
1216           {\r
1217             if (!jalview.util.Comparison.isGap(seq.getCharAt(startres)))\r
1218             {\r
1219               endEditing();\r
1220               break;\r
1221             }\r
1222             deleteChar(startres, new SequenceI[]\r
1223                        {seq}, fixedRight);\r
1224           }\r
1225         }\r
1226         else\r
1227         {\r
1228           //could be a keyboard edit trying to delete none gaps\r
1229           int max = 0;\r
1230           for (int m = startres; m < lastres; m++)\r
1231           {\r
1232             if (!jalview.util.Comparison.isGap(seq.getCharAt(m)))\r
1233             {\r
1234               break;\r
1235             }\r
1236             max++;\r
1237           }\r
1238 \r
1239           if (max > 0)\r
1240           {\r
1241             editCommand.appendEdit(EditCommand.DELETE_GAP,\r
1242                                    new SequenceI[]\r
1243                                    {seq},\r
1244                                    startres, max,\r
1245                                    av.alignment,\r
1246                                    true);\r
1247           }\r
1248         }\r
1249       }\r
1250     }\r
1251 \r
1252     lastres = startres;\r
1253     seqCanvas.repaint();\r
1254   }\r
1255 \r
1256   void insertChar(int j, SequenceI[] seq, int fixedColumn)\r
1257   {\r
1258     int blankColumn = fixedColumn;\r
1259     for (int s = 0; s < seq.length; s++)\r
1260     {\r
1261       //Find the next gap before the end of the visible region boundary\r
1262       //If lastCol > j, theres a boundary after the gap insertion\r
1263 \r
1264       for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)\r
1265       {\r
1266         if (jalview.util.Comparison.isGap(seq[s].getCharAt(blankColumn)))\r
1267         {\r
1268           //Theres a space, so break and insert the gap\r
1269           break;\r
1270         }\r
1271       }\r
1272 \r
1273       if (blankColumn <= j)\r
1274       {\r
1275         blankColumn = fixedColumn;\r
1276         endEditing();\r
1277         return;\r
1278       }\r
1279     }\r
1280 \r
1281     editCommand.appendEdit(EditCommand.DELETE_GAP,\r
1282                            seq,\r
1283                            blankColumn, 1, av.alignment, true);\r
1284 \r
1285     editCommand.appendEdit(EditCommand.INSERT_GAP,\r
1286                            seq,\r
1287                            j, 1, av.alignment,\r
1288                            true);\r
1289 \r
1290   }\r
1291 \r
1292   void deleteChar(int j, SequenceI[] seq, int fixedColumn)\r
1293   {\r
1294 \r
1295     editCommand.appendEdit(EditCommand.DELETE_GAP,\r
1296                            seq,\r
1297                            j, 1, av.alignment, true);\r
1298 \r
1299     editCommand.appendEdit(EditCommand.INSERT_GAP,\r
1300                            seq,\r
1301                            fixedColumn, 1, av.alignment, true);\r
1302   }\r
1303 \r
1304   /**\r
1305    * DOCUMENT ME!\r
1306    *\r
1307    * @param e DOCUMENT ME!\r
1308    */\r
1309   public void mouseEntered(MouseEvent e)\r
1310   {\r
1311     if (oldSeq < 0)\r
1312     {\r
1313       oldSeq = 0;\r
1314     }\r
1315 \r
1316     if (scrollThread != null)\r
1317     {\r
1318       scrollThread.running = false;\r
1319       scrollThread = null;\r
1320     }\r
1321   }\r
1322 \r
1323   /**\r
1324    * DOCUMENT ME!\r
1325    *\r
1326    * @param e DOCUMENT ME!\r
1327    */\r
1328   public void mouseExited(MouseEvent e)\r
1329   {\r
1330     if (av.getWrapAlignment())\r
1331     {\r
1332       return;\r
1333     }\r
1334 \r
1335     if (mouseDragging)\r
1336     {\r
1337       scrollThread = new ScrollThread();\r
1338     }\r
1339   }\r
1340 \r
1341   public void mouseClicked(MouseEvent evt)\r
1342   {\r
1343     SequenceI sequence = av.alignment.getSequenceAt(findSeq(evt));\r
1344     if (evt.getClickCount() > 1)\r
1345     {\r
1346       if (av.getSelectionGroup().getSize() == 1\r
1347           && av.getSelectionGroup().getEndRes()\r
1348           - av.getSelectionGroup().getStartRes() < 2)\r
1349       {\r
1350         av.setSelectionGroup(null);\r
1351       }\r
1352 \r
1353       SequenceFeature[] features = findFeaturesAtRes(\r
1354           sequence.getDatasetSequence(),\r
1355           sequence.findPosition(findRes(evt))\r
1356           );\r
1357 \r
1358       if (features != null && features.length > 0)\r
1359       {\r
1360         SearchResults highlight = new SearchResults();\r
1361         highlight.addResult(sequence,\r
1362                             features[0].getBegin(),\r
1363                             features[0].getEnd());\r
1364         seqCanvas.highlightSearchResults(highlight);\r
1365       }\r
1366       if (features != null && features.length > 0)\r
1367       {\r
1368         seqCanvas.getFeatureRenderer().amendFeatures(\r
1369             new SequenceI[]\r
1370             {sequence}, features, false, ap);\r
1371 \r
1372         seqCanvas.highlightSearchResults(null);\r
1373       }\r
1374     }\r
1375   }\r
1376 \r
1377   public void mouseWheelMoved(MouseWheelEvent e)\r
1378   {\r
1379     e.consume();\r
1380       if (e.getWheelRotation() > 0)\r
1381       {\r
1382         ap.scrollUp(false);\r
1383       }\r
1384       else\r
1385       {\r
1386         ap.scrollUp(true);\r
1387       }\r
1388   }\r
1389 \r
1390   /**\r
1391    * DOCUMENT ME!\r
1392    *\r
1393    * @param evt DOCUMENT ME!\r
1394    */\r
1395   public void doMousePressedDefineMode(MouseEvent evt)\r
1396   {\r
1397     int res = findRes(evt);\r
1398     int seq = findSeq(evt);\r
1399     oldSeq = seq;\r
1400 \r
1401     startWrapBlock = wrappedBlock;\r
1402 \r
1403     if (av.wrapAlignment && seq > av.alignment.getHeight())\r
1404     {\r
1405       JOptionPane.showInternalMessageDialog(Desktop.desktop,\r
1406                                             "Cannot edit annotations in wrapped view.",\r
1407                                             "Wrapped view - no edit",\r
1408                                             JOptionPane.WARNING_MESSAGE);\r
1409       return;\r
1410     }\r
1411 \r
1412     if (seq < 0 || res < 0)\r
1413     {\r
1414       return;\r
1415     }\r
1416 \r
1417     SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
1418 \r
1419     if ( (sequence == null) || (res > sequence.getLength()))\r
1420     {\r
1421       return;\r
1422     }\r
1423 \r
1424     stretchGroup = av.getSelectionGroup();\r
1425 \r
1426     if (stretchGroup == null)\r
1427     {\r
1428       stretchGroup = av.alignment.findGroup(sequence);\r
1429 \r
1430       if ( (stretchGroup != null) && (res > stretchGroup.getStartRes()) &&\r
1431           (res < stretchGroup.getEndRes()))\r
1432       {\r
1433         av.setSelectionGroup(stretchGroup);\r
1434       }\r
1435       else\r
1436       {\r
1437         stretchGroup = null;\r
1438       }\r
1439     }\r
1440     else if (!stretchGroup.getSequences(null).contains(sequence) ||\r
1441              (stretchGroup.getStartRes() > res) ||\r
1442              (stretchGroup.getEndRes() < res))\r
1443     {\r
1444       stretchGroup = null;\r
1445 \r
1446       SequenceGroup[] allGroups = av.alignment.findAllGroups(sequence);\r
1447 \r
1448       if (allGroups != null)\r
1449       {\r
1450         for (int i = 0; i < allGroups.length; i++)\r
1451         {\r
1452           if ( (allGroups[i].getStartRes() <= res) &&\r
1453               (allGroups[i].getEndRes() >= res))\r
1454           {\r
1455             stretchGroup = allGroups[i];\r
1456             break;\r
1457           }\r
1458         }\r
1459       }\r
1460 \r
1461       av.setSelectionGroup(stretchGroup);\r
1462 \r
1463     }\r
1464 \r
1465     if (javax.swing.SwingUtilities.isRightMouseButton(evt))\r
1466     {\r
1467       SequenceFeature [] allFeatures = findFeaturesAtRes(sequence.getDatasetSequence(),\r
1468                                                sequence.findPosition(res));\r
1469       Vector links = new Vector();\r
1470       for (int i = 0; i < allFeatures.length; i++)\r
1471       {\r
1472         if (allFeatures[i].links != null)\r
1473         {\r
1474           for (int j = 0; j < allFeatures[i].links.size(); j++)\r
1475           {\r
1476             links.addElement(allFeatures[i].links.elementAt(j));\r
1477           }\r
1478         }\r
1479       }\r
1480 \r
1481       jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(ap, null, links);\r
1482       pop.show(this, evt.getX(), evt.getY());\r
1483       return;\r
1484     }\r
1485 \r
1486     if (av.cursorMode)\r
1487     {\r
1488       seqCanvas.cursorX = findRes(evt);\r
1489       seqCanvas.cursorY = findSeq(evt);\r
1490       seqCanvas.repaint();\r
1491       return;\r
1492     }\r
1493 \r
1494     if (stretchGroup == null)\r
1495     {\r
1496       //Only if left mouse button do we want to change group sizes\r
1497 \r
1498       // define a new group here\r
1499       SequenceGroup sg = new SequenceGroup();\r
1500       sg.setStartRes(res);\r
1501       sg.setEndRes(res);\r
1502       sg.addSequence(sequence, false);\r
1503       av.setSelectionGroup(sg);\r
1504       stretchGroup = sg;\r
1505 \r
1506       if (av.getConservationSelected())\r
1507       {\r
1508         SliderPanel.setConservationSlider(ap,\r
1509                                           av.getGlobalColourScheme(),\r
1510                                           "Background");\r
1511       }\r
1512 \r
1513       if (av.getAbovePIDThreshold())\r
1514       {\r
1515         SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),\r
1516                                        "Background");\r
1517       }\r
1518       if ( (stretchGroup != null) && (stretchGroup.getEndRes() == res))\r
1519       {\r
1520         // Edit end res position of selected group\r
1521         changeEndRes = true;\r
1522       }\r
1523       else if ( (stretchGroup != null) &&\r
1524                (stretchGroup.getStartRes() == res))\r
1525       {\r
1526         // Edit end res position of selected group\r
1527         changeStartRes = true;\r
1528       }\r
1529       stretchGroup.getWidth();\r
1530     }\r
1531 \r
1532     seqCanvas.repaint();\r
1533   }\r
1534 \r
1535   /**\r
1536    * DOCUMENT ME!\r
1537    *\r
1538    * @param evt DOCUMENT ME!\r
1539    */\r
1540   public void doMouseReleasedDefineMode(MouseEvent evt)\r
1541   {\r
1542     if (stretchGroup == null)\r
1543     {\r
1544       return;\r
1545     }\r
1546 \r
1547     if (stretchGroup.cs != null)\r
1548     {\r
1549       if (stretchGroup.cs instanceof ClustalxColourScheme)\r
1550       {\r
1551         ( (ClustalxColourScheme) stretchGroup.cs).resetClustalX(\r
1552             stretchGroup.getSequences(av.hiddenRepSequences),\r
1553             stretchGroup.getWidth());\r
1554       }\r
1555 \r
1556       if (stretchGroup.cs instanceof Blosum62ColourScheme\r
1557           || stretchGroup.cs instanceof PIDColourScheme\r
1558           || stretchGroup.cs.conservationApplied()\r
1559           || stretchGroup.cs.getThreshold() > 0)\r
1560       {\r
1561         stretchGroup.recalcConservation();\r
1562       }\r
1563 \r
1564       if (stretchGroup.cs.conservationApplied())\r
1565       {\r
1566         SliderPanel.setConservationSlider(ap, stretchGroup.cs,\r
1567                                           stretchGroup.getName());\r
1568       }\r
1569       else\r
1570       {\r
1571         SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,\r
1572                                        stretchGroup.getName());\r
1573       }\r
1574       PaintRefresher.Refresh(this, av.getSequenceSetId());\r
1575       ap.paintAlignment(true);\r
1576     }\r
1577 \r
1578     changeEndRes = false;\r
1579     changeStartRes = false;\r
1580     stretchGroup = null;\r
1581 \r
1582   }\r
1583 \r
1584   /**\r
1585    * DOCUMENT ME!\r
1586    *\r
1587    * @param evt DOCUMENT ME!\r
1588    */\r
1589   public void doMouseDraggedDefineMode(MouseEvent evt)\r
1590   {\r
1591     int res = findRes(evt);\r
1592     int y = findSeq(evt);\r
1593 \r
1594     if (wrappedBlock != startWrapBlock)\r
1595     {\r
1596       return;\r
1597     }\r
1598 \r
1599     if (stretchGroup == null)\r
1600     {\r
1601       return;\r
1602     }\r
1603 \r
1604     if (res >= av.alignment.getWidth())\r
1605     {\r
1606       res = av.alignment.getWidth() - 1;\r
1607     }\r
1608 \r
1609     if (stretchGroup.getEndRes() == res)\r
1610     {\r
1611       // Edit end res position of selected group\r
1612       changeEndRes = true;\r
1613     }\r
1614     else if (stretchGroup.getStartRes() == res)\r
1615     {\r
1616       // Edit start res position of selected group\r
1617       changeStartRes = true;\r
1618     }\r
1619 \r
1620     if (res < av.getStartRes())\r
1621     {\r
1622       res = av.getStartRes();\r
1623     }\r
1624 \r
1625     if (changeEndRes)\r
1626     {\r
1627       if (res > (stretchGroup.getStartRes() - 1))\r
1628       {\r
1629         stretchGroup.setEndRes(res);\r
1630       }\r
1631     }\r
1632     else if (changeStartRes)\r
1633     {\r
1634       if (res < (stretchGroup.getEndRes() + 1))\r
1635       {\r
1636         stretchGroup.setStartRes(res);\r
1637       }\r
1638     }\r
1639 \r
1640     int dragDirection = 0;\r
1641 \r
1642     if (y > oldSeq)\r
1643     {\r
1644       dragDirection = 1;\r
1645     }\r
1646     else if (y < oldSeq)\r
1647     {\r
1648       dragDirection = -1;\r
1649     }\r
1650 \r
1651     while ( (y != oldSeq) && (oldSeq > -1) && (y < av.alignment.getHeight()))\r
1652     {\r
1653       // This routine ensures we don't skip any sequences, as the\r
1654       // selection is quite slow.\r
1655       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1656 \r
1657       oldSeq += dragDirection;\r
1658 \r
1659       if (oldSeq < 0)\r
1660       {\r
1661         break;\r
1662       }\r
1663 \r
1664       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1665 \r
1666       if (stretchGroup.getSequences(null).contains(nextSeq))\r
1667       {\r
1668         stretchGroup.deleteSequence(seq, false);\r
1669       }\r
1670       else\r
1671       {\r
1672         if (seq != null)\r
1673         {\r
1674           stretchGroup.addSequence(seq, false);\r
1675         }\r
1676 \r
1677         stretchGroup.addSequence(nextSeq, false);\r
1678       }\r
1679     }\r
1680 \r
1681     if (oldSeq < 0)\r
1682     {\r
1683       oldSeq = -1;\r
1684     }\r
1685 \r
1686     mouseDragging = true;\r
1687 \r
1688     if (scrollThread != null)\r
1689     {\r
1690       scrollThread.setEvent(evt);\r
1691     }\r
1692 \r
1693     seqCanvas.repaint();\r
1694   }\r
1695 \r
1696   void scrollCanvas(MouseEvent evt)\r
1697   {\r
1698     if (evt == null)\r
1699     {\r
1700       if (scrollThread != null)\r
1701       {\r
1702         scrollThread.running = false;\r
1703         scrollThread = null;\r
1704       }\r
1705       mouseDragging = false;\r
1706     }\r
1707     else\r
1708     {\r
1709       if (scrollThread == null)\r
1710       {\r
1711         scrollThread = new ScrollThread();\r
1712       }\r
1713 \r
1714       mouseDragging = true;\r
1715       scrollThread.setEvent(evt);\r
1716     }\r
1717 \r
1718   }\r
1719 \r
1720   // this class allows scrolling off the bottom of the visible alignment\r
1721   class ScrollThread\r
1722       extends Thread\r
1723   {\r
1724     MouseEvent evt;\r
1725     boolean running = false;\r
1726 \r
1727     public ScrollThread()\r
1728     {\r
1729       start();\r
1730     }\r
1731 \r
1732     public void setEvent(MouseEvent e)\r
1733     {\r
1734       evt = e;\r
1735     }\r
1736 \r
1737     public void stopScrolling()\r
1738     {\r
1739       running = false;\r
1740     }\r
1741 \r
1742     public void run()\r
1743     {\r
1744       running = true;\r
1745 \r
1746       while (running)\r
1747       {\r
1748         if (evt != null)\r
1749         {\r
1750           if (mouseDragging && (evt.getY() < 0) &&\r
1751               (av.getStartSeq() > 0))\r
1752           {\r
1753             running = ap.scrollUp(true);\r
1754           }\r
1755 \r
1756           if (mouseDragging && (evt.getY() >= getHeight()) &&\r
1757               (av.alignment.getHeight() > av.getEndSeq()))\r
1758           {\r
1759             running = ap.scrollUp(false);\r
1760           }\r
1761 \r
1762           if (mouseDragging && (evt.getX() < 0))\r
1763           {\r
1764             running = ap.scrollRight(false);\r
1765           }\r
1766           else if (mouseDragging && (evt.getX() >= getWidth()))\r
1767           {\r
1768             running = ap.scrollRight(true);\r
1769           }\r
1770         }\r
1771 \r
1772         try\r
1773         {\r
1774           Thread.sleep(20);\r
1775         }\r
1776         catch (Exception ex)\r
1777         {\r
1778         }\r
1779       }\r
1780     }\r
1781   }\r
1782 }\r