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