Keyboard editing added to applet
authoramwaterhouse <Andrew Waterhouse>
Tue, 4 Apr 2006 10:47:57 +0000 (10:47 +0000)
committeramwaterhouse <Andrew Waterhouse>
Tue, 4 Apr 2006 10:47:57 +0000 (10:47 +0000)
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/SeqCanvas.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/SequenceRenderer.java

index 189a36d..7a0052f 100755 (executable)
@@ -250,6 +250,13 @@ public class AlignFrame extends Frame implements ActionListener,
 \r
   public void keyPressed(KeyEvent evt)\r
   {\r
+    if (viewport.cursorMode\r
+        && evt.getKeyCode() >= KeyEvent.VK_0\r
+        && evt.getKeyCode() <= KeyEvent.VK_9)\r
+    {\r
+      alignPanel.seqPanel.numberPressed(evt.getKeyChar());\r
+    }\r
+\r
     switch (evt.getKeyCode())\r
     {\r
       case 27: // escape key\r
@@ -262,6 +269,10 @@ public class AlignFrame extends Frame implements ActionListener,
         }\r
         break;\r
       case KeyEvent.VK_C:\r
+        if (viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.setCursorColumn();\r
+        }\r
         if (evt.isControlDown() || evt.isMetaDown())\r
         {\r
           copy_actionPerformed();\r
@@ -280,21 +291,110 @@ public class AlignFrame extends Frame implements ActionListener,
         }\r
         break;\r
       case KeyEvent.VK_DOWN:\r
-        moveSelectedSequences(false);\r
+        if(viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.moveCursor(0,1);\r
+        }\r
+        else\r
+          moveSelectedSequences(false);\r
         break;\r
+\r
       case KeyEvent.VK_UP:\r
-        moveSelectedSequences(true);\r
+        if (viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.moveCursor(0,-1);\r
+        }\r
+        else\r
+          moveSelectedSequences(true);\r
+        break;\r
+\r
+      case KeyEvent.VK_LEFT:\r
+        if(viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.moveCursor(-1,0);\r
+        }\r
+        break;\r
+\r
+      case KeyEvent.VK_RIGHT:\r
+        if (viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.moveCursor(1,0);\r
+        }\r
+        break;\r
+      case KeyEvent.VK_SPACE:\r
+        if(viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.insertGapAtCursor(evt.isControlDown());\r
+        }\r
+        break;\r
+\r
+      case KeyEvent.VK_DELETE:\r
+        if(viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.deleteGapAtCursor(evt.isControlDown());\r
+        }\r
+        break;\r
+\r
+      case KeyEvent.VK_BACK_SPACE:\r
+        if(!viewport.cursorMode)\r
+        {\r
+          cut_actionPerformed();\r
+          alignPanel.seqPanel.seqCanvas.repaint();\r
+        }\r
+        break;\r
+\r
+      case KeyEvent.VK_S:\r
+        if(viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.setCursorRow();\r
+        }\r
+        break;\r
+      case KeyEvent.VK_P:\r
+        if(viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.setCursorPosition();\r
+        }\r
+        break;\r
+\r
+      case KeyEvent.VK_ENTER:\r
+      case KeyEvent.VK_COMMA:\r
+        if(viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.setCursorRowAndColumn();\r
+        }\r
         break;\r
+\r
+      case KeyEvent.VK_Q:\r
+        if(viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.setSelectionAreaAtCursor(true);\r
+        }\r
+        break;\r
+      case KeyEvent.VK_M:\r
+        if(viewport.cursorMode)\r
+        {\r
+          alignPanel.seqPanel.setSelectionAreaAtCursor(false);\r
+        }\r
+        break;\r
+\r
+     case KeyEvent.VK_F2:\r
+       viewport.cursorMode = ! viewport.cursorMode;\r
+       statusBar.setText("Keyboard editing mode is "+\r
+           (viewport.cursorMode ? "on" : "off"));\r
+       if(viewport.cursorMode)\r
+       {\r
+         alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;\r
+         alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;\r
+       }\r
+       alignPanel.seqPanel.seqCanvas.repaint();\r
+       break;\r
+\r
       case KeyEvent.VK_F:\r
         if (evt.isControlDown())\r
         {\r
           findMenuItem_actionPerformed();\r
         }\r
         break;\r
-      case KeyEvent.VK_BACK_SPACE:\r
-      case KeyEvent.VK_DELETE:\r
-        cut_actionPerformed();\r
-        break;\r
     }\r
   }\r
   public void keyReleased(KeyEvent evt)\r
