Merge branch 'features/JAL-845splitPaneMergeDevelop' into merge_JAL-845_JAL-1640
[jalview.git] / src / jalview / gui / SeqPanel.java
index 4a43798..bd8bb7c 100644 (file)
  */
 package jalview.gui;
 
+import jalview.api.AlignViewportI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
+import jalview.commands.EditCommand.Edit;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
@@ -34,7 +36,11 @@ import jalview.structure.SelectionListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.SequenceListener;
 import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
+import jalview.util.Comparison;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -245,22 +251,33 @@ public class SeqPanel extends JPanel implements MouseListener,
     return seq;
   }
 
+  /**
+   * When all of a sequence of edits are complete, put the resulting edit list
+   * on the history stack (undo list), and reset flags for editing in progress.
+   */
   void endEditing()
   {
-    if (editCommand != null && editCommand.getSize() > 0)
+    try
+    {
+      if (editCommand != null && editCommand.getSize() > 0)
+      {
+        ap.alignFrame.addHistoryItem(editCommand);
+        av.firePropertyChange("alignment", null, av.getAlignment()
+                .getSequences());
+      }
+    } finally
     {
-      ap.alignFrame.addHistoryItem(editCommand);
-      av.firePropertyChange("alignment", null, av.getAlignment()
-              .getSequences());
+      /*
+       * Tidy up come what may...
+       */
+      startseq = -1;
+      lastres = -1;
+      editingSeqs = false;
+      groupEditing = false;
+      keyboardNo1 = null;
+      keyboardNo2 = null;
+      editCommand = null;
     }
-
-    startseq = -1;
-    lastres = -1;
-    editingSeqs = false;
-    groupEditing = false;
-    keyboardNo1 = null;
-    keyboardNo2 = null;
-    editCommand = null;
   }
 
   void setCursorRow()
@@ -640,6 +657,11 @@ public class SeqPanel extends JPanel implements MouseListener,
   }
 
   @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this.ap == null ? null : this.ap.av;
+  }
+  @Override
   public void updateColours(SequenceI seq, int index)
   {
     System.out.println("update the seqPanel colours");
@@ -777,35 +799,36 @@ public class SeqPanel extends JPanel implements MouseListener,
   int setStatusMessage(SequenceI sequence, int res, int seq)
   {
     int pos = -1;
-    StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
-            + sequence.getName());
+    StringBuilder text = new StringBuilder(32);
+    text.append("Sequence " + (seq + 1) + " ID: " + sequence.getName());
 
-    Object obj = null;
+    String residue = null;
+    /*
+     * Try to translate the display character to residue name (null for gap).
+     */
+    final String displayChar = String.valueOf(sequence.getCharAt(res));
     if (av.getAlignment().isNucleotide())
     {
-      obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
-              + "");
-      if (obj != null)
+      residue = ResidueProperties.nucleotideName.get(displayChar);
+      if (residue != null)
       {
-        text.append(" Nucleotide: ");
+        text.append(" Nucleotide: ").append(residue);
       }
     }
     else
     {
-      obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
-      if (obj != null)
+      residue = "X".equalsIgnoreCase(displayChar) ? "STOP"
+              : ResidueProperties.aa2Triplet.get(displayChar);
+      if (residue != null)
       {
-        text.append("  Residue: ");
+        text.append(" Residue: ").append(residue);
       }
     }
 
-    if (obj != null)
+    if (residue != null)
     {
       pos = sequence.findPosition(res);
-      if (obj != "")
-      {
-        text.append(obj + " (" + pos + ")");
-      }
+      text.append(" (").append(Integer.toString(pos)).append(")");
     }
     ap.alignFrame.statusBar.setText(text.toString());
     return pos;
@@ -926,7 +949,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
     }
 
-    StringBuffer message = new StringBuffer();
+    StringBuilder message = new StringBuilder(64);
     if (groupEditing)
     {
       message.append("Edit group:");
@@ -1151,8 +1174,8 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
         else
         {
-          editCommand.appendEdit(Action.INSERT_GAP, groupSeqs,
-                  startres, startres - lastres, av.getAlignment(), true);
+          appendEdit(Action.INSERT_GAP, groupSeqs, startres, startres
+                  - lastres);
         }
       }
       else
