sequences are private in SequenceGroup
[jalview.git] / src / jalview / appletgui / SeqPanel.java
index e29f4e1..6869964 100755 (executable)
 \r
 package jalview.appletgui;\r
 \r
-import java.util.*;\r
-\r
 import java.awt.*;\r
 import java.awt.event.*;\r
 \r
 import jalview.datamodel.*;\r
 import jalview.schemes.*;\r
 \r
+import java.util.Vector;\r
+\r
 public class SeqPanel\r
     extends Panel implements MouseMotionListener, MouseListener\r
 {\r
@@ -36,20 +36,30 @@ public class SeqPanel
 \r
   protected int lastres;\r
   protected int startseq;\r
-  int startEdit = -1;\r
-  int endEdit = -1;\r
 \r
   protected AlignViewport av;\r
 \r
   // if character is inserted or deleted, we will need to recalculate the conservation\r
-  int seqEditOccurred = -1;\r
+  boolean seqEditOccurred = false;\r
 \r
   ScrollThread scrollThread = null;\r
   boolean mouseDragging = false;\r
-\r
   boolean editingSeqs = false;\r
   boolean groupEditing = false;\r
 \r
+  int oldSeq = -1;\r
+  boolean changeEndSeq = false;\r
+  boolean changeStartSeq = false;\r
+  boolean changeEndRes = false;\r
+  boolean changeStartRes = false;\r
+  SequenceGroup stretchGroup = null;\r
+\r
+  StringBuffer keyboardNo1;\r
+  StringBuffer keyboardNo2;\r
+\r
+  boolean mouseWheelPressed = false;\r
+  Point lastMousePress;\r
+\r
   public SeqPanel(AlignViewport avp, AlignmentPanel p)\r
   {\r
     this.av = avp;\r
@@ -66,23 +76,297 @@ public class SeqPanel
     seqCanvas.repaint();\r
   }\r
 \r
+  void endEditing()\r
+  {\r
+    startseq = -1;\r
+    lastres = -1;\r
+    seqEditOccurred = false;\r
+    editingSeqs = false;\r
+    groupEditing = false;\r
+    keyboardNo1 = null;\r
+    keyboardNo2 = null;\r
+   }\r
+\r
+   void setCursorRow()\r
+   {\r
+     seqCanvas.cursorY = getKeyboardNo(keyboardNo1)-1;\r
+     scrollToVisible();\r
+   }\r
+\r
+   void setCursorColumn()\r
+   {\r
+     seqCanvas.cursorX = getKeyboardNo(keyboardNo1)-1;\r
+     scrollToVisible();\r
+   }\r
+\r
+   void setCursorRowAndColumn()\r
+   {\r
+     if(keyboardNo2==null)\r
+     {\r
+       keyboardNo2 = new StringBuffer();\r
+     }\r
+     else\r
+     {\r
+       seqCanvas.cursorX = getKeyboardNo(keyboardNo1) - 1;\r
+       seqCanvas.cursorY = getKeyboardNo(keyboardNo2) - 1;\r
+       scrollToVisible();\r
+     }\r
+   }\r
+\r
+   void setCursorPosition()\r
+   {\r
+     SequenceI sequence =\r
+         (Sequence) av.getAlignment().getSequenceAt(seqCanvas.cursorY);\r
+\r
+     seqCanvas.cursorX = sequence.findIndex(\r
+         getKeyboardNo(keyboardNo1)-1\r
+         );\r
+     scrollToVisible();\r
+   }\r
+\r
+   void moveCursor(int dx, int dy)\r
+   {\r
+     seqCanvas.cursorX += dx;\r
+     seqCanvas.cursorY += dy;\r
+     scrollToVisible();\r
+   }\r
+\r
+   void scrollToVisible()\r
+   {\r
+     if (seqCanvas.cursorX < 0)\r
+       seqCanvas.cursorX = 0;\r
+     else if (seqCanvas.cursorX > av.alignment.getWidth() - 1)\r
+       seqCanvas.cursorX = av.alignment.getWidth() - 1;\r
+\r
+     if (seqCanvas.cursorY < 0)\r
+       seqCanvas.cursorY = 0;\r
+     else if (seqCanvas.cursorY > av.alignment.getHeight() - 1)\r
+       seqCanvas.cursorY = av.alignment.getHeight() - 1;\r
+\r
+\r
+     endEditing();\r
+     if (av.wrapAlignment)\r
+     {\r
+       ap.scrollToWrappedVisible(seqCanvas.cursorX);\r
+     }\r
+     else\r
+     {\r
+       while (seqCanvas.cursorY < av.startSeq)\r
+       {\r
+         ap.scrollUp(true);\r
+       }\r
+       while (seqCanvas.cursorY + 1 > av.endSeq)\r
+       {\r
+         ap.scrollUp(false);\r
+       }\r
+       while (seqCanvas.cursorX < av.startRes)\r
+       {\r
+\r
+         if (!ap.scrollRight(false))\r
+           break;\r
+       }\r
+       while (seqCanvas.cursorX > av.endRes)\r
+       {\r
+         if (!ap.scrollRight(true))\r
+           break;\r
+       }\r
+     }\r
+     setStatusMessage(av.alignment.getSequenceAt(seqCanvas.cursorY),\r
+                      seqCanvas.cursorX, seqCanvas.cursorY);\r
+\r
+     seqCanvas.repaint();\r
+   }\r
+\r
+   void setSelectionAreaAtCursor(boolean topLeft)\r
+   {\r
+     SequenceI sequence =\r
+         (Sequence) av.getAlignment().getSequenceAt(seqCanvas.cursorY);\r
+\r
+     if(av.getSelectionGroup()!=null)\r
+     {\r
+       SequenceGroup sg = av.selectionGroup;\r
+       //Find the top and bottom of this group\r
+       int min = av.alignment.getHeight(), max = 0;\r
+       for(int i=0; i<sg.getSize(false); i++)\r
+       {\r
+         int index = av.alignment.findIndex( sg.getSequenceAt(i) );\r
+         if(index > max)\r
+           max = index;\r
+         if(index < min)\r
+           min = index;\r
+       }\r
+\r
+       max ++;\r
+\r
+       if(topLeft)\r
+       {\r
+         sg.setStartRes(seqCanvas.cursorX);\r
+         if(sg.getEndRes()<seqCanvas.cursorX)\r
+           sg.setEndRes(seqCanvas.cursorX);\r
+\r
+         min = seqCanvas.cursorY;\r
+       }\r
+       else\r
+       {\r
+         sg.setEndRes(seqCanvas.cursorX);\r
+         if(sg.getStartRes()>seqCanvas.cursorX)\r
+           sg.setStartRes(seqCanvas.cursorX);\r
+\r
+         max = seqCanvas.cursorY+1;\r
+       }\r
+\r
+       if(min>max)\r
+       {\r
+         // Only the user can do this\r
+         av.setSelectionGroup(null);\r
+       }\r
+       else\r
+       {\r
+         // Now add any sequences between min and max\r
+         sg.getSequences(false).removeAllElements();\r
+         for (int i = min; i < max; i++)\r
+         {\r
+           sg.addSequence(av.alignment.getSequenceAt(i), false);\r
+         }\r
+       }\r
+     }\r
+\r
+     if (av.getSelectionGroup() == null)\r
+     {\r
+       SequenceGroup sg = new SequenceGroup();\r
+       sg.setStartRes(seqCanvas.cursorX);\r
+       sg.setEndRes(seqCanvas.cursorX);\r
+       sg.addSequence(sequence, false);\r
+       av.setSelectionGroup(sg);\r
+     }\r
+\r
+\r
+     ap.repaint();\r
+   }\r
+\r
+   void insertGapAtCursor(boolean group)\r
+   {\r
+     ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence",\r
+                                                  av.alignment, HistoryItem.EDIT));\r
+     groupEditing = group;\r
+     startseq = seqCanvas.cursorY;\r
+     lastres = seqCanvas.cursorX;\r
+     editSequence(true, seqCanvas.cursorX+getKeyboardNo(keyboardNo1));\r
+     editOccurred();\r
+   }\r
+\r
+   void deleteGapAtCursor(boolean group)\r
+   {\r
+     ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence",\r
+                                                  av.alignment, HistoryItem.EDIT));\r
+     groupEditing = group;\r
+     startseq = seqCanvas.cursorY;\r
+     lastres = seqCanvas.cursorX+getKeyboardNo(keyboardNo1);\r
+     editSequence(false, seqCanvas.cursorX);\r
+     editOccurred();\r
+   }\r
+\r
+   void numberPressed(char value)\r
+   {\r
+     if(keyboardNo1==null)\r
+       keyboardNo1 = new StringBuffer();\r
+\r
+     if(keyboardNo2!=null)\r
+       keyboardNo2.append(value);\r
+     else\r
+       keyboardNo1.append(value);\r
+   }\r
+\r
+   int getKeyboardNo(StringBuffer kb)\r
+   {\r
+     if(kb==null)\r
+       return 1;\r
+     else\r
+       return Integer.parseInt(kb.toString());\r
+   }\r
+\r
+   void setStatusMessage(SequenceI sequence, int res, int seq)\r
+   {\r
+     StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " +\r
+                                          sequence.getName());\r
+\r
+     Object obj = null;\r
+     if (av.alignment.isNucleotide())\r
+     {\r
+       obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res) +\r
+                                                  "");\r
+       if (obj != null)\r
+         text.append(" Nucleotide: ");\r
+     }\r
+     else\r
+     {\r
+       obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");\r
+       if (obj != null)\r
+         text.append("  Residue: ");\r
+     }\r
+\r
+     if (obj != null)\r
+     {\r
+\r
+       if (obj != "")\r
+       {\r
+         text.append(obj + " (" + sequence.findPosition(res) +\r
+                     ")");\r
+       }\r
+     }\r
 \r
