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