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