+     ap.alignFrame.statusBar.setText(text.toString());\r
+\r
+    }\r
      public void mousePressed(MouseEvent evt)\r
      {\r
-       if (evt.isShiftDown() || evt.isAltDown() || evt.isControlDown())\r
+       lastMousePress = evt.getPoint();\r
+\r
+       if ( (evt.getModifiers() & InputEvent.BUTTON2_MASK) ==\r
+         InputEvent.BUTTON2_MASK)\r
+       {\r
+         mouseWheelPressed = true;\r
+         return;\r
+       }\r
+\r
+       if (evt.isShiftDown() || evt.isAltDown() ||\r
+           evt.isControlDown())\r
        {\r
          if (evt.isAltDown() || evt.isControlDown())\r
          {\r
            groupEditing = true;\r
          }\r
-\r
          editingSeqs = true;\r
-         doMousePressed(evt);\r
        }\r
        else\r
        {\r
          doMousePressedDefineMode(evt);\r
+         return;\r
        }\r
+\r
+\r
+       int seq = findSeq(evt);\r
+       int res = findRes(evt);\r
+\r
+       if(seq<0 || res<0)\r
+         return;\r
+\r
+       ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence",\r
+                                                          av.alignment, HistoryItem.EDIT));\r
+\r
+         if ((seq < av.getAlignment().getHeight()) &&\r
+                 (res < av.getAlignment().getSequenceAt(seq).getLength()))\r
+         {\r
+             startseq = seq;\r
+             lastres = res;\r
+         }\r
+         else\r
+         {\r
+             startseq = -1;\r
+             lastres = -1;\r
+         }\r
+\r
+        return;\r
      }\r
 \r
      public void mouseClicked(MouseEvent evt){}\r
