Does edit affect any groups?
[jalview.git] / src / jalview / appletgui / SeqPanel.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 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 \r
20 package jalview.appletgui;\r
21 \r
22 import java.util.*;\r
23 \r
24 import java.awt.*;\r
25 import java.awt.event.*;\r
26 \r
27 import jalview.datamodel.*;\r
28 import jalview.schemes.*;\r
29 \r
30 public class SeqPanel\r
31     extends Panel\r
32 {\r
33 \r
34   public SeqCanvas seqCanvas;\r
35   public AlignmentPanel ap;\r
36 \r
37   protected int lastres;\r
38   protected int startseq;\r
39   int startEdit = -1;\r
40   int endEdit = -1;\r
41 \r
42   protected AlignViewport av;\r
43 \r
44   // if character is inserted or deleted, we will need to recalculate the conservation\r
45   int seqEditOccurred = -1;\r
46 \r
47   ScrollThread scrollThread = null;\r
48   boolean mouseDragging = false;\r
49 \r
50   boolean editingSeqs = false;\r
51   boolean groupEditing = false;\r
52 \r
53   public SeqPanel(AlignViewport avp, AlignmentPanel p)\r
54   {\r
55     this.av = avp;\r
56 \r
57     seqCanvas = new SeqCanvas(avp);\r
58     setLayout(new BorderLayout());\r
59     add(seqCanvas);\r
60 \r
61     ap = p;\r
62 \r
63     seqCanvas.addMouseMotionListener(new MouseMotionAdapter()\r
64     {\r
65       public void mouseMoved(MouseEvent evt)\r
66       {\r
67         if (av.getWrapAlignment())\r
68         {\r
69           return;\r
70         }\r
71         doMouseMoved(evt);\r
72       }\r
73 \r
74       public void mouseDragged(MouseEvent evt)\r
75       {\r
76         if (av.getWrapAlignment())\r
77         {\r
78           return;\r
79         }\r
80         if (editingSeqs)\r
81         {\r
82           doMouseDragged(evt);\r
83         }\r
84         else\r
85         {\r
86           doMouseDraggedDefineMode(evt);\r
87         }\r
88       }\r
89     });\r
90 \r
91     seqCanvas.addMouseListener(new MouseAdapter()\r
92     {\r
93       public void mouseReleased(MouseEvent evt)\r
94       {\r
95         if (av.getWrapAlignment())\r
96         {\r
97           return;\r
98         }\r
99         if (editingSeqs)\r
100         {\r
101           doMouseReleased(evt);\r
102         }\r
103         else\r
104         {\r
105           doMouseReleasedDefineMode(evt);\r
106         }\r
107 \r
108       }\r
109 \r
110       public void mousePressed(MouseEvent evt)\r
111       {\r
112         if (av.getWrapAlignment())\r
113         {\r
114           return;\r
115         }\r
116         if (evt.isShiftDown() || evt.isAltDown() || evt.isControlDown())\r
117         {\r
118           if (evt.isAltDown() || evt.isControlDown())\r
119           {\r
120             groupEditing = true;\r
121           }\r
122 \r
123           editingSeqs = true;\r
124           doMousePressed(evt);\r
125         }\r
126         else\r
127         {\r
128           doMousePressedDefineMode(evt);\r
129         }\r
130       }\r
131 \r
132       public void mouseExited(MouseEvent evt)\r
133       {\r
134         if (av.getWrapAlignment() || editingSeqs)\r
135         {\r
136           return;\r
137         }\r
138         doMouseExitedDefineMode(evt);\r
139 \r
140       }\r
141 \r
142       public void mouseEntered(MouseEvent evt)\r
143       {\r
144         if (av.getWrapAlignment() || editingSeqs)\r
145         {\r
146           return;\r
147         }\r
148         doMouseEnteredDefineMode(evt);\r
149       }\r
150 \r
151     });\r
152     seqCanvas.repaint();\r
153   }\r
154 \r
155   public void doMouseReleased(MouseEvent evt)\r
156   {\r
157 \r
158     if (seqEditOccurred > -1)\r
159     {\r
160       editOccurred(seqEditOccurred);\r
161     }\r
162 \r
163     startseq = -1;\r
164     lastres = -1;\r
165     seqEditOccurred = -1;\r
166     editingSeqs = false;\r
167     groupEditing = false;\r
168     ap.repaint();\r
169   }\r
170 \r
171   public void doMousePressed(MouseEvent evt)\r
172   {\r
173     ap.alignFrame.addHistoryItem(new HistoryItem(\r
174         "Edit Sequence", av.alignment, HistoryItem.EDIT));\r
175     int seq;\r
176     int res;\r
177 \r
178     int x = evt.getX();\r
179     int y = evt.getY();\r
180 \r
181     res = x / av.getCharWidth() + av.getStartRes();\r
182     seq = y / av.getCharHeight() + av.getStartSeq();\r
183 \r
184     if (seq < av.getAlignment().getHeight() &&\r
185         res < av.getAlignment().getSequenceAt(seq).getLength())\r
186     {\r
187       //char resstr = align.getSequenceAt(seq).getSequence().charAt(res);\r
188       // Find the residue's position in the sequence (res is the position\r
189       // in the alignment\r
190 \r
191       startseq = seq;\r
192       lastres = res;\r
193     }\r
194     else\r
195     {\r
196       startseq = -1;\r
197       lastres = -1;\r
198     }\r
199 \r
200     return;\r
201   }\r
202 \r
203   public void doMouseMoved(MouseEvent evt)\r
204   {\r
205     int res = 0, seq = 0;\r
206     int x = evt.getX();\r
207     int y = evt.getY();\r
208     if (av.wrapAlignment)\r
209     {\r
210       y -= 2 * av.charHeight;\r
211       int chunkHeight = (av.getAlignment().getHeight() + 2) * av.charHeight;\r
212 \r
213       res = (int) ( (y / chunkHeight) * (getSize().width / av.charWidth)) +\r
214           x / av.getCharWidth() + av.getStartRes();\r
215 \r
216       y %= chunkHeight;\r
217       seq = y / av.getCharHeight() + av.getStartSeq();\r
218 \r
219     }\r
220     else\r
221     {\r
222       res = x / av.getCharWidth() + av.getStartRes();\r
223       seq = y / av.getCharHeight() + av.getStartSeq();\r
224     }\r
225 \r
226     if (seq >= av.getAlignment().getHeight())\r
227     {\r
228       return;\r
229     }\r
230 \r
231     SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
232     if (res > sequence.getLength())\r
233     {\r
234       return;\r
235     }\r
236 \r
237     Object obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");\r
238     String aa = "";\r
239     if (obj != null)\r
240     {\r
241       aa = obj.toString();\r
242     }\r
243 \r
244     StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " +\r
245                                          sequence.getName());\r
246     if (aa != "")\r
247     {\r
248       text.append("  Residue: " + aa + " (" +\r
249                   av.getAlignment().getSequenceAt(seq).findPosition(res) + ")");\r
250     }\r
251 \r
252     ap.alignFrame.statusBar.setText(text.toString());\r
253 \r
254     // use aa to see if the mouse pointer is on a\r
255     if (av.showSequenceFeatures)\r
256     {\r
257       Vector features = sequence.getSequenceFeatures();\r
258       Enumeration e = features.elements();\r
259       StringBuffer sbuffer = new StringBuffer();\r
260 \r
261       while (e.hasMoreElements())\r
262       {\r
263         SequenceFeature sf = (SequenceFeature) e.nextElement();\r
264         if (sf.getStart() <= sequence.findPosition(res) &&\r
265             sf.getEnd() >= sequence.findPosition(res))\r
266         {\r
267           if (sbuffer.length() > 0)\r
268           {\r
269             sbuffer.append("; ");\r
270           }\r
271           sbuffer.append(sf.getType() + " " + sf.getDescription());\r
272           if (sf.getStatus().length() > 0)\r
273           {\r
274             sbuffer.append(" (" + sf.getStatus() + ")");\r
275           }\r
276         }\r
277 \r
278       }\r
279     }\r
280 \r
281   }\r
282 \r
283   public void doMouseDragged(MouseEvent evt)\r
284   {\r
285 \r
286     // If we're dragging we're editing\r
287     int res = evt.getX() / av.getCharWidth() + av.getStartRes();\r
288     if (res < 0)\r
289     {\r
290       res = 0;\r
291     }\r
292 \r
293     if (lastres == -1 || lastres == res)\r
294     {\r
295       return;\r
296     }\r
297 \r
298     boolean dragRight = true;\r
299     if (res < av.getAlignment().getWidth() && res < lastres)\r
300     {\r
301       dragRight = false;\r
302     }\r
303 \r
304     if (res != lastres)\r
305     {\r
306       // Group editing\r
307       if (groupEditing)\r
308       {\r
309         SequenceGroup sg = av.getSelectionGroup();\r
310         if (sg == null)\r
311         {\r
312           lastres = -1;\r
313           return;\r
314         }\r
315 \r
316         // drag to right\r
317         if (dragRight)\r
318         {\r
319           sg.setEndRes(sg.getEndRes() + (res - lastres));\r
320         }\r
321 \r
322         // drag to left\r
323         else\r
324         {\r
325           /// Are we able to delete?\r
326           // ie are all columns blank?\r
327           boolean deleteAllowed = false;\r
328           for (int s = 0; s < sg.getSize(); s++)\r
329           {\r
330             SequenceI seq = sg.getSequenceAt(s);\r
331             for (int j = res; j < lastres; j++)\r
332             {\r
333               if (seq.getSequence().length() <= j)\r
334               {\r
335                 continue;\r
336               }\r
337 \r
338               if (!jalview.util.Comparison.isGap(seq.getSequence().charAt(j)))\r
339               {\r
340                 // Not a gap, block edit not valid\r
341                 res = j + 1;\r
342                 deleteAllowed = false;\r
343                 continue;\r
344               }\r
345               deleteAllowed = true;\r
346             }\r
347           }\r
348 \r
349           if (!deleteAllowed)\r
350           {\r
351             lastres = -1;\r
352             return;\r
353           }\r
354 \r
355           sg.setEndRes(sg.getEndRes() - (lastres - res));\r
356         }\r
357 \r
358         for (int i = 0; i < sg.getSize(); i++)\r
359         {\r
360           SequenceI s = sg.getSequenceAt(i);\r
361           int k = av.alignment.findIndex(s);\r
362 \r
363           // drag to right\r
364           if (dragRight)\r
365           {\r
366             for (int j = lastres; j < res; j++)\r
367             {\r
368               insertChar(j, k);\r
369             }\r
370           }\r
371 \r
372           // drag to left\r
373           else\r
374           {\r
375             for (int j = res; j < lastres; j++)\r
376             {\r
377               if (s.getLength() > j)\r
378               {\r
379                 deleteChar(res, k);\r
380               }\r
381             }\r
382           }\r
383         }\r
384       }\r
385       else /////Editing a single sequence///////////\r
386       {\r
387         if (res < av.getAlignment().getWidth() && res > lastres)\r
388         {\r
389           // dragging to the right\r
390           for (int j = lastres; j < res; j++)\r
391           {\r
392             insertChar(j, startseq);\r
393           }\r
394         }\r
395         else if (res < av.getAlignment().getWidth() && res < lastres)\r
396         {\r
397           // dragging to the left\r
398           for (int j = lastres; j > res; j--)\r
399           {\r
400             if (jalview.util.Comparison.isGap(\r
401                 av.alignment.getSequenceAt(startseq).getSequence().charAt(res)))\r
402             {\r
403 \r
404               deleteChar(res, startseq);\r
405             }\r
406             else\r
407             {\r
408 \r
409               break;\r
410             }\r
411           }\r
412         }\r
413 \r
414       }\r
415     }\r
416 \r
417     endEdit = res;\r
418     lastres = res;\r
419     seqCanvas.repaint();\r
420   }\r
421 \r
422   public void drawChars(int seqstart, int seqend, int start)\r
423   {\r
424     seqCanvas.drawPanel(seqCanvas.gg, start, av.getEndRes(), seqstart, seqend,\r
425                         av.getStartRes(), av.getStartSeq(), 0);\r
426     seqCanvas.repaint();\r
427   }\r
428 \r
429   public void insertChar(int j, int seq)\r
430   {\r
431     av.alignment.getSequenceAt(seq).insertCharAt(j, av.getGapCharacter());\r
432     seqEditOccurred = seq;\r
433   }\r
434 \r
435   public void deleteChar(int j, int seq)\r
436   {\r
437 \r
438     av.alignment.getSequenceAt(seq).deleteCharAt(j);\r
439     seqEditOccurred = seq;\r
440     av.alignment.getWidth();\r
441     repaint();\r
442   }\r
443 \r
444   void editOccurred(int i)\r
445   {\r
446     if (endEdit == startEdit)\r
447     {\r
448         ap.alignFrame.historyList.pop();\r
449         ap.alignFrame.updateEditMenuBar();\r
450     }\r
451 \r
452     av.updateConservation();\r
453     av.updateConsensus();\r
454 \r
455 \r
456     //Does the edit affect any groups?\r
457     //////////////////////////////\r
458     Vector groupsToUpdate = new Vector();\r
459     SequenceGroup sg = av.getSelectionGroup();\r
460 \r
461     int s, g, gSize = av.alignment.getGroups().size();\r
462     if (sg != null)\r
463     {\r
464       groupsToUpdate.addElement(av.getSelectionGroup());\r
465       int sSize = sg.getSize();\r
466       for (s = 0; s < sSize; s++)\r
467       {\r
468         SequenceI seq = sg.getSequenceAt(s);\r
469 \r
470         for (g = 0; g < gSize; g++)\r
471         {\r
472           SequenceGroup sg2 = (SequenceGroup) av.alignment.getGroups().\r
473               elementAt(\r
474                   g);\r
475           if (sg2.sequences.contains(seq))\r
476           {\r
477             if (!groupsToUpdate.contains(sg2))\r
478               groupsToUpdate.addElement(sg2);\r
479             continue;\r
480           }\r
481         }\r
482       }\r
483     }\r
484     else\r
485     {\r
486       SequenceI seq = av.alignment.getSequenceAt(startseq);\r
487       for (g = 0; g < gSize; g++)\r
488       {\r
489         SequenceGroup sg2 = (SequenceGroup) av.alignment.getGroups().elementAt(\r
490             g);\r
491         if (sg2.sequences.contains(seq))\r
492         {\r
493           if (!groupsToUpdate.contains(sg2))\r
494             groupsToUpdate.addElement(sg2);\r
495           continue;\r
496         }\r
497       }\r
498     }\r
499 \r
500     gSize = groupsToUpdate.size();\r
501     for (g = 0; g < gSize; g++)\r
502     {\r
503       ( (SequenceGroup) groupsToUpdate.elementAt(g)).recalcConservation();\r
504     }\r
505     ////End of updating groups\r
506       //////////////////////////////////////////\r
507 \r
508 \r
509     // Y O Y CLUSTALX\r
510     ColourSchemeI cs = av.getGlobalColourScheme();\r
511 \r
512     if (av.getSelectionGroup() != null)\r
513     {\r
514         av.getSelectionGroup().recalcConservation();\r
515     }\r
516 \r
517     if(cs!=null)\r
518     {\r
519       if (cs.conservationApplied())\r
520       {\r
521         jalview.analysis.Conservation c = new jalview.analysis.Conservation("All",\r
522                                           ResidueProperties.propHash, 3,\r
523                                           av.alignment.getSequences(),\r
524                                           0, av.alignment.getWidth() - 1);\r
525         c.calculate();\r
526         c.verdict(false, av.ConsPercGaps);\r
527 \r
528         if (cs instanceof ClustalxColourScheme)\r
529         {\r
530           ClustalxColourScheme cxs = (ClustalxColourScheme) cs;\r
531           cxs.resetClustalX(av.alignment.getSequences(),\r
532                             av.alignment.getWidth());\r
533           cs.setConservation(c);\r
534           av.setGlobalColourScheme(cs);\r
535         }\r
536         else\r
537         {\r
538           cs.setConservation(c);\r
539           av.setGlobalColourScheme(cs);\r
540         }\r
541 \r
542       }\r
543 \r
544       if (cs instanceof ClustalxColourScheme)\r
545       {\r
546         ( (ClustalxColourScheme) cs).resetClustalX(av.alignment.getSequences(),\r
547                                                    av.alignment.getWidth());\r
548         av.setGlobalColourScheme(cs);\r
549       }\r
550         }\r
551   }\r
552 \r
553 //////////////////////////////////////////\r
554 /////Everything below this is for defining the boundary of the rubberband\r
555 //////////////////////////////////////////\r
556   int oldSeq = -1;\r
557   public void doMousePressedDefineMode(MouseEvent evt)\r
558   {\r
559     int res = evt.getX() / av.getCharWidth() + av.getStartRes();\r
560     int seq = evt.getY() / av.getCharHeight() + av.getStartSeq();\r
561     oldSeq = seq;\r
562 \r
563     SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
564 \r
565     if (sequence == null || res > sequence.getLength())\r
566     {\r
567       return;\r
568     }\r
569 \r
570     stretchGroup = av.getSelectionGroup();\r
571 \r
572     if (stretchGroup == null)\r
573     {\r
574       stretchGroup = av.alignment.findGroup(sequence);\r
575       if (stretchGroup != null && res > stretchGroup.getStartRes() &&\r
576           res < stretchGroup.getEndRes())\r
577       {\r
578         av.setSelectionGroup(stretchGroup);\r
579       }\r
580       else\r
581       {\r
582         stretchGroup = null;\r
583       }\r
584     }\r
585 \r
586     else if (!stretchGroup.sequences.contains(sequence)\r
587              || stretchGroup.getStartRes() > res\r
588              || stretchGroup.getEndRes() < res)\r
589     {\r
590       stretchGroup = null;\r
591 \r
592       SequenceGroup[] allGroups = av.alignment.findAllGroups(sequence);\r
593 \r
594       if (allGroups != null)\r
595       {\r
596         for (int i = 0; i < allGroups.length; i++)\r
597         {\r
598           if (allGroups[i].getStartRes() <= res &&\r
599               allGroups[i].getEndRes() >= res)\r
600           {\r
601             stretchGroup = allGroups[i];\r
602             av.setSelectionGroup(stretchGroup);\r
603             break;\r
604           }\r
605         }\r
606       }\r
607     }\r
608 \r
609     if (stretchGroup == null)\r
610     {\r
611       // define a new group here\r
612       SequenceGroup sg = new SequenceGroup();\r
613       sg.setStartRes(res);\r
614       sg.setEndRes(res);\r
615       sg.addSequence(sequence, false);\r
616       av.setSelectionGroup(sg);\r
617       stretchGroup = sg;\r
618 \r
619       if (av.getConservationSelected())\r
620       {\r
621         SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(),\r
622                                           "Background");\r
623       }\r
624       if (av.getAbovePIDThreshold())\r
625       {\r
626         SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),\r
627                                        "Background");\r
628       }\r
629 \r
630     }\r
631 \r
632     // DETECT RIGHT MOUSE BUTTON IN AWT\r
633     else if ( (evt.getModifiers() & InputEvent.BUTTON3_MASK) ==\r
634              InputEvent.BUTTON3_MASK)\r
635     {\r
636       APopupMenu popup = new APopupMenu(ap, null, null);\r
637       this.add(popup);\r
638       popup.show(this, evt.getX(), evt.getY());\r
639     }\r
640 \r
641     if (stretchGroup != null && stretchGroup.getEndRes() == res)\r
642     {\r
643       // Edit end res position of selected group\r
644       changeEndRes = true;\r
645     }\r
646 \r
647     else if (stretchGroup != null && stretchGroup.getStartRes() == res)\r
648     {\r
649       // Edit end res position of selected group\r
650       changeStartRes = true;\r
651     }\r
652 \r
653   }\r
654 \r
655   boolean changeEndSeq = false;\r
656   boolean changeStartSeq = false;\r
657   boolean changeEndRes = false;\r
658   boolean changeStartRes = false;\r
659   SequenceGroup stretchGroup = null;\r
660 \r
661   public void doMouseReleasedDefineMode(MouseEvent evt)\r
662   {\r
663     if(mouseDragging)\r
664      {\r
665        stretchGroup.recalcConservation();\r
666        mouseDragging = false;\r
667      }\r
668 \r
669     if (stretchGroup == null)\r
670     {\r
671       return;\r
672     }\r
673 \r
674     if(stretchGroup.cs!=null)\r
675     {\r
676       if (stretchGroup.cs.conservationApplied())\r
677       {\r
678         SliderPanel.setConservationSlider(ap, stretchGroup.cs,\r
679                                           stretchGroup.getName());\r
680       }\r
681       else\r
682       {\r
683         SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,\r
684                                        stretchGroup.getName());\r
685       }\r
686     }\r
687     changeEndRes = false;\r
688     changeStartRes = false;\r
689     stretchGroup = null;\r
690     seqCanvas.repaint();\r
691     ap.repaint();\r
692   }\r
693 \r
694   boolean remove = false;\r
695   public void doMouseDraggedDefineMode(MouseEvent evt)\r
696   {\r
697     int res = evt.getX() / av.getCharWidth() + av.getStartRes();\r
698     int y = evt.getY() / av.getCharHeight() + av.getStartSeq();\r
699 \r
700     if (stretchGroup == null)\r
701     {\r
702       return;\r
703     }\r
704 \r
705     if (res > av.alignment.getWidth())\r
706     {\r
707       res = av.alignment.getWidth() - 1;\r
708     }\r
709 \r
710     if (stretchGroup.getEndRes() == res)\r
711     {\r
712       // Edit end res position of selected group\r
713       changeEndRes = true;\r
714     }\r
715 \r
716     else if (stretchGroup.getStartRes() == res)\r
717     {\r
718       // Edit start res position of selected group\r
719       changeStartRes = true;\r
720     }\r
721 \r
722     if (res < av.getStartRes())\r
723     {\r
724       res = av.getStartRes();\r
725     }\r
726     else if (res > av.getEndRes())\r
727     {\r
728       res = av.getEndRes();\r
729     }\r
730 \r
731     if (changeEndRes)\r
732     {\r
733       if (res > stretchGroup.getStartRes() - 1)\r
734       {\r
735         stretchGroup.setEndRes(res);\r
736       }\r
737     }\r
738     else if (changeStartRes)\r
739     {\r
740       if (res < stretchGroup.getEndRes() + 1)\r
741       {\r
742         stretchGroup.setStartRes(res);\r
743       }\r
744     }\r
745 \r
746     int dragDirection = 0;\r
747     if (y > oldSeq)\r
748     {\r
749       dragDirection = 1;\r
750     }\r
751     else if (y < oldSeq)\r
752     {\r
753       dragDirection = -1;\r
754     }\r
755 \r
756     while (y != oldSeq && oldSeq > 0 && y < av.alignment.getHeight())\r
757     {\r
758       // This routine ensures we don't skip any sequences, as the\r
759       // selection is quite slow.\r
760       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
761 \r
762       oldSeq += dragDirection;\r
763       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
764 \r
765       if (stretchGroup.sequences.contains(nextSeq))\r
766       {\r
767         stretchGroup.deleteSequence(seq, false);\r
768       }\r
769       else\r
770       {\r
771         if (seq != null)\r
772         {\r
773           stretchGroup.addSequence(seq, false);\r
774         }\r
775         stretchGroup.addSequence(nextSeq, false);\r
776       }\r
777     }\r
778     oldSeq = y;\r
779     mouseDragging = true;\r
780     if (scrollThread != null)\r
781     {\r
782       scrollThread.setEvent(evt);\r
783     }\r
784 \r
785     seqCanvas.repaint();\r
786   }\r
787 \r
788   public void doMouseEnteredDefineMode(MouseEvent e)\r
789   {\r
790     if (scrollThread != null)\r
791     {\r
792       scrollThread.running = false;\r
793     }\r
794   }\r
795 \r
796   public void doMouseExitedDefineMode(MouseEvent e)\r
797   {\r
798     if (av.getWrapAlignment())\r
799     {\r
800       return;\r
801     }\r
802 \r
803     if (mouseDragging)\r
804     {\r
805       scrollThread = new ScrollThread();\r
806     }\r
807 \r
808   }\r
809 \r
810   // this class allows scrolling off the bottom of the visible alignment\r
811   class ScrollThread\r
812       extends Thread\r
813   {\r
814     MouseEvent evt;\r
815     boolean running = false;\r
816     public ScrollThread()\r
817     {\r
818       start();\r
819     }\r
820 \r
821     public void setEvent(MouseEvent e)\r
822     {\r
823       evt = e;\r
824     }\r
825 \r
826     public void stopScrolling()\r
827     {\r
828       running = false;\r
829     }\r
830 \r
831     public void run()\r
832     {\r
833       running = true;\r
834       while (running)\r
835       {\r
836         if (evt != null)\r
837         {\r
838 \r
839           if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)\r
840           {\r
841             running = ap.scrollUp(true);\r
842           }\r
843 \r
844           if (mouseDragging && evt.getY() >= getSize().height &&\r
845               av.alignment.getHeight() > av.getEndSeq())\r
846           {\r
847             running = ap.scrollUp(false);\r
848           }\r
849 \r
850           if (mouseDragging && evt.getX() < 0)\r
851           {\r
852             running = ap.scrollRight(true);\r
853           }\r
854 \r
855           else if (mouseDragging && evt.getX() >= getSize().width)\r
856           {\r
857             running = ap.scrollRight(false);\r
858           }\r
859         }\r
860 \r
861         try\r
862         {\r
863           Thread.sleep(75);\r
864         }\r
865         catch (Exception ex)\r
866         {}\r
867       }\r
868     }\r
869   }\r
870 \r
871 }\r