@@ -1167,8 +1190,8 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
         else
         {
-          editCommand.appendEdit(Action.DELETE_GAP, groupSeqs,
-                  startres, lastres - startres, av.getAlignment(), true);
+          appendEdit(Action.DELETE_GAP, groupSeqs, startres, lastres
+                  - startres);
         }
 
       }
@@ -1189,8 +1212,8 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
         else
         {
-          editCommand.appendEdit(Action.INSERT_GAP, new SequenceI[]
-          { seq }, lastres, startres - lastres, av.getAlignment(), true);
+          appendEdit(Action.INSERT_GAP, new SequenceI[]
+          { seq }, lastres, startres - lastres);
         }
       }
       else
@@ -1202,7 +1225,7 @@ public class SeqPanel extends JPanel implements MouseListener,
           {
             for (int j = lastres; j > startres; j--)
             {
-              if (!jalview.util.Comparison.isGap(seq.getCharAt(startres)))
+              if (!Comparison.isGap(seq.getCharAt(startres)))
               {
                 endEditing();
                 break;
@@ -1217,7 +1240,7 @@ public class SeqPanel extends JPanel implements MouseListener,
             int max = 0;
             for (int m = startres; m < lastres; m++)
             {
-              if (!jalview.util.Comparison.isGap(seq.getCharAt(m)))
+              if (!Comparison.isGap(seq.getCharAt(m)))
               {
                 break;
               }
@@ -1226,9 +1249,8 @@ public class SeqPanel extends JPanel implements MouseListener,
 
             if (max > 0)
             {
-              editCommand.appendEdit(Action.DELETE_GAP,
-                      new SequenceI[]
-                      { seq }, startres, max, av.getAlignment(), true);
+              appendEdit(Action.DELETE_GAP, new SequenceI[]
+              { seq }, startres, max);
             }
           }
         }
@@ -1244,8 +1266,8 @@ public class SeqPanel extends JPanel implements MouseListener,
           }
           else
           {
-            editCommand.appendEdit(Action.INSERT_NUC, new SequenceI[]
-            { seq }, lastres, startres - lastres, av.getAlignment(), true);
+            appendEdit(Action.INSERT_NUC, new SequenceI[]
+            { seq }, lastres, startres - lastres);
           }
         }
       }
@@ -1280,22 +1302,37 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
     }
 
-    editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1,
-            av.getAlignment(), true);
+    appendEdit(Action.DELETE_GAP, seq, blankColumn, 1);
 
-    editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    appendEdit(Action.INSERT_GAP, seq, j, 1);
 
   }
 
+  /**
+   * Helper method to add and perform one edit action.
+   * 
+   * @param action
+   * @param seq
+   * @param pos
+   * @param count
+   */
+  protected void appendEdit(Action action, SequenceI[] seq, int pos,
+          int count)
+  {
+
+    final Edit edit = new EditCommand().new Edit(action, seq, pos, count,
+            av.getAlignment().getGapCharacter());
+
+    editCommand.appendEdit(edit, av.getAlignment(),
+            true, null);
+  }
+
   void deleteChar(int j, SequenceI[] seq, int fixedColumn)
   {
 
-    editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    appendEdit(Action.DELETE_GAP, seq, j, 1);
 
-    editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1,
-            av.getAlignment(), true);
+    appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1);
   }
 
   /**
@@ -1801,52 +1838,68 @@ public class SeqPanel extends JPanel implements MouseListener,
     // handles selection messages...
     // TODO: extend config options to allow user to control if selections may be
     // shared between viewports.
-    if (av == source
-            || !av.followSelection
-            || (av.isSelectionGroupChanged(false) || av
-                    .isColSelChanged(false))
-            || (source instanceof AlignViewport && ((AlignViewport) source)
-                    .getSequenceSetId().equals(av.getSequenceSetId())))
+    boolean iSentTheSelection = (av == source
+            || (source instanceof AlignViewport && ((AlignmentViewport) source)
+            .getSequenceSetId().equals(av.getSequenceSetId())));
+    if (iSentTheSelection || !av.followSelection)
+    {
+      return;
+    }
+
+    /*
+     * Ignore the selection if there is one of our own pending.
+     */
+    if (av.isSelectionGroupChanged(false) || av.isColSelChanged(false))
+    {
+      return;
+    }
+
+    /*
+     * Check for selection in a view of which this one is a dna/protein
+     * complement.
+     */
+    if (selectionFromTranslation(seqsel, colsel, source))
     {
       return;
     }