@@ -90,24 +374,19 @@ public class SeqPanel
 \r
   public void mouseReleased(MouseEvent evt)\r
   {\r
+    mouseDragging = false;\r
+    mouseWheelPressed = false;\r
+\r
     if (!editingSeqs)\r
     {\r
-      doMouseReleasedDefineMode(evt);\r
-      return;\r
+       doMouseReleasedDefineMode(evt);\r
+       return;\r
     }\r
 \r
+     editOccurred();\r
 \r
-    if (seqEditOccurred > -1)\r
-    {\r
-      editOccurred(seqEditOccurred);\r
-    }\r
-\r
-    startseq = -1;\r
-    lastres = -1;\r
-    seqEditOccurred = -1;\r
-    editingSeqs = false;\r
-    groupEditing = false;\r
-    ap.repaint();\r
+     endEditing();\r
+     ap.repaint();\r
   }\r
 \r
   int startWrapBlock=-1;\r
@@ -259,238 +538,489 @@ public class SeqPanel
         && sequence.getSequenceFeatures()!=null\r
         && av.featuresDisplayed!=null)\r
     {\r
+      StringBuffer featureText = new StringBuffer();\r
+      Vector allFeatures = getAllFeaturesAtRes(sequence, sequence.findPosition(res));\r
+\r
       int index = 0;\r
-      sequence.getSequenceFeatures();\r
-      boolean first = true;\r
-      while (index < sequence.getSequenceFeatures().length)\r
+      while (index < allFeatures.size())\r
       {\r
-        SequenceFeature sf = sequence.getSequenceFeatures()[index];\r
-        if (sf.getBegin() <= sequence.findPosition(res) &&\r
-            sf.getEnd() >= sequence.findPosition(res))\r
-        {\r
-          if(!av.featuresDisplayed.containsKey(sf.getType()))\r
-           {\r
-             index++;\r
-             continue;\r
-           }\r
-\r
-          if(first)\r
-          {\r
-            text.append(" Sequence Feature:");\r
-            first = false;\r
-          }\r
+        SequenceFeature sf = (SequenceFeature) allFeatures.elementAt(index);\r
 \r
-          text.append(" "+sf.getType());\r
+        featureText.append(sf.getType()+" "+sf.begin+":"+sf.end);\r
 \r
-          if(sf.getDescription()!=null)\r
-            text.append(" "+sf.getDescription());\r
+        if (sf.getDescription() != null)\r
+          featureText.append(" " + sf.getDescription());\r
 \r
-          if (sf.getStatus()!=null && sf.getStatus().length() > 0)\r
-          {\r
-            text.append(" (" + sf.getStatus() + ")");\r
-          }\r
-          text.append("; ");\r
+        if (sf.getValue("status") != null )\r
+        {\r
+          featureText.append(" (" + sf.getValue("status") + ")");\r
         }\r
+        featureText.append("\n");\r
 \r
         index++;\r
-\r
       }\r
+\r
+\r
+        if (tooltip == null)\r
+          tooltip = new Tooltip(featureText.toString(), seqCanvas);\r
+        else\r
+          tooltip.setTip(featureText.toString());\r
+\r
+        tooltip.repaint();\r
+\r
+     //   text.append(" Sequence Feature:");\r
+     //   text.append(featureText);\r
+\r
     }\r
 \r
+\r
      ap.alignFrame.statusBar.setText(text.toString());\r
 \r
   }\r
 \r
