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