@@ -335,6 +435,8 @@ public void itemStateChanged(ItemEvent evt)
             abovePIDThreshold_actionPerformed();\r
           else if(evt.getSource()==applyToAllGroups)\r
             applyToAllGroups_actionPerformed();\r
+      else if(evt.getSource()==autoCalculate)\r
+          viewport.autocalculateConsensus = autoCalculate.getState();\r
   }\r
  public void actionPerformed(ActionEvent evt)\r
  {\r
@@ -1003,8 +1105,13 @@ public void itemStateChanged(ItemEvent evt)
 \r
   public void alignmentChanged()\r
   {\r
-    viewport.updateConsensus();\r
-    viewport.updateConservation ();\r
+    viewport.alignment.padGaps();\r
+    if(viewport.autocalculateConsensus)\r
+    {\r
+      viewport.updateConsensus();\r
+      viewport.updateConservation();\r
+    }\r
+\r
     resetAllColourSchemes();\r
     if(alignPanel.overviewPanel!=null)\r
       alignPanel.overviewPanel.updateOverviewImage();\r
@@ -1749,6 +1856,8 @@ public void itemStateChanged(ItemEvent evt)
     protected CheckboxMenuItem scaleRight = new CheckboxMenuItem();\r
     MenuItem modifyPID = new MenuItem();\r
     MenuItem modifyConservation = new MenuItem();\r
+    protected CheckboxMenuItem autoCalculate\r
+        = new CheckboxMenuItem("Autocalculate Consensus", true);\r
     protected Menu sortByTreeMenu = new Menu();\r
     Menu sort = new Menu();\r
     Menu calculate = new Menu();\r
@@ -1926,6 +2035,7 @@ public void itemStateChanged(ItemEvent evt)
         sortByTreeMenu.setLabel("By Tree Order");\r
         sort.setLabel("Sort");\r
         calculate.setLabel("Calculate Tree");\r
+        autoCalculate.addItemListener(this);\r
         inputText.setLabel("Input from textbox");\r
         inputText.addActionListener(this);\r
 \r
@@ -2021,6 +2131,7 @@ public void itemStateChanged(ItemEvent evt)
         calculateMenu.addSeparator();\r
         calculateMenu.add(pairwiseAlignmentMenuItem);\r
         calculateMenu.add(PCAMenuItem);\r
+        calculateMenu.add(autoCalculate);\r
         this.add(statusBar, BorderLayout.SOUTH);\r
         pasteMenu.add(pasteNew);\r
         pasteMenu.add(pasteThis);\r
index bef5e1a..36b0a31 100755 (executable)
@@ -36,6 +36,9 @@ public class AlignViewport
   int startSeq;\r
   int endSeq;\r
 \r
+\r
+  boolean cursorMode = false;\r
+\r
   boolean showJVSuffix = true;\r
   boolean showText = true;\r
   boolean showColourText = false;\r
@@ -83,6 +86,8 @@ public class AlignViewport
   AlignmentAnnotation conservation;\r
   AlignmentAnnotation quality;\r
 \r
+  boolean autocalculateConsensus = true;\r
+\r
   public int ConsPercGaps = 25; // JBPNote : This should be a scalable property!\r
 \r
   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(this);\r
index e0c327f..30e1d70 100755 (executable)
@@ -306,6 +306,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener
       }\r
       setScrollValues(hscroll.getValue(), vscroll.getValue()+1);\r
     }\r
+\r
     repaint();\r
     return true;\r
   }\r
@@ -313,7 +314,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener
   public boolean scrollRight(boolean right)\r
   {\r
 \r
-    if (right)\r
+    if (!right)\r
     {\r
       if (hscroll.getValue() < 1)\r
       {\r
index 465dd38..f6ef577 100755 (executable)
@@ -37,12 +37,13 @@ public class SeqCanvas
 \r
   SearchResults searchResults = null;\r
 \r
-  int chunkHeight;\r
-  int chunkWidth;\r
-\r
   boolean fastPaint = false;\r
 \r
 \r
+  int cursorX = 0;\r
+  int cursorY = 0;\r
+\r
+\r
   public SeqCanvas(AlignViewport av)\r
   {\r
     this.av = av;\r
@@ -468,6 +469,14 @@ public class SeqCanvas
                                 av.charWidth, av.charHeight);\r
        }\r
    }\r
+\r
+   if (av.cursorMode && cursorY == i\r
+       && cursorX >= startRes && cursorX <= endRes)\r
+   {\r
+     sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * av.charWidth,\r
+                   offset + ( (i - startSeq) * av.charHeight));\r
+   }\r
+\r
   }\r
 \r
   //\r
index e29f4e1..021a5dc 100755 (executable)
@@ -19,8 +19,6 @@
 \r
 package jalview.appletgui;\r
 \r