-  public void mouseDragged(MouseEvent evt)\r
+  Vector getAllFeaturesAtRes(SequenceI seq, int res)\r
   {\r
-    if (!editingSeqs)\r
+    Vector allFeatures = new Vector();\r
+    int index = 0;\r
+    if(seq.getSequenceFeatures()!=null)\r
     {\r
-      doMouseDraggedDefineMode(evt);\r
-      return;\r
+      while (index < seq.getSequenceFeatures().length)\r
+      {\r
+        SequenceFeature sf = seq.getSequenceFeatures()[index];\r
+        if (sf.getBegin() <= res &&\r
+            sf.getEnd() >= res)\r
+        {\r
+          if (av.featuresDisplayed.containsKey(sf.getType()))\r
+          {\r
+            allFeatures.addElement(sf);\r
+          }\r
+        }\r
+        index++;\r
+      }\r
     }\r
+    return allFeatures;\r
+  }\r
 \r
-    // If we're dragging we're editing\r
-    int res = findRes(evt);\r
-    if (res < 0)\r
-    {\r
-      res = 0;\r
-    }\r
+  Tooltip tooltip;\r
 \r
-    if (lastres == -1 || lastres == res)\r
+  public void mouseDragged(MouseEvent evt)\r
+  {\r
+    if (mouseWheelPressed)\r
     {\r
-      return;\r
-    }\r
+      int oldWidth = av.charWidth;\r
 \r
-    boolean dragRight = true;\r
-    if (res < av.getAlignment().getWidth() && res < lastres)\r
-    {\r
-      dragRight = false;\r
-    }\r
+      //Which is bigger, left-right or up-down?\r
+      if (Math.abs(evt.getY() - lastMousePress.y)\r
+          > Math.abs(evt.getX() - lastMousePress.x))\r
+      {\r
+        int fontSize = av.font.getSize();\r
 \r
-    if (res != lastres)\r
-    {\r
-      // Group editing\r
-      if (groupEditing)\r
+        if (evt.getY() < lastMousePress.y && av.charHeight > 1)\r
+        {\r
+          fontSize--;\r
+        }\r
+        else if (evt.getY() > lastMousePress.y)\r
+        {\r
+          fontSize++;\r
+        }\r
+\r
+\r
+        if(fontSize<1)\r
+          fontSize = 1;\r
+\r
+        av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));\r
+        av.charWidth = oldWidth;\r
+      }\r
+      else\r
       {\r
-        SequenceGroup sg = av.getSelectionGroup();\r
-        if (sg == null)\r
+        if (evt.getX() < lastMousePress.x && av.charWidth > 1)\r
         {\r
-          lastres = -1;\r
-          return;\r
+          av.charWidth--;\r
+        }\r
+        else if (evt.getX() > lastMousePress.x)\r
+        {\r
+          av.charWidth++;\r
         }\r
 \r
-        // drag to right\r
-        if (dragRight)\r
+        if(av.charWidth<1)\r
         {\r
-          sg.setEndRes(sg.getEndRes() + (res - lastres));\r
+          av.charWidth = 1;\r
         }\r
+      }\r
+\r
+      ap.fontChanged();\r
+\r
+      FontMetrics fm = getFontMetrics(av.getFont());\r
+      av.validCharWidth = fm.charWidth('M') <= av.charWidth;\r
+\r
+      lastMousePress = evt.getPoint();\r
+\r
+      ap.repaint();\r
+      ap.annotationPanel.image = null;\r
+      return;\r
+      }\r
+\r
+      if (!editingSeqs)\r
+      {\r
+        doMouseDraggedDefineMode(evt);\r
+        return;\r
+      }\r
+\r
+  int res = findRes(evt);\r
+\r
+  if (res < 0)\r
+  {\r
+      res = 0;\r
+  }\r
 \r
