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