+
     // do we want to thread this ? (contention with seqsel and colsel locks, I
     // suspect)
     // rules are: colsel is copied if there is a real intersection between
     // sequence selection
-    boolean repaint = false, copycolsel = true;
-    // if (!av.isSelectionGroupChanged(false))
+    boolean repaint = false;
+    boolean copycolsel = true;
+
+    SequenceGroup sgroup = null;
+    if (seqsel != null && seqsel.getSize() > 0)
     {
-      SequenceGroup sgroup = null;
-      if (seqsel != null && seqsel.getSize() > 0)
+      if (av.getAlignment() == null)
       {
-        if (av.getAlignment() == null)
-        {
-          jalview.bin.Cache.log.warn("alignviewport av SeqSetId="
-                  + av.getSequenceSetId() + " ViewId=" + av.getViewId()
-                  + " 's alignment is NULL! returning immediatly.");
-          return;
-        }
-        sgroup = seqsel.intersect(av.getAlignment(),
-                (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
-        if ((sgroup == null || sgroup.getSize() == 0)
-                || (colsel == null || colsel.size() == 0))
-        {
-          // don't copy columns if the region didn't intersect.
-          copycolsel = false;
-        }
+        jalview.bin.Cache.log.warn("alignviewport av SeqSetId="
+                + av.getSequenceSetId() + " ViewId=" + av.getViewId()
+                + " 's alignment is NULL! returning immediately.");
+        return;
       }
-      if (sgroup != null && sgroup.getSize() > 0)
+      sgroup = seqsel.intersect(av.getAlignment(),
+              (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
+      if ((sgroup == null || sgroup.getSize() == 0)
+              || (colsel == null || colsel.size() == 0))
       {
-        av.setSelectionGroup(sgroup);
+        // don't copy columns if the region didn't intersect.
+        copycolsel = false;
       }
-      else
-      {
-        av.setSelectionGroup(null);
-      }
-      av.isSelectionGroupChanged(true);
-      repaint = true;
     }
+    if (sgroup != null && sgroup.getSize() > 0)
+    {
+      av.setSelectionGroup(sgroup);
+    }
+    else
+    {
+      av.setSelectionGroup(null);
+    }
+    av.isSelectionGroupChanged(true);
+    repaint = true;
+
     if (copycolsel)
     {
       // the current selection is unset or from a previous message
@@ -1874,6 +1927,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       av.isColSelChanged(true);
       repaint = true;
     }
+
     if (copycolsel
             && av.hasHiddenColumns()
             && (av.getColumnSelection() == null || av.getColumnSelection()
@@ -1881,11 +1935,52 @@ public class SeqPanel extends JPanel implements MouseListener,
     {
       System.err.println("Bad things");
     }
-    if (repaint)
+    if (repaint) // always true!
     {
       // probably finessing with multiple redraws here
       PaintRefresher.Refresh(this, av.getSequenceSetId());
       // ap.paintAlignment(false);
     }
   }
+
+  /**
+   * If this panel is a cdna/protein translation view of the selection source,
+   * tries to map the source selection to a local one, and returns true. Else
+   * returns false.
+   * 
+   * @param seqsel
+   * @param colsel
+   * @param source
+   */
+  protected boolean selectionFromTranslation(SequenceGroup seqsel,
+          ColumnSelection colsel, SelectionSource source)
+  {
+    if (!(source instanceof AlignViewportI)) {
+      return false;
+    }
+    final AlignViewportI sourceAv = (AlignViewportI) source;
+    if (sourceAv.getCodingComplement() != av && av.getCodingComplement() != sourceAv)
+    {
+      return false;
+    }
+
+    /*
+     * Map sequence selection
+     */
+    SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+
+    /*
+     * Map column selection
+     */
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
+            av);
+    av.setColumnSelection(cs);
+    av.isColSelChanged(true);
+
+    PaintRefresher.Refresh(this, av.getSequenceSetId());
+
+    return true;
+  }
 }