-        // drag to left\r
+  if ((lastres == -1) || (lastres == res))\r
+  {\r
+      return;\r
+  }\r
+\r
+  if ( (res < av.getAlignment().getWidth()) && (res < lastres))\r
+  {\r
+    // dragLeft, delete gap\r
+    editSequence(false, res);\r
+  }\r
+  else\r
+    editSequence(true, res);\r
+\r
+  mouseDragging = true;\r
+  if(scrollThread!=null)\r
+    scrollThread.setEvent(evt);\r
+\r
+  }\r
+\r
+  synchronized void editSequence(boolean insertGap, int startres)\r
+    {\r
+      int fixedLeft = -1;\r
+      int fixedRight = -1;\r
+      boolean fixedColumns = false;\r
+      SequenceGroup sg = av.getSelectionGroup();\r
+\r
+      if(groupEditing && sg==null)\r
+        return;\r
+\r
+        SequenceI seq = av.alignment.getSequenceAt(startseq);\r
+        StringBuffer message = new StringBuffer();\r
+        if (groupEditing)\r
+           message.append("Edit group:");\r
         else\r
+            message.append("Edit sequence: "+seq.getName());\r
+\r
+       if(insertGap)\r
+         message.append(" insert ");\r
+       else\r
+         message.append(" delete ");\r
+\r
+       message.append(Math.abs(startres-lastres)+" gaps.");\r
+       ap.alignFrame.statusBar.setText(message.toString());\r
+\r
+\r
+        //Are we editing within a selection group?\r
+        if (groupEditing\r
+            || (sg != null && sg.getSequences(false).contains(seq)))\r
         {\r
-          /// Are we able to delete?\r
-          // ie are all columns blank?\r
-          boolean deleteAllowed = false;\r
-          for (int s = 0; s < sg.getSize(); s++)\r
-          {\r
-            SequenceI seq = sg.getSequenceAt(s);\r
-            for (int j = res; j < lastres; j++)\r
-            {\r
-              if (seq.getSequence().length() <= j)\r
-              {\r
-                continue;\r
-              }\r
+          fixedColumns = true;\r
 \r
-              if (!jalview.util.Comparison.isGap(seq.getSequence().charAt(j)))\r
-              {\r
-                // Not a gap, block edit not valid\r
-                res = j + 1;\r
-                deleteAllowed = false;\r
-                continue;\r
-              }\r
-              deleteAllowed = true;\r
-            }\r
-          }\r
+          fixedLeft = sg.getStartRes();\r
+          fixedRight = sg.getEndRes();\r
 \r
-          if (!deleteAllowed)\r
+          if (   (startres < fixedLeft && lastres >= fixedLeft)\r
+              || (startres >= fixedLeft && lastres < fixedLeft)\r
+              || (startres > fixedRight && lastres <=fixedRight)\r
+              || (startres <= fixedRight && lastres > fixedRight))\r
           {\r
-            lastres = -1;\r
+            endEditing();\r
             return;\r
           }\r
 \r
-          sg.setEndRes(sg.getEndRes() - (lastres - res));\r
+          if (fixedLeft > startres)\r
+          {\r
+            fixedRight = fixedLeft - 1;\r
+            fixedLeft = 0;\r
+          }\r
+          else if (fixedRight < startres)\r
+          {\r
+            fixedLeft = fixedRight;\r
+            fixedRight = -1;\r
+          }\r
         }\r
 \r
-        for (int i = 0; i < sg.getSize(); i++)\r
-        {\r
-          SequenceI s = sg.getSequenceAt(i);\r
-          int k = av.alignment.findIndex(s);\r
 \r
+        if (groupEditing)\r
+        {\r
           // drag to right\r
-          if (dragRight)\r
+          if (insertGap)\r
           {\r
-            for (int j = lastres; j < res; j++)\r
+              //If the user has selected the whole sequence, and is dragging to\r
+              // the right, we can still extend the alignment and selectionGroup\r
+              if(sg.getStartRes() == 0 && sg.getEndRes() + 1 == av.alignment.getWidth())\r
+              {\r
+                sg.setEndRes(av.alignment.getWidth() + startres - lastres);\r
+                fixedRight = sg.getEndRes();\r
+              }\r
+\r
+            // Is it valid with fixed columns??\r
+            // Find the next gap before the end\r
+            // of the visible region boundary\r
+            boolean blank = false;\r
+            for (fixedRight = fixedRight;\r
+                 fixedRight > lastres;\r
+                 fixedRight--)\r
             {\r
-              insertChar(j, k);\r
+              blank = true;\r
+              for (int s = 0; s < sg.getSize(false); s++)\r
+              {\r
+                seq = sg.getSequenceAt(s);\r
+                for (int j = 0; j < startres - lastres; j++)\r
+                {\r
+                  if (!jalview.util.Comparison.isGap(\r
+                      seq.getCharAt(fixedRight - j)))\r
+                  {\r
+                    blank = false;\r
+                    break;\r
+                  }\r
+                }\r
+              }\r
+              if (blank)\r
+                break;\r
+            }\r
+\r
+            if (!blank)\r
+            {\r
+              if(sg.getSize(false) == av.alignment.getHeight())\r
+              {\r
+                //We can still insert gaps if the selectionGroup\r
+                //contains all the sequences\r
+                sg.setEndRes(sg.getEndRes()+startres-lastres);\r
+                fixedRight = av.alignment.getWidth()+startres-lastres;\r
+              }\r
+              else\r
+              {\r
+                endEditing();\r
+                return;\r
+              }\r
             }\r
           }\r
 \r
+\r
           // drag to left\r
-          else\r
+          else if(!insertGap)\r
           {\r
-            for (int j = res; j < lastres; j++)\r
+            /// Are we able to delete?\r
+            // ie are all columns blank?\r
+\r
+            for (int s = 0; s < sg.getSize(false); s++)\r
             {\r
-              if (s.getLength() > j)\r
+              seq = sg.getSequenceAt(s);\r
+\r
+              for (int j = startres; j < lastres; j++)\r
               {\r
-                deleteChar(res, k);\r
+                if (seq.getSequence().length() <= j)\r
+                {\r
+                  continue;\r
+                }\r
+\r
+                if (!jalview.util.Comparison.isGap(\r
+                    seq.getSequence().charAt(j)))\r
+                {\r
+                  // Not a gap, block edit not valid\r
+                  endEditing();\r
+                  return;\r
+                }\r
               }\r
             }\r
           }\r
-        }\r
-      }\r
-      else /////Editing a single sequence///////////\r
-      {\r
-        if (res < av.getAlignment().getWidth() && res > lastres)\r
-        {\r
-          // dragging to the right\r
-          for (int j = lastres; j < res; j++)\r
+\r
+\r
+          for (int i = 0; i < sg.getSize(false); i++)\r
           {\r
-            insertChar(j, startseq);\r
+            seq = sg.getSequenceAt(i);\r
+\r
+            if (insertGap)\r
+            {\r
+              // dragging to the right\r
+              for (int j = lastres; j < startres; j++)\r
+              {\r
+                if (fixedColumns && fixedRight != -1)\r
+                {\r
+                  insertChar(j, seq, fixedRight);\r
+                }\r
+                else\r
+                  insertChar(j, seq);\r
+              }\r
+            }\r
+            else\r
+            {\r
+              // dragging to the left\r
+              for (int j = lastres; j > startres; j--)\r
+              {\r
+                if (fixedColumns && fixedRight != -1)\r
+                {\r
+                  deleteChar(startres, seq, fixedRight);\r
+                }\r
+                else\r
+                {\r
+                  deleteChar(startres, seq);\r
+                }\r
+              }\r
+            }\r
           }\r
         }\r
-        else if (res < av.getAlignment().getWidth() && res < lastres)\r
+        else /////Editing a single sequence///////////\r
         {\r
-          // dragging to the left\r
-          for (int j = lastres; j > res; j--)\r
+          if (insertGap)\r
           {\r
-            if (jalview.util.Comparison.isGap(\r
-                av.alignment.getSequenceAt(startseq).getSequence().charAt(res)))\r
+            // dragging to the right\r
+            for (int j = lastres; j < startres; j++)\r
             {\r
-\r
-              deleteChar(res, startseq);\r
+              if (fixedColumns && fixedRight != -1)\r
+              {\r
+                if (sg.getStartRes() == 0\r
+                    && sg.getEndRes() + 1 == av.alignment.getWidth()\r
+                    && !jalview.util.Comparison.isGap(seq.getCharAt(fixedRight)))\r
+                {\r
+                  //Single sequence edit, whole sequence selected,\r
+                  //extend the selection group\r
+                  sg.setEndRes(av.alignment.getWidth() -1 + startres - lastres);\r
+                  fixedColumns = false;\r
+                  insertChar(j, seq);\r
+                }\r
+                else\r
+                  insertChar(j, seq, fixedRight);\r
+              }\r
+              else\r
+                insertChar(j, seq);\r
             }\r
-            else\r
+          }\r
+          else\r
+          {\r
+            // dragging to the left\r
+            for (int j = lastres; j > startres; j--)\r
             {\r
-              if(scrollThread!=null)\r
+              if (fixedColumns && fixedRight != -1)\r
+              {\r
+                deleteChar(startres, seq, fixedRight);\r
+              }\r
+              else\r
               {\r
-                scrollThread.running = false;\r
-                scrollThread = null;\r
+                deleteChar(startres, seq);\r
               }\r
-              break;\r
             }\r
           }\r
         }\r
 \r
-      }\r
+        lastres = startres;\r
+        seqCanvas.repaint();\r
     }\r
 \r