-import java.util.*;\r
-\r
 import java.awt.*;\r
 import java.awt.event.*;\r
 \r
@@ -36,20 +34,27 @@ 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
   public SeqPanel(AlignViewport avp, AlignmentPanel p)\r
   {\r
     this.av = avp;\r
@@ -66,23 +71,281 @@ 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
+     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(); 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.sequences.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
+     ap.alignFrame.statusBar.setText(text.toString());\r
+\r
+    }\r
      public void mousePressed(MouseEvent evt)\r
      {\r
-       if (evt.isShiftDown() || evt.isAltDown() || evt.isControlDown())\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 +353,18 @@ public class SeqPanel
 \r
   public void mouseReleased(MouseEvent evt)\r
   {\r
+    mouseDragging = 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
@@ -304,191 +561,362 @@ public class SeqPanel
   public void mouseDragged(MouseEvent evt)\r
   {\r
     if (!editingSeqs)\r
-    {\r
-      doMouseDraggedDefineMode(evt);\r
-      return;\r
-    }\r
+{\r
+  doMouseDraggedDefineMode(evt);\r
+  return;\r
+}\r
 \r
-    // If we're dragging we're editing\r
-    int res = findRes(evt);\r
-    if (res < 0)\r
-    {\r
+  int res = findRes(evt);\r
+\r
+  if (res < 0)\r
+  {\r
       res = 0;\r
-    }\r
+  }\r
 \r
-    if (lastres == -1 || lastres == res)\r
-    {\r
+  if ((lastres == -1) || (lastres == res))\r
+  {\r
       return;\r
-    }\r
+  }\r
 \r
-    boolean dragRight = true;\r
-    if (res < av.getAlignment().getWidth() && res < lastres)\r
-    {\r
-      dragRight = false;\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
-    if (res != lastres)\r
+  mouseDragging = true;\r
+  if(scrollThread!=null)\r
+    scrollThread.setEvent(evt);\r
+\r
+  }\r
+\r
+  synchronized void editSequence(boolean insertGap, int startres)\r
     {\r
-      // Group editing\r
-      if (groupEditing)\r
-      {\r
-        SequenceGroup sg = av.getSelectionGroup();\r
-        if (sg == null)\r
-        {\r
-          lastres = -1;\r
-          return;\r
-        }\r
+      int fixedLeft = -1;\r
+      int fixedRight = -1;\r
+      boolean fixedColumns = false;\r
+      SequenceGroup sg = av.getSelectionGroup();\r
 \r
-        // drag to right\r
-        if (dragRight)\r
-        {\r
-          sg.setEndRes(sg.getEndRes() + (res - lastres));\r
-        }\r
+      if(groupEditing && sg==null)\r
+        return;\r
 \r
-        // drag to left\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.sequences.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
+              blank = true;\r
+              for (int s = 0; s < sg.getSize(); 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
-              insertChar(j, k);\r
+              if(sg.getSize() == av.alignment.getHeight())\r
+              {\r
+                //We can still insert gaps if the selectionGroup\r
+                //contains all the sequences\r
+                sg.setEndRes(sg.getEndRes()+1+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(); 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(); 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
-                scrollThread.running = false;\r
-                scrollThread = null;\r
+                deleteChar(startres, seq, fixedRight);\r
+              }\r
+              else\r
+              {\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
     if (scrollThread != null)\r
@@ -597,12 +1025,6 @@ public class SeqPanel
 \r
   }\r
 \r
-  boolean changeEndSeq = false;\r
-  boolean changeStartSeq = false;\r
-  boolean changeEndRes = false;\r
-  boolean changeStartRes = false;\r
-  SequenceGroup stretchGroup = null;\r
-\r
   public void doMouseReleasedDefineMode(MouseEvent evt)\r
   {\r
     if (stretchGroup == null)\r
index 3f87b02..891dfab 100755 (executable)
@@ -264,4 +264,22 @@ public class SequenceRenderer
     }\r
   }\r
 \r
+  public void drawCursor(SequenceI seq, int res, int x1, int y1)\r
+  {\r
+    int pady = av.charHeight / 5;\r
+    int charOffset = 0;\r
+    graphics.setColor(Color.black);\r
+    graphics.fillRect(x1, y1, av.charWidth, av.charHeight);\r
+    graphics.setColor(Color.white);\r
+\r
+    graphics.setColor(Color.white);\r
+\r
+    char s = seq.getCharAt(res);\r
+\r
+    charOffset = (av.charWidth - fm.charWidth(s)) / 2;\r
+    graphics.drawString(String.valueOf(s),\r
+              charOffset + x1,\r
+              (y1 + av.charHeight) - pady);\r
+    }\r
+\r
 }\r