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