-    mouseDragging = true;\r
-    if (res > av.endRes || res < av.startRes)\r
+\r
+    /**\r
+     * DOCUMENT ME!\r
+     *\r
+     * @param j DOCUMENT ME!\r
+     * @param seq DOCUMENT ME!\r
+     */\r
+    void insertChar(int j, SequenceI seq)\r
     {\r
-      mouseExited(evt);\r
+        seq.insertCharAt(j, av.getGapCharacter());\r
+        seqEditOccurred = true;\r
     }\r
 \r
-    if (scrollThread != null)\r
-      scrollThread.setEvent(evt);\r
+    void insertChar(int j, SequenceI seq, int fixedColumn)\r
+    {\r
+      //Find the next gap before the end of the visible region boundary\r
+      //If lastCol > j, theres a boundary after the gap insertion\r
+      int blankColumn = fixedColumn;\r
+      for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)\r
+      {\r
+        if (jalview.util.Comparison.isGap(seq.getCharAt(blankColumn)))\r
+        {\r
+          //Theres a space, so break and insert the gap\r
+          break;\r
+        }\r
+      }\r
 \r
+      if (blankColumn <= j)\r
+      {\r
+        endEditing();\r
+        return;\r
+      }\r
 \r
-    endEdit = res;\r
-    lastres = res;\r
-    seqCanvas.repaint();\r
-  }\r
+      if (!jalview.util.Comparison.isGap(seq.getCharAt(blankColumn)))\r
+      {\r
+        //Just Checking\r
+        System.out.println("Tried removing residue (INSERT)"+seq.getCharAt(fixedColumn));\r
+        return;\r
+      }\r
 \r
