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