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