-  public void insertChar(int j, int seq)\r
-  {\r
-    av.alignment.getSequenceAt(seq).insertCharAt(j, av.getGapCharacter());\r
-    seqEditOccurred = seq;\r
-  }\r
+      seq.deleteCharAt(blankColumn);\r
+      seq.insertCharAt(j, av.getGapCharacter());\r
+      seqEditOccurred = true;\r
+    }\r
 \r
-  public void deleteChar(int j, int seq)\r
-  {\r
+    void deleteChar(int j, SequenceI seq, int fixedColumn)\r
+    {\r
+      if (!jalview.util.Comparison.isGap(seq.getCharAt(j)))\r
+      {\r
+        ap.alignFrame.statusBar.setText(\r
+            "End editing: Tried removing residue " + seq.getCharAt(j));\r
+        return;\r
+      }\r
 \r
-    av.alignment.getSequenceAt(seq).deleteCharAt(j);\r
-    seqEditOccurred = seq;\r
-    av.alignment.getWidth();\r
-    repaint();\r
-  }\r
+      seq.deleteCharAt(j);\r
+      seq.insertCharAt(fixedColumn, av.getGapCharacter());\r
+      seqEditOccurred = true;\r
+    }\r
 \r
-  void editOccurred(int i)\r
-  {\r
-    if (endEdit == startEdit)\r
+    /**\r
+     * DOCUMENT ME!\r
+     *\r
+     * @param j DOCUMENT ME!\r
+     * @param seq DOCUMENT ME!\r
+     */\r
+    void deleteChar(int j, SequenceI seq)\r
     {\r
+      if (!jalview.util.Comparison.isGap(seq.getCharAt(j)))\r
+      {\r
+        ap.alignFrame.statusBar.setText(\r
+            "End editing: Tried removing residue " + seq.getCharAt(j));\r
+        return;\r
+      }\r
+\r
+        seq.deleteCharAt(j);\r
+        seqEditOccurred = true;\r
+        seqCanvas.repaint();\r
+    }\r
+\r
+    /**\r
+     * DOCUMENT ME!\r
+     *\r
+     * @param i DOCUMENT ME!\r
+     */\r
+    void editOccurred()\r
+    {\r
+      if (!seqEditOccurred)\r
+      {\r
         ap.alignFrame.historyList.pop();\r
         ap.alignFrame.updateEditMenuBar();\r
-    }\r
+      }\r
 \r
-    av.firePropertyChange("alignment", null,av.getAlignment().getSequences());\r
-  }\r
+      endEditing();\r
+\r
+      av.firePropertyChange("alignment", null,av.getAlignment().getSequences());\r
+\r
+    }\r
 \r
 //////////////////////////////////////////\r
 /////Everything below this is for defining the boundary of the rubberband\r
 //////////////////////////////////////////\r
-  int oldSeq = -1;\r
   public void doMousePressedDefineMode(MouseEvent evt)\r
   {\r
+\r
+\r
     if (scrollThread != null)\r
     {\r
       scrollThread.running = false;\r
@@ -528,7 +1058,7 @@ public class SeqPanel
       }\r
     }\r
 \r
-    else if (!stretchGroup.sequences.contains(sequence)\r
+    else if (!stretchGroup.getSequences(false).contains(sequence)\r
              || stretchGroup.getStartRes() > res\r
              || stretchGroup.getEndRes() < res)\r
     {\r
@@ -544,64 +1074,91 @@ public class SeqPanel
               allGroups[i].getEndRes() >= res)\r
           {\r
             stretchGroup = allGroups[i];\r
-            av.setSelectionGroup(stretchGroup);\r
             break;\r
           }\r
         }\r
       }\r
+      av.setSelectionGroup(stretchGroup);\r
     }\r
 \r
