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