paintrefresher
[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     PaintRefresher.Refresh(av.alignment);\r
663   }\r
664 \r
665   public void doMouseDraggedDefineMode(MouseEvent evt)\r
666   {\r
667     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();\r
668     int y = (evt.getY() / av.getCharHeight()) + av.getStartSeq();\r
669 \r
670     if (stretchGroup == null)\r
671     {\r
672       return;\r
673     }\r
674 \r
675     if (res > av.alignment.getWidth())\r
676     {\r
677       res = av.alignment.getWidth() - 1;\r
678     }\r
679 \r
680     if (stretchGroup.getEndRes() == res)\r
681     {\r
682       // Edit end res position of selected group\r
683       changeEndRes = true;\r
684     }\r
685     else if (stretchGroup.getStartRes() == res)\r
686     {\r
687       // Edit start res position of selected group\r
688       changeStartRes = true;\r
689     }\r
690 \r
691     if (res < av.getStartRes())\r
692     {\r
693       res = av.getStartRes();\r
694     }\r
695     else if (res > av.getEndRes())\r
696     {\r
697       res = av.getEndRes();\r
698     }\r
699 \r
700     if (changeEndRes)\r
701     {\r
702       if (res > (stretchGroup.getStartRes() - 1))\r
703       {\r
704         stretchGroup.setEndRes(res);\r
705       }\r
706     }\r
707     else if (changeStartRes)\r
708     {\r
709       if (res < (stretchGroup.getEndRes() + 1))\r
710       {\r
711         stretchGroup.setStartRes(res);\r
712       }\r
713     }\r
714 \r
715     int dragDirection = 0;\r
716 \r
717     if (y > oldSeq)\r
718     {\r
719       dragDirection = 1;\r
720     }\r
721     else if (y < oldSeq)\r
722     {\r
723       dragDirection = -1;\r
724     }\r
725 \r
726     while ( (y != oldSeq) && (oldSeq > 0) && (y < av.alignment.getHeight()))\r
727     {\r
728       // This routine ensures we don't skip any sequences, as the\r
729       // selection is quite slow.\r
730       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
731 \r
732       oldSeq += dragDirection;\r
733 \r
734       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
735 \r
736       if (stretchGroup.sequences.contains(nextSeq))\r
737       {\r
738           stretchGroup.deleteSequence(seq, false);\r
739       }\r
740       else\r
741       {\r
742         if (seq != null)\r
743         {\r
744           stretchGroup.addSequence(seq, false);\r
745         }\r
746         stretchGroup.addSequence(nextSeq, false);\r
747       }\r
748     }\r
749 \r
750     oldSeq = y;\r
751     mouseDragging = true;\r
752 \r
753     if (scrollThread != null)\r
754     {\r
755       scrollThread.setEvent(evt);\r
756     }\r
757 \r
758     seqCanvas.repaint();\r
759   }\r
760 \r
761   public void doMouseEnteredDefineMode(MouseEvent e)\r
762   {\r
763     if (scrollThread != null)\r
764     {\r
765       scrollThread.running = false;\r
766     }\r
767   }\r
768 \r
769   public void doMouseExitedDefineMode(MouseEvent e)\r
770   {\r
771     if (av.getWrapAlignment())\r
772     {\r
773       return;\r
774     }\r
775 \r
776     if (mouseDragging)\r
777     {\r
778       scrollThread = new ScrollThread();\r
779     }\r
780   }\r
781 \r
782   // this class allows scrolling off the bottom of the visible alignment\r
783   class ScrollThread\r
784       extends Thread\r
785   {\r
786     MouseEvent evt;\r
787     boolean running = false;\r
788 \r
789     public ScrollThread()\r
790     {\r
791       start();\r
792     }\r
793 \r
794     public void setEvent(MouseEvent e)\r
795     {\r
796       evt = e;\r
797     }\r
798 \r
799     public void stopScrolling()\r
800     {\r
801       running = false;\r
802     }\r
803 \r
804     public void run()\r
805     {\r
806       running = true;\r
807 \r
808       while (running)\r
809       {\r
810         if (evt != null)\r
811         {\r
812           if (mouseDragging && (evt.getY() < 0) &&\r
813               (av.getStartSeq() > 0))\r
814           {\r
815             running = ap.scrollUp(true);\r
816           }\r
817 \r
818           if (mouseDragging && (evt.getY() >= getHeight()) &&\r
819               (av.alignment.getHeight() > av.getEndSeq()))\r
820           {\r
821             running = ap.scrollUp(false);\r
822           }\r
823 \r
824           if (mouseDragging && (evt.getX() < 0))\r
825           {\r
826             running = ap.scrollRight(true);\r
827           }\r
828           else if (mouseDragging && (evt.getX() >= getWidth()))\r
829           {\r
830             running = ap.scrollRight(false);\r
831           }\r
832         }\r
833 \r
834         try\r
835         {\r
836           Thread.sleep(75);\r
837         }\r
838         catch (Exception ex)\r
839         {\r
840         }\r
841       }\r
842     }\r
843   }\r
844 }\r