-    if (stretchGroup == null)\r
+    if (av.cursorMode)\r
     {\r
-      // define a new group here\r
-      SequenceGroup sg = new SequenceGroup();\r
-      sg.setStartRes(res);\r
-      sg.setEndRes(res);\r
-      sg.addSequence(sequence, false);\r
-      av.setSelectionGroup(sg);\r
-      stretchGroup = sg;\r
+      seqCanvas.cursorX = findRes(evt);\r
+      seqCanvas.cursorY = findSeq(evt);\r
+      seqCanvas.repaint();\r
+      return;\r
+    }\r
 \r
-      if (av.getConservationSelected())\r
-      {\r
-        SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(),\r
-                                          "Background");\r
-      }\r
-      if (av.getAbovePIDThreshold())\r
-      {\r
-        SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),\r
-                                       "Background");\r
-      }\r
 \r
-    }\r
 \r
     // DETECT RIGHT MOUSE BUTTON IN AWT\r
-    else if ( (evt.getModifiers() & InputEvent.BUTTON3_MASK) ==\r
+    if ( (evt.getModifiers() & InputEvent.BUTTON3_MASK) ==\r
              InputEvent.BUTTON3_MASK)\r
     {\r
-      APopupMenu popup = new APopupMenu(ap, null, null);\r
+      Vector allFeatures = getAllFeaturesAtRes(sequence,\r
+                                               sequence.findPosition(res));\r
+\r
+      Vector links = null;\r
+      if(allFeatures!=null)\r
+      {\r
+        for (int i = 0; i < allFeatures.size(); i++)\r
+        {\r
+          SequenceFeature sf = (SequenceFeature) allFeatures.elementAt(i);\r
+          if (sf.links != null)\r
+          {\r
+            links = new Vector();\r
+            for (int j = 0; j < sf.links.size(); j++)\r
+            {\r
+              links.addElement(sf.links.elementAt(j));\r
+            }\r
+          }\r
+        }\r
+      }\r
+      APopupMenu popup = new APopupMenu(ap, null, links);\r
       this.add(popup);\r
       popup.show(this, evt.getX(), evt.getY());\r
+      ap.repaint();\r
     }\r
-\r
-    if (stretchGroup != null && stretchGroup.getEndRes() == res)\r
+    else\r
     {\r
-      // Edit end res position of selected group\r
-      changeEndRes = true;\r
-    }\r
+      //Only if left mouse button do we want to change group sizes\r
 \r
-    else if (stretchGroup != null && stretchGroup.getStartRes() == res)\r
-    {\r
-      // Edit end res position of selected group\r
-      changeStartRes = true;\r
-    }\r
+      if (stretchGroup == null)\r
+      {\r
+        // define a new group here\r
+        SequenceGroup sg = new SequenceGroup();\r
+        sg.setStartRes(res);\r
+        sg.setEndRes(res);\r
+        sg.addSequence(sequence, false);\r
+        av.setSelectionGroup(sg);\r
+        stretchGroup = sg;\r
+\r
+        if (av.getConservationSelected())\r
+        {\r
+          SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(),\r
+                                            "Background");\r
+        }\r
+        if (av.getAbovePIDThreshold())\r
+        {\r
+          SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),\r
+                                         "Background");\r
+        }\r
 \r
-  }\r
+      }\r
 \r
-  boolean changeEndSeq = false;\r
-  boolean changeStartSeq = false;\r
-  boolean changeEndRes = false;\r
-  boolean changeStartRes = false;\r
-  SequenceGroup stretchGroup = null;\r
+      if (stretchGroup != null && stretchGroup.getEndRes() == res)\r
+      {\r
+        // Edit end res position of selected group\r
+        changeEndRes = true;\r
+      }\r
+\r
+      else if (stretchGroup != null && stretchGroup.getStartRes() == res)\r
+      {\r
+        // Edit end res position of selected group\r
+        changeStartRes = true;\r
+      }\r
+    }\r
+  }\r
 \r
   public void doMouseReleasedDefineMode(MouseEvent evt)\r
   {\r
@@ -614,8 +1171,8 @@ public class SeqPanel
     {\r
       if (stretchGroup.cs instanceof ClustalxColourScheme)\r
       {\r
-        ( (ClustalxColourScheme) stretchGroup.cs).resetClustalX(stretchGroup.\r
-            sequences,\r
+        ( (ClustalxColourScheme) stretchGroup.cs).resetClustalX(\r
+            stretchGroup.getSequences(true),\r
             stretchGroup.getWidth());\r
       }\r
 \r
@@ -718,7 +1275,7 @@ public class SeqPanel
 \r
           Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
 \r
-          if (stretchGroup.sequences.contains(nextSeq))\r
+          if (stretchGroup.getSequences(false).contains(nextSeq))\r
           {\r
               stretchGroup.deleteSequence(seq, false);\r
           }\r