CursoMode added
[jalview.git] / src / jalview / gui / SeqPanel.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.gui;\r
20 \r
21 import 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 \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     // if character is inserted or deleted, we will need to recalculate the conservation\r
51     boolean seqEditOccurred = false;\r
52     ScrollThread scrollThread = null;\r
53     boolean mouseDragging = false;\r
54     boolean editingSeqs = false;\r
55     boolean groupEditing = false;\r
56 \r
57     //////////////////////////////////////////\r
58     /////Everything below this is for defining the boundary of the rubberband\r
59     //////////////////////////////////////////\r
60     int oldSeq = -1;\r
61     boolean changeEndSeq = false;\r
62     boolean changeStartSeq = false;\r
63     boolean changeEndRes = false;\r
64     boolean changeStartRes = false;\r
65     SequenceGroup stretchGroup = null;\r
66     boolean remove = false;\r
67 \r
68     boolean mouseWheelPressed = false;\r
69 \r
70     /**\r
71      * Creates a new SeqPanel object.\r
72      *\r
73      * @param avp DOCUMENT ME!\r
74      * @param p DOCUMENT ME!\r
75      */\r
76     public SeqPanel(AlignViewport avp, AlignmentPanel p)\r
77     {\r
78         ToolTipManager.sharedInstance().registerComponent(this);\r
79         ToolTipManager.sharedInstance().setInitialDelay(0);\r
80         ToolTipManager.sharedInstance().setDismissDelay(10000);\r
81         this.av = avp;\r
82         setBackground(Color.white);\r
83 \r
84         seqCanvas = new SeqCanvas(avp);\r
85         setLayout(new BorderLayout());\r
86         add(seqCanvas, BorderLayout.CENTER);\r
87 \r
88         ap = p;\r
89 \r
90         if(!av.isDataset())\r
91         {\r
92           addMouseMotionListener(this);\r
93           addMouseListener(this);\r
94           addMouseWheelListener(this);\r
95         }\r
96     }\r
97 \r
98     int startWrapBlock=-1;\r
99     int wrappedBlock=-1;\r
100     int findRes(MouseEvent evt)\r
101    {\r
102      int res = 0;\r
103      int x = evt.getX();\r
104 \r
105     if (av.wrapAlignment)\r
106     {\r
107 \r
108       int hgap = av.charHeight;\r
109       if (av.scaleAboveWrapped)\r
110         hgap += av.charHeight;\r
111 \r
112       int cHeight = av.getAlignment().getHeight() * av.charHeight\r
113           + hgap + seqCanvas.getAnnotationHeight();\r
114 \r
115         int y = evt.getY();\r
116         y -= hgap;\r
117         x -= seqCanvas.LABEL_WEST;\r
118 \r
119 \r
120         int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());\r
121 \r
122         wrappedBlock = y / cHeight;\r
123         wrappedBlock += av.getStartRes() / cwidth;\r
124 \r
125         res = wrappedBlock * cwidth + x / av.getCharWidth();\r
126 \r
127     }\r
128     else\r
129     {\r
130         res = (x / av.getCharWidth()) + av.getStartRes();\r
131     }\r
132 \r
133     if(av.hasHiddenColumns)\r
134           res = av.getColumnSelection().adjustForHiddenColumns(res);\r
135 \r
136     return res;\r
137 \r
138    }\r
139 \r
140    int findSeq(MouseEvent evt)\r
141    {\r
142 \r
143      int seq = 0;\r
144      int y = evt.getY();\r
145 \r
146      if (av.wrapAlignment)\r
147      {\r
148        int hgap = av.charHeight;\r
149        if (av.scaleAboveWrapped)\r
150          hgap += av.charHeight;\r
151 \r
152        int cHeight = av.getAlignment().getHeight() * av.charHeight\r
153            + hgap + seqCanvas.getAnnotationHeight();\r
154 \r
155          y -= hgap;\r
156 \r
157        seq = ( (y % cHeight) / av.getCharHeight());\r
158      }\r
159      else\r
160      {\r
161        seq = (y / av.getCharHeight()) + av.getStartSeq();\r
162      }\r
163 \r
164      return seq;\r
165    }\r
166 \r
167    void endEditing()\r
168    {\r
169      startseq = -1;\r
170      lastres = -1;\r
171      seqEditOccurred = false;\r
172      editingSeqs = false;\r
173      groupEditing = false;\r
174      keyboardGaps = null;\r
175     }\r
176 \r
177 \r
178     void moveCursor(int dx, int dy)\r
179     {\r
180       seqCanvas.cursorX += dx;\r
181       seqCanvas.cursorY += dy;\r
182 \r
183       if(seqCanvas.cursorX<0)\r
184         seqCanvas.cursorX = 0;\r
185       else if(seqCanvas.cursorX>av.alignment.getWidth()-1)\r
186         seqCanvas.cursorX = av.alignment.getWidth()-1;\r
187       if(seqCanvas.cursorX<av.startRes)\r
188         ap.scrollRight(false);\r
189       else if(seqCanvas.cursorX>av.endRes)\r
190         ap.scrollRight(true);\r
191 \r
192       if(seqCanvas.cursorY<0)\r
193         seqCanvas.cursorY=0;\r
194       else if(seqCanvas.cursorY>av.alignment.getHeight()-1)\r
195         seqCanvas.cursorY = av.alignment.getHeight()-1;\r
196       if(seqCanvas.cursorY<av.startSeq)\r
197         ap.scrollUp(true);\r
198       else if(seqCanvas.cursorY+1>av.endSeq)\r
199         ap.scrollUp(false);\r
200 \r
201       setStatusMessage(av.alignment.getSequenceAt(seqCanvas.cursorY),\r
202                        seqCanvas.cursorX, seqCanvas.cursorY);\r
203       seqCanvas.repaint();\r
204     }\r
205 \r
206     void setSelectionAreaAtCursor(boolean topLeft)\r
207     {\r
208       SequenceI sequence =\r
209           (Sequence) av.getAlignment().getSequenceAt(seqCanvas.cursorY);\r
210 \r
211       if(av.getSelectionGroup()!=null)\r
212       {\r
213         SequenceGroup sg = av.selectionGroup;\r
214         //Find the top and bottom of this group\r
215         int min = av.alignment.getHeight(), max = 0;\r
216         for(int i=0; i<sg.getSize(); i++)\r
217         {\r
218           int index = av.alignment.findIndex( sg.getSequenceAt(i) );\r
219           if(index > max)\r
220             max = index;\r
221           if(index < min)\r
222             min = index;\r
223         }\r
224 \r
225         max ++;\r
226 \r
227         if(topLeft)\r
228         {\r
229           sg.setStartRes(seqCanvas.cursorX);\r
230           if(sg.getEndRes()<seqCanvas.cursorX)\r
231             sg.setEndRes(seqCanvas.cursorX);\r
232 \r
233           min = seqCanvas.cursorY;\r
234         }\r
235         else\r
236         {\r
237           sg.setEndRes(seqCanvas.cursorX);\r
238           if(sg.getStartRes()>seqCanvas.cursorX)\r
239             sg.setStartRes(seqCanvas.cursorX);\r
240 \r
241           max = seqCanvas.cursorY+1;\r
242         }\r
243 \r
244         if(min>max)\r
245         {\r
246           // Only the user can do this\r
247           av.setSelectionGroup(null);\r
248         }\r
249         else\r
250         {\r
251           // Now add any sequences between min and max\r
252           sg.sequences.clear();\r
253           for (int i = min; i < max; i++)\r
254           {\r
255             sg.addSequence(av.alignment.getSequenceAt(i), false);\r
256           }\r
257         }\r
258       }\r
259 \r
260       if (av.getSelectionGroup() == null)\r
261       {\r
262         SequenceGroup sg = new SequenceGroup();\r
263         sg.setStartRes(seqCanvas.cursorX);\r
264         sg.setEndRes(seqCanvas.cursorX);\r
265         sg.addSequence(sequence, false);\r
266         av.setSelectionGroup(sg);\r
267       }\r
268 \r
269 \r
270       ap.repaint();\r
271     }\r
272 \r
273     void insertGapAtCursor(boolean group)\r
274     {\r
275       ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence",\r
276                                                    av.alignment, HistoryItem.EDIT));\r
277       groupEditing = group;\r
278       startseq = seqCanvas.cursorY;\r
279       lastres = seqCanvas.cursorX;\r
280       editSequence(true, seqCanvas.cursorX+getKeyboardGaps());\r
281       editOccurred();\r
282     }\r
283 \r
284     void deleteGapAtCursor(boolean group)\r
285     {\r
286       ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence",\r
287                                                    av.alignment, HistoryItem.EDIT));\r
288       groupEditing = group;\r
289       startseq = seqCanvas.cursorY;\r
290       lastres = seqCanvas.cursorX+getKeyboardGaps();\r
291       editSequence(false, seqCanvas.cursorX);\r
292       editOccurred();\r
293     }\r
294 \r
295     void numberPressed(char value)\r
296     {\r
297       if(keyboardGaps==null)\r
298         keyboardGaps = new StringBuffer();\r
299 \r
300       keyboardGaps.append(value);\r
301     }\r
302 \r
303     StringBuffer keyboardGaps;\r
304     int getKeyboardGaps()\r
305     {\r
306       if(keyboardGaps==null)\r
307         return 1;\r
308       else\r
309         return Integer.parseInt(keyboardGaps.toString());\r
310     }\r
311 \r
312 \r
313     /**\r
314      * DOCUMENT ME!\r
315      *\r
316      * @param evt DOCUMENT ME!\r
317      */\r
318     public void mouseReleased(MouseEvent evt)\r
319     {\r
320       mouseDragging = false;\r
321       mouseWheelPressed = false;\r
322 \r
323       if (!editingSeqs)\r
324       {\r
325          doMouseReleasedDefineMode(evt);\r
326          return;\r
327       }\r
328 \r
329        editOccurred();\r
330 \r
331        endEditing();\r
332        ap.repaint();\r
333     }\r
334 \r
335 \r
336 \r
337     /**\r
338      * DOCUMENT ME!\r
339      *\r
340      * @param evt DOCUMENT ME!\r
341      */\r
342     public void mousePressed(MouseEvent evt)\r
343     {\r
344       if (javax.swing.SwingUtilities.isMiddleMouseButton(evt))\r
345       {\r
346         mouseWheelPressed = true;\r
347         return;\r
348       }\r
349 \r
350       if (evt.isShiftDown() || evt.isAltDown() ||\r
351           evt.isControlDown())\r
352       {\r
353         if (evt.isAltDown() || evt.isControlDown())\r
354         {\r
355           groupEditing = true;\r
356         }\r
357         editingSeqs = true;\r
358       }\r
359       else\r
360       {\r
361         doMousePressedDefineMode(evt);\r
362         return;\r
363       }\r
364 \r
365 \r
366 \r
367       int seq = findSeq(evt);\r
368       int res = findRes(evt);\r
369 \r
370       if(seq<0 || res<0)\r
371         return;\r
372 \r
373       ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence",\r
374                                                          av.alignment, HistoryItem.EDIT));\r
375 \r
376         if ((seq < av.getAlignment().getHeight()) &&\r
377                 (res < av.getAlignment().getSequenceAt(seq).getLength()))\r
378         {\r
379             startseq = seq;\r
380             lastres = res;\r
381         }\r
382         else\r
383         {\r
384             startseq = -1;\r
385             lastres = -1;\r
386         }\r
387 \r
388         return;\r
389     }\r
390 \r
391     /**\r
392      * DOCUMENT ME!\r
393      *\r
394      * @param evt DOCUMENT ME!\r
395      */\r
396     public void mouseMoved(MouseEvent evt)\r
397     {\r
398       if (editingSeqs)\r
399       {\r
400        // This is because MacOSX creates a mouseMoved\r
401        // If control is down, other platforms will not.\r
402        mouseDragged(evt);\r
403       }\r
404 \r
405       int res = findRes(evt);\r
406       int seq = findSeq(evt);\r
407 \r
408 \r
409       if(res<0 || seq<0 || seq >= av.getAlignment().getHeight())\r
410             return;\r
411 \r
412       SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
413 \r
414       if (res > sequence.getLength())\r
415       {\r
416         return;\r
417       }\r
418 \r
419       if(seqCanvas.pdbCanvas!=null && sequence==seqCanvas.pdbCanvas.sequence)\r
420       {\r
421         seqCanvas.pdbCanvas.highlightRes(sequence.findPosition(res));\r
422       }\r
423 \r
424       setStatusMessage(sequence, res, seq);\r
425 \r
426         // use aa to see if the mouse pointer is on a\r
427         if (av.showSequenceFeatures)\r
428         {\r
429             SequenceFeature [] features = sequence.getDatasetSequence().getSequenceFeatures();\r
430             if(features!=null)\r
431             {\r
432               StringBuffer sbuffer = new StringBuffer("<html>");\r
433 \r
434               for (int i = 0; i < features.length; i++)\r
435               {\r
436 \r
437                 if ( (features[i].getBegin() <= sequence.findPosition(res)) &&\r
438                     (features[i].getEnd() >= sequence.findPosition(res)))\r
439                 {\r
440                   if(av.featuresDisplayed==null\r
441                     || !av.featuresDisplayed.containsKey(features[i].getType()))\r
442                   continue;\r
443 \r
444 \r
445                   if (features[i].getType().equals("disulfide bond"))\r
446                   {\r
447                     if (features[i].getBegin() == sequence.findPosition(res)\r
448                         || features[i].getEnd() == sequence.findPosition(res))\r
449                     {\r
450                       if (sbuffer.length() > 6)\r
451                         sbuffer.append("<br>");\r
452                       sbuffer.append("disulfide bond " + features[i].getBegin() + ":" +\r
453                                      features[i].getEnd());\r
454                     }\r
455                   }\r
456                   else\r
457                   {\r
458                     if (sbuffer.length() > 6)\r
459                       sbuffer.append("<br>");\r
460                     if(features[i].featureGroup!=null)\r
461                       sbuffer.append(features[i].featureGroup+";");\r
462 \r
463                     sbuffer.append(features[i].getType());\r
464 \r
465                     if (features[i].getDescription() != null\r
466                         && !features[i].description.equals(features[i].getType()))\r
467                       sbuffer.append("; " + features[i].getDescription());\r
468 \r
469                     if (features[i].getStatus() != null && features[i].getStatus().length()>0)\r
470                     {\r
471                       sbuffer.append("; (" + features[i].getStatus() + ")");\r
472                     }\r
473                   }\r
474                 }\r
475 \r
476               }\r
477 \r
478               sbuffer.append("</html>");\r
479               if(sbuffer.length()==13) // <html></html>\r
480                 setToolTipText("");\r
481               else\r
482                setToolTipText(sbuffer.toString());\r
483             }\r
484             else\r
485               setToolTipText("");\r
486         }\r
487     }\r
488 \r
489     void setStatusMessage(SequenceI sequence, int res, int seq)\r
490     {\r
491       StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " +\r
492                                            sequence.getName());\r
493 \r
494       Object obj = null;\r
495       if (av.alignment.isNucleotide())\r
496       {\r
497         obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res) +\r
498                                                    "");\r
499         if (obj != null)\r
500           text.append(" Nucleotide: ");\r
501       }\r
502       else\r
503       {\r
504         obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");\r
505         if (obj != null)\r
506           text.append("  Residue: ");\r
507       }\r
508 \r
509       if (obj != null)\r
510       {\r
511 \r
512         if (obj != "")\r
513         {\r
514           text.append(obj + " (" + sequence.findPosition(res) +\r
515                       ")");\r
516         }\r
517       }\r
518       ap.alignFrame.statusBar.setText(text.toString());\r
519 \r
520     }\r
521 \r
522     /**\r
523      * DOCUMENT ME!\r
524      *\r
525      * @param evt DOCUMENT ME!\r
526      */\r
527     public void mouseDragged(MouseEvent evt)\r
528     {\r
529       if (!editingSeqs)\r
530       {\r
531         doMouseDraggedDefineMode(evt);\r
532         return;\r
533       }\r
534 \r
535         int res = findRes(evt);\r
536 \r
537         if (res < 0)\r
538         {\r
539             res = 0;\r
540         }\r
541 \r
542         if ((lastres == -1) || (lastres == res))\r
543         {\r
544             return;\r
545         }\r
546 \r
547         if ( (res < av.getAlignment().getWidth()) && (res < lastres))\r
548         {\r
549           // dragLeft, delete gap\r
550           editSequence(false, res);\r
551         }\r
552         else\r
553           editSequence(true, res);\r
554 \r
555         mouseDragging = true;\r
556         if(scrollThread!=null)\r
557           scrollThread.setEvent(evt);\r
558 \r
559     }\r
560 \r
561     void editSequence(boolean insertGap, int startres)\r
562     {\r
563       int fixedLeft = -1;\r
564       int fixedRight = -1;\r
565       boolean fixedColumns = false;\r
566 \r
567        if(insertGap && av.hasHiddenColumns)\r
568         {\r
569           //Stop editing if the user has dragged beyond hiddenBoundary\r
570           fixedRight = av.getColumnSelection().getHiddenRegionBoundary(lastres);\r
571           if( fixedRight < lastres)\r
572           {\r
573             if(lastres!=fixedRight)\r
574             {\r
575               endEditing();\r
576               return;\r
577             }\r
578           }\r
579         }\r
580 \r
581         if (!groupEditing && av.hasHiddenRows)\r
582         {\r
583           if (av.alignment.getSequenceAt(startseq).getHiddenSequences() != null)\r
584           {\r
585             groupEditing = true;\r
586           }\r
587         }\r
588 \r
589 \r
590         SequenceI seq = av.alignment.getSequenceAt(startseq);\r
591         StringBuffer message = new StringBuffer();\r
592         if (groupEditing)\r
593           message.append("Edit group:");\r
594        else\r
595             message.append("Edit sequence: "+seq.getName());\r
596 \r
597        if(insertGap)\r
598          message.append(" insert ");\r
599        else\r
600          message.append(" delete ");\r
601 \r
602        message.append(Math.abs(startres-lastres)+" gaps.");\r
603        ap.alignFrame.statusBar.setText(message.toString());\r
604 \r
605 \r
606         //Are we editing within a selection group?\r
607         if (groupEditing\r
608             || (av.getSelectionGroup() != null &&\r
609                 av.getSelectionGroup().sequences.contains(seq)))\r
610         {\r
611           fixedColumns = true;\r
612 \r
613           fixedLeft = av.getSelectionGroup().getStartRes();\r
614           fixedRight = av.getSelectionGroup().getEndRes();\r
615 \r
616           if (   (startres < fixedLeft && lastres >= fixedLeft)\r
617               || (startres >= fixedLeft && lastres < fixedLeft)\r
618               || (startres > fixedRight && lastres <=fixedRight)\r
619               || (startres <= fixedRight && lastres > fixedRight))\r
620           {\r
621             endEditing();\r
622             return;\r
623           }\r
624 \r
625           if (fixedLeft > startres)\r
626           {\r
627             fixedRight = fixedLeft - 1;\r
628             fixedLeft = 0;\r
629           }\r
630           else if (fixedRight < startres)\r
631           {\r
632             fixedLeft = fixedRight;\r
633             fixedRight = -1;\r
634           }\r
635         }\r
636 \r
637         if (groupEditing)\r
638         {\r
639           SequenceGroup sg = av.getSelectionGroup();\r
640           if(fixedLeft>sg.getEndRes()\r
641           ||fixedRight<sg.getStartRes())\r
642        {\r
643          endEditing();\r
644          return;\r
645        }\r
646 \r
647           if (av.hasHiddenRows)\r
648           {\r
649             //sg might be null as the user may only see 1 sequence\r
650             if (sg == null)\r
651             {\r
652               sg = new SequenceGroup();\r
653               sg.addSequence(av.alignment.getSequenceAt(startseq), false);\r
654             }\r
655 \r
656             SequenceGroup tmp = new SequenceGroup();\r
657 \r
658             //Do any of the sequences have hidden associates?\r
659             for (int s = 0; s < sg.getSize(); s++)\r
660             {\r
661               seq = sg.getSequenceAt(s);\r
662               tmp.addSequence(seq, false);\r
663               if (seq.getHiddenSequences() != null)\r
664               {\r
665                 for (int h = 0; h < seq.getHiddenSequences().getSize(); h++)\r
666                   tmp.addSequence(seq.getHiddenSequences().getSequenceAt(h),\r
667                                   false);\r
668               }\r
669             }\r
670 \r
671             sg = tmp;\r
672           }\r
673 \r
674           if (sg == null)\r
675           {\r
676             endEditing();\r
677             return;\r
678           }\r
679 \r
680          // int blankColumn = -1;\r
681 \r
682           // drag to right\r
683           if (insertGap)\r
684           {\r
685             // Is it valid??\r
686             // Find the next gap before the end\r
687             // of the visible region boundary\r
688             if (av.hasHiddenColumns)\r
689             {\r
690              // int lastCol = av.getColumnSelection().\r
691              //     getHiddenRegionBoundary(res);\r
692 \r
693             }\r
694 \r
695               if (fixedRight != startres && fixedRight<av.alignment.getWidth()-1)\r
696               {\r
697                 for (fixedRight = fixedRight;\r
698                      fixedRight > lastres;\r
699                      fixedRight --)\r
700                 {\r
701                   boolean blank = true;\r
702                   for (int s = 0; s < sg.getSize(); s++)\r
703                   {\r
704                     seq = sg.getSequenceAt(s);\r
705 \r
706                     if (seq.getSequence().length() <= fixedRight)\r
707                     {\r
708                       continue;\r
709                     }\r
710 \r
711                     if (!jalview.util.Comparison.isGap(\r
712                         seq.getCharAt(fixedRight)))\r
713                     {\r
714                       blank = false;\r
715                       continue;\r
716                     }\r
717                   }\r
718                   if (blank)\r
719                     break;\r
720                 }\r
721               }\r
722 \r
723               if (fixedRight <= startres)\r
724               {\r
725                 endEditing();\r
726                 return;\r
727               }\r
728             }\r
729 \r
730 \r
731           // drag to left\r
732           else\r
733           {\r
734             /// Are we able to delete?\r
735             // ie are all columns blank?\r
736 \r
737             for (int s = 0; s < sg.getSize(); s++)\r
738             {\r
739               seq = sg.getSequenceAt(s);\r
740 \r
741               for (int j = startres; j < lastres; j++)\r
742               {\r
743                 if (seq.getSequence().length() <= j)\r
744                 {\r
745                   continue;\r
746                 }\r
747 \r
748                 if (!jalview.util.Comparison.isGap(\r
749                     seq.getSequence().charAt(j)))\r
750                 {\r
751                   // Not a gap, block edit not valid\r
752                   endEditing();\r
753                   return;\r
754                 }\r
755               }\r
756             }\r
757           }\r
758 \r
759 \r
760           for (int i = 0; i < sg.getSize(); i++)\r
761           {\r
762             seq = sg.getSequenceAt(i);\r
763 \r
764             if (insertGap)\r
765             {\r
766               // dragging to the right\r
767               for (int j = lastres; j < startres; j++)\r
768               {\r
769                 if (fixedColumns && fixedRight != -1)\r
770                 {\r
771                   insertChar(j, seq, fixedRight);\r
772                 }\r
773                 else\r
774                   insertChar(j, seq);\r
775               }\r
776             }\r
777             else\r
778             {\r
779               // dragging to the left\r
780               for (int j = lastres; j > startres; j--)\r
781               {\r
782                 if (fixedColumns && fixedRight != -1)\r
783                 {\r
784                   deleteChar(startres, seq, fixedRight);\r
785                 }\r
786                 else\r
787                 {\r
788                   deleteChar(startres, seq);\r
789                 }\r
790               }\r
791             }\r
792           }\r
793         }\r
794         else /////Editing a single sequence///////////\r
795         {\r
796 \r
797           if (insertGap)\r
798           {\r
799             // dragging to the right\r
800             for (int j = lastres; j < startres; j++)\r
801             {\r
802               if (fixedColumns && fixedRight != -1)\r
803                 insertChar(j, seq, fixedRight);\r
804               else\r
805                 insertChar(j, seq);\r
806             }\r
807           }\r
808           else\r
809           {\r
810             // dragging to the left\r
811             for (int j = lastres; j > startres; j--)\r
812             {\r
813               if (fixedColumns && fixedRight != -1)\r
814               {\r
815                 deleteChar(startres, seq, fixedRight);\r
816               }\r
817               else\r
818               {\r
819                 deleteChar(startres, seq);\r
820               }\r
821             }\r
822           }\r
823         }\r
824 \r
825         lastres = startres;\r
826         seqCanvas.repaint();\r
827     }\r
828 \r
829 \r
830     /**\r
831      * DOCUMENT ME!\r
832      *\r
833      * @param j DOCUMENT ME!\r
834      * @param seq DOCUMENT ME!\r
835      */\r
836     void insertChar(int j, SequenceI seq)\r
837     {\r
838         seq.insertCharAt(j, av.getGapCharacter());\r
839         seqEditOccurred = true;\r
840     }\r
841 \r
842     void insertChar(int j, SequenceI seq, int fixedColumn)\r
843     {\r
844       //Find the next gap before the end of the visible region boundary\r
845       //If lastCol > j, theres a boundary after the gap insertion\r
846       int blankColumn = fixedColumn;\r
847       for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)\r
848       {\r
849         if (jalview.util.Comparison.isGap(seq.getCharAt(blankColumn)))\r
850     {\r
851           //Theres a space, so break and insert the gap\r
852           break;\r
853         }\r
854       }\r
855 \r
856       if (blankColumn <= j)\r
857       {\r
858         endEditing();\r
859         return;\r
860       }\r
861 \r
862       if (!jalview.util.Comparison.isGap(seq.getCharAt(blankColumn)))\r
863       {\r
864         //Just Checking\r
865         System.out.println("Tried removing residue (INSERT)"+seq.getCharAt(fixedColumn));\r
866         return;\r
867       }\r
868 \r
869       seq.deleteCharAt(blankColumn);\r
870       seq.insertCharAt(j, av.getGapCharacter());\r
871       seqEditOccurred = true;\r
872     }\r
873 \r
874     void deleteChar(int j, SequenceI seq, int fixedColumn)\r
875     {\r
876       if (!jalview.util.Comparison.isGap(seq.getCharAt(j)))\r
877       {\r
878         ap.alignFrame.statusBar.setText(\r
879             "End editing: Tried removing residue " + seq.getCharAt(j));\r
880         return;\r
881       }\r
882 \r
883       seq.deleteCharAt(j);\r
884       seq.insertCharAt(fixedColumn, av.getGapCharacter());\r
885       seqEditOccurred = true;\r
886     }\r
887 \r
888     /**\r
889      * DOCUMENT ME!\r
890      *\r
891      * @param j DOCUMENT ME!\r
892      * @param seq DOCUMENT ME!\r
893      */\r
894     void deleteChar(int j, SequenceI seq)\r
895     {\r
896       if (!jalview.util.Comparison.isGap(seq.getCharAt(j)))\r
897       {\r
898         ap.alignFrame.statusBar.setText(\r
899             "End editing: Tried removing residue " + seq.getCharAt(j));\r
900         return;\r
901       }\r
902 \r
903         seq.deleteCharAt(j);\r
904         seqEditOccurred = true;\r
905         seqCanvas.repaint();\r
906     }\r
907     /**\r
908      * DOCUMENT ME!\r
909      *\r
910      * @param e DOCUMENT ME!\r
911      */\r
912     public void mouseEntered(MouseEvent e)\r
913     {\r
914         if(oldSeq < 0)\r
915           oldSeq = 0;\r
916 \r
917         if (scrollThread != null)\r
918         {\r
919             scrollThread.running = false;\r
920             scrollThread = null;\r
921         }\r
922     }\r
923 \r
924     /**\r
925      * DOCUMENT ME!\r
926      *\r
927      * @param e DOCUMENT ME!\r
928      */\r
929     public void mouseExited(MouseEvent e)\r
930     {\r
931         if (av.getWrapAlignment())\r
932         {\r
933             return;\r
934         }\r
935 \r
936         if (mouseDragging)\r
937         {\r
938             scrollThread = new ScrollThread();\r
939         }\r
940     }\r
941 \r
942     public void mouseClicked(MouseEvent evt)\r
943     {}\r
944 \r
945     public void mouseWheelMoved(MouseWheelEvent e)\r
946     {\r
947       e.consume();\r
948       if (mouseWheelPressed)\r
949       {\r
950         Font font = av.getFont();\r
951         int fontSize = font.getSize();\r
952         if (e.getWheelRotation() > 0 && fontSize < 51)\r
953           fontSize++;\r
954         else if (fontSize > 1)\r
955           fontSize--;\r
956 \r
957         av.setFont(new Font(font.getName(), font.getStyle(), fontSize));\r
958         ap.fontChanged();\r
959       }\r
960       else\r
961       {\r
962         if (e.getWheelRotation() > 0)\r
963           ap.scrollUp(false);\r
964         else\r
965           ap.scrollUp(true);\r
966       }\r
967 \r
968     }\r
969 \r
970 \r
971 \r
972     /**\r
973      * DOCUMENT ME!\r
974      *\r
975      * @param i DOCUMENT ME!\r
976      */\r
977     void editOccurred()\r
978     {\r
979       if (!seqEditOccurred)\r
980       {\r
981         ap.alignFrame.historyList.pop();\r
982         ap.alignFrame.updateEditMenuBar();\r
983       }\r
984 \r
985       endEditing();\r
986 \r
987       av.firePropertyChange("alignment", null,av.getAlignment().getSequences());\r
988 \r
989     }\r
990 \r
991     /**\r
992      * DOCUMENT ME!\r
993      *\r
994      * @param evt DOCUMENT ME!\r
995      */\r
996     public void doMousePressedDefineMode(MouseEvent evt)\r
997     {\r
998       int res = findRes(evt);\r
999       int seq = findSeq(evt);\r
1000       oldSeq = seq;\r
1001 \r
1002       startWrapBlock=wrappedBlock;\r
1003 \r
1004       if(av.wrapAlignment && seq>av.alignment.getHeight())\r
1005       {\r
1006           JOptionPane.showInternalMessageDialog(Desktop.desktop,\r
1007               "Cannot edit annotations in wrapped view.",\r
1008               "Wrapped view - no edit",\r
1009               JOptionPane.WARNING_MESSAGE);\r
1010         return;\r
1011       }\r
1012 \r
1013       if(seq<0 || res<0)\r
1014             return;\r
1015 \r
1016         SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
1017 \r
1018         if ((sequence == null) || (res > sequence.getLength()))\r
1019         {\r
1020             return;\r
1021         }\r
1022 \r
1023         stretchGroup = av.getSelectionGroup();\r
1024 \r
1025         if (stretchGroup == null)\r
1026         {\r
1027             stretchGroup = av.alignment.findGroup(sequence);\r
1028 \r
1029             if ((stretchGroup != null) && (res > stretchGroup.getStartRes()) &&\r
1030                     (res < stretchGroup.getEndRes()))\r
1031             {\r
1032                 av.setSelectionGroup(stretchGroup);\r
1033             }\r
1034             else\r
1035             {\r
1036                 stretchGroup = null;\r
1037             }\r
1038         }\r
1039         else if (!stretchGroup.sequences.contains(sequence) ||\r
1040                 (stretchGroup.getStartRes() > res) ||\r
1041                 (stretchGroup.getEndRes() < res))\r
1042         {\r
1043             stretchGroup = null;\r
1044 \r
1045             SequenceGroup[] allGroups = av.alignment.findAllGroups(sequence);\r
1046 \r
1047             if (allGroups != null)\r
1048             {\r
1049                 for (int i = 0; i < allGroups.length; i++)\r
1050                 {\r
1051                     if ((allGroups[i].getStartRes() <= res) &&\r
1052                             (allGroups[i].getEndRes() >= res))\r
1053                     {\r
1054                         stretchGroup = allGroups[i];\r
1055                         av.setSelectionGroup(stretchGroup);\r
1056 \r
1057                         break;\r
1058                     }\r
1059                 }\r
1060             }\r
1061         }\r
1062 \r
1063         if (av.cursorMode)\r
1064         {\r
1065           seqCanvas.cursorX = findRes(evt);\r
1066           seqCanvas.cursorY = findSeq(evt);\r
1067           seqCanvas.repaint();\r
1068           return;\r
1069         }\r
1070 \r
1071 \r
1072         if (stretchGroup == null)\r
1073         {\r
1074             // define a new group here\r
1075             SequenceGroup sg = new SequenceGroup();\r
1076             sg.setStartRes(res);\r
1077             sg.setEndRes(res);\r
1078             sg.addSequence(sequence, false);\r
1079             av.setSelectionGroup(sg);\r
1080             stretchGroup = sg;\r
1081 \r
1082             if (av.getConservationSelected())\r
1083             {\r
1084                 SliderPanel.setConservationSlider(ap,\r
1085                     av.getGlobalColourScheme(), "Background");\r
1086             }\r
1087 \r
1088             if (av.getAbovePIDThreshold())\r
1089             {\r
1090                 SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),\r
1091                     "Background");\r
1092             }\r
1093         }\r
1094         else if (javax.swing.SwingUtilities.isRightMouseButton(evt))\r
1095         {\r
1096             jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(ap, null);\r
1097             pop.show(this, evt.getX(), evt.getY());\r
1098 \r
1099             // edit the properties of existing group\r
1100         }\r
1101 \r
1102         if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))\r
1103         {\r
1104             // Edit end res position of selected group\r
1105             changeEndRes = true;\r
1106         }\r
1107         else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res))\r
1108         {\r
1109             // Edit end res position of selected group\r
1110             changeStartRes = true;\r
1111         }\r
1112 \r
1113         stretchGroup.getWidth();\r
1114 \r
1115         seqCanvas.repaint();\r
1116     }\r
1117 \r
1118     /**\r
1119      * DOCUMENT ME!\r
1120      *\r
1121      * @param evt DOCUMENT ME!\r
1122      */\r
1123     public void doMouseReleasedDefineMode(MouseEvent evt)\r
1124     {\r
1125         if (stretchGroup == null)\r
1126         {\r
1127             return;\r
1128         }\r
1129 \r
1130 \r
1131         if(stretchGroup.cs!=null)\r
1132         {\r
1133           if (stretchGroup.cs instanceof ClustalxColourScheme)\r
1134           {\r
1135             ( (ClustalxColourScheme) stretchGroup.cs).resetClustalX(stretchGroup.\r
1136                 sequences,\r
1137                 stretchGroup.getWidth());\r
1138           }\r
1139 \r
1140           if (stretchGroup.cs.conservationApplied())\r
1141           {\r
1142             SliderPanel.setConservationSlider(ap, stretchGroup.cs,\r
1143                                               stretchGroup.getName());\r
1144             stretchGroup.recalcConservation();\r
1145           }\r
1146           else\r
1147           {\r
1148             SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,\r
1149                                            stretchGroup.getName());\r
1150           }\r
1151         }\r
1152         changeEndRes = false;\r
1153         changeStartRes = false;\r
1154         stretchGroup = null;\r
1155         PaintRefresher.Refresh(av.alignment);\r
1156     }\r
1157 \r
1158     /**\r
1159      * DOCUMENT ME!\r
1160      *\r
1161      * @param evt DOCUMENT ME!\r
1162      */\r
1163     public void doMouseDraggedDefineMode(MouseEvent evt)\r
1164     {\r
1165       int res = findRes(evt);\r
1166       int y = findSeq(evt);\r
1167 \r
1168       if(wrappedBlock!=startWrapBlock)\r
1169         return;\r
1170 \r
1171        if (stretchGroup == null)\r
1172        {\r
1173             return;\r
1174        }\r
1175 \r
1176 \r
1177         if(y > av.alignment.getHeight())\r
1178         {\r
1179           y = av.alignment.getHeight() -1;\r
1180         }\r
1181 \r
1182         if (stretchGroup.getEndRes() == res)\r
1183         {\r
1184             // Edit end res position of selected group\r
1185             changeEndRes = true;\r
1186         }\r
1187         else if (stretchGroup.getStartRes() == res)\r
1188         {\r
1189             // Edit start res position of selected group\r
1190             changeStartRes = true;\r
1191         }\r
1192 \r
1193         if (res < av.getStartRes())\r
1194         {\r
1195             res = av.getStartRes();\r
1196         }\r
1197 \r
1198         if (changeEndRes)\r
1199         {\r
1200             if (res > (stretchGroup.getStartRes() - 1))\r
1201             {\r
1202                 stretchGroup.setEndRes(res);\r
1203             }\r
1204         }\r
1205         else if (changeStartRes)\r
1206         {\r
1207             if (res < (stretchGroup.getEndRes() + 1))\r
1208             {\r
1209                 stretchGroup.setStartRes(res);\r
1210             }\r
1211         }\r
1212 \r
1213         int dragDirection = 0;\r
1214 \r
1215         if (y > oldSeq)\r
1216         {\r
1217             dragDirection = 1;\r
1218         }\r
1219         else if (y < oldSeq)\r
1220         {\r
1221             dragDirection = -1;\r
1222         }\r
1223 \r
1224 \r
1225         while ((y != oldSeq) && (oldSeq > -1) && (y < av.alignment.getHeight()))\r
1226         {\r
1227             // This routine ensures we don't skip any sequences, as the\r
1228             // selection is quite slow.\r
1229             Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1230 \r
1231             oldSeq += dragDirection;\r
1232 \r
1233             if(oldSeq<0)\r
1234               break;\r
1235 \r
1236             Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1237 \r
1238             if (stretchGroup.sequences.contains(nextSeq))\r
1239             {\r
1240                 stretchGroup.deleteSequence(seq, false);\r
1241             }\r
1242             else\r
1243             {\r
1244                 if (seq != null)\r
1245                 {\r
1246                     stretchGroup.addSequence(seq, false);\r
1247                 }\r
1248 \r
1249                 stretchGroup.addSequence(nextSeq, false);\r
1250             }\r
1251         }\r
1252 \r
1253         if(oldSeq < 0)\r
1254           oldSeq = -1;\r
1255 \r
1256         mouseDragging = true;\r
1257 \r
1258         if (scrollThread != null)\r
1259         {\r
1260             scrollThread.setEvent(evt);\r
1261         }\r
1262 \r
1263         seqCanvas.repaint();\r
1264     }\r
1265 \r
1266 \r
1267 \r
1268     // this class allows scrolling off the bottom of the visible alignment\r
1269     class ScrollThread extends Thread\r
1270     {\r
1271         MouseEvent evt;\r
1272         boolean running = false;\r
1273 \r
1274         public ScrollThread()\r
1275         {\r
1276             start();\r
1277         }\r
1278 \r
1279         public void setEvent(MouseEvent e)\r
1280         {\r
1281             evt = e;\r
1282         }\r
1283 \r
1284         public void stopScrolling()\r
1285         {\r
1286             running = false;\r
1287         }\r
1288 \r
1289         public void run()\r
1290         {\r
1291             running = true;\r
1292 \r
1293             while (running)\r
1294             {\r
1295                 if (evt != null)\r
1296                 {\r
1297                     if (mouseDragging && (evt.getY() < 0) &&\r
1298                             (av.getStartSeq() > 0))\r
1299                     {\r
1300                         running = ap.scrollUp(true);\r
1301                     }\r
1302 \r
1303                     if (mouseDragging && (evt.getY() >= getHeight()) &&\r
1304                             (av.alignment.getHeight() > av.getEndSeq()))\r
1305                     {\r
1306                         running = ap.scrollUp(false);\r
1307                     }\r
1308 \r
1309                     if (mouseDragging && (evt.getX() < 0))\r
1310                     {\r
1311                         running = ap.scrollRight(false);\r
1312                     }\r
1313                     else if (mouseDragging && (evt.getX() >= getWidth()))\r
1314                     {\r
1315                         running = ap.scrollRight(true);\r
1316                     }\r
1317                 }\r
1318 \r
1319                 try\r
1320                 {\r
1321                     Thread.sleep(20);\r
1322                 }\r
1323                 catch (Exception ex)\r
1324                 {\r
1325                 }\r
1326             }\r
1327         }\r
1328     }\r
1329 }\r