JAL-845 linked protein/dna 'slave' further PoC functionality features/JAL-845linkedProteinDnaEditing
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 30 Jan 2015 17:19:01 +0000 (17:19 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 30 Jan 2015 17:19:01 +0000 (17:19 +0000)
31 files changed:
resources/lang/Messages.properties
src/jalview/analysis/AlignmentSorter.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/api/AlignViewportI.java
src/jalview/bin/Jalview.java
src/jalview/commands/EditCommand.java
src/jalview/commands/OrderCommand.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentOrder.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/Desktop.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SplitFrame.java [new file with mode: 0644]
src/jalview/gui/TreeCanvas.java
src/jalview/gui/TreePanel.java
src/jalview/gui/WsJobParameters.java
src/jalview/io/VamsasAppDatastore.java
src/jalview/jbgui/GSplitFrame.java [new file with mode: 0644]
src/jalview/jbgui/GTreePanel.java
src/jalview/structure/CommandListener.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/util/MappingUtils.java [new file with mode: 0644]
src/jalview/viewmodel/AlignmentViewport.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/commands/EditCommandTest.java
test/jalview/io/Jalview2xmlTests.java

index 8cf8797..0d67dac 100644 (file)
@@ -745,6 +745,7 @@ label.paste_new_window = Paste To New Window
 label.settings_for_param = Settings for {0}
 label.view_params = View {0}
 label.select_all_views = Select all views
+label.all_views = All Views
 label.align_sequences_to_existing_alignment = Align sequences to an existing alignment
 label.realign_with_params = Realign with {0}
 label.calcname_with_default_settings = {0} with Defaults
index b7cfbbd..4f52741 100755 (executable)
  */
 package jalview.analysis;
 
-import java.util.*;
-
-import jalview.datamodel.*;
-import jalview.util.*;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequenceNode;
+import jalview.util.Comparison;
+import jalview.util.MessageManager;
+import jalview.util.QuickSort;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Routines for manipulating the order of a multiple sequence alignment TODO:
@@ -169,7 +178,7 @@ public class AlignmentSorter
    * @param tmp
    *          sequences as a vector
    */
-  private static void setOrder(AlignmentI align, Vector tmp)
+  private static void setOrder(AlignmentI align, List<SequenceI> tmp)
   {
     setOrder(align, vectorSubsetToArray(tmp, align.getSequences()));
   }
@@ -285,7 +294,7 @@ public class AlignmentSorter
   {
     // MAINTAINS ORIGNAL SEQUENCE ORDER,
     // ORDERS BY GROUP SIZE
-    Vector groups = new Vector();
+    List<SequenceGroup> groups = new ArrayList<SequenceGroup>();
 
     if (groups.hashCode() != lastGroupHash)
     {
@@ -303,11 +312,11 @@ public class AlignmentSorter
     {
       for (int j = 0; j < groups.size(); j++)
       {
-        SequenceGroup sg2 = (SequenceGroup) groups.elementAt(j);
+        SequenceGroup sg2 = groups.get(j);
 
         if (sg.getSize() > sg2.getSize())
         {
-          groups.insertElementAt(sg, j);
+          groups.add(j, sg);
 
           break;
         }
@@ -315,22 +324,22 @@ public class AlignmentSorter
 
       if (!groups.contains(sg))
       {
-        groups.addElement(sg);
+        groups.add(sg);
       }
     }
 
     // NOW ADD SEQUENCES MAINTAINING ALIGNMENT ORDER
     // /////////////////////////////////////////////
-    Vector seqs = new Vector();
+    List<SequenceI> seqs = new ArrayList<SequenceI>();
 
     for (int i = 0; i < groups.size(); i++)
     {
-      SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
+      SequenceGroup sg = groups.get(i);
       SequenceI[] orderedseqs = sg.getSequencesInOrder(align);
 
       for (int j = 0; j < orderedseqs.length; j++)
       {
-        seqs.addElement(orderedseqs[j]);
+        seqs.add(orderedseqs[j]);
       }
     }
 
@@ -346,28 +355,8 @@ public class AlignmentSorter
   }
 
   /**
-   * Converts Vector to array. java 1.18 does not have Vector.toArray()
-   * 
-   * @param tmp
-   *          Vector of SequenceI objects
-   * 
-   * @return array of Sequence[]
-   */
-  private static SequenceI[] vectorToArray(Vector tmp)
-  {
-    SequenceI[] seqs = new SequenceI[tmp.size()];
-
-    for (int i = 0; i < tmp.size(); i++)
-    {
-      seqs[i] = (SequenceI) tmp.elementAt(i);
-    }
-
-    return seqs;
-  }
-
-  /**
    * Select sequences in order from tmp that is present in mask, and any
-   * remaining seqeunces in mask not in tmp
+   * remaining sequences in mask not in tmp
    * 
    * @param tmp
    *          thread safe collection of sequences
@@ -379,6 +368,10 @@ public class AlignmentSorter
   private static SequenceI[] vectorSubsetToArray(List<SequenceI> tmp,
           List<SequenceI> mask)
   {
+    // or?
+    // tmp2 = tmp.retainAll(mask);
+    // return tmp2.addAll(mask.removeAll(tmp2))
+
     ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
     int i, idx;
     boolean[] tmask = new boolean[mask.size()];
@@ -421,7 +414,7 @@ public class AlignmentSorter
   public static void sortBy(AlignmentI align, AlignmentOrder order)
   {
     // Get an ordered vector of sequences which may also be present in align
-    Vector tmp = order.getOrder();
+    List<SequenceI> tmp = order.getOrder();
 
     if (lastOrder == order)
     {
@@ -452,11 +445,12 @@ public class AlignmentSorter
    * 
    * @return DOCUMENT ME!
    */
-  private static Vector getOrderByTree(AlignmentI align, NJTree tree)
+  private static List<SequenceI> getOrderByTree(AlignmentI align,
+          NJTree tree)
   {
     int nSeq = align.getHeight();
 
-    Vector tmp = new Vector();
+    List<SequenceI> tmp = new ArrayList<SequenceI>();
 
     tmp = _sortByTree(tree.getTopNode(), tmp, align.getSequences());
 
@@ -494,7 +488,7 @@ public class AlignmentSorter
    */
   public static void sortByTree(AlignmentI align, NJTree tree)
   {
-    Vector tmp = getOrderByTree(align, tree);
+    List<SequenceI> tmp = getOrderByTree(align, tree);
 
     // tmp should properly permute align with tree.
     if (lastTree != tree)
@@ -522,22 +516,22 @@ public class AlignmentSorter
    * 
    * @param align
    *          DOCUMENT ME!
-   * @param seqs
+   * @param tmp
    *          DOCUMENT ME!
    */
-  private static void addStrays(AlignmentI align, Vector seqs)
+  private static void addStrays(AlignmentI align, List<SequenceI> tmp)
   {
     int nSeq = align.getHeight();
 
     for (int i = 0; i < nSeq; i++)
     {
-      if (!seqs.contains(align.getSequenceAt(i)))
+      if (!tmp.contains(align.getSequenceAt(i)))
       {
-        seqs.addElement(align.getSequenceAt(i));
+        tmp.add(align.getSequenceAt(i));
       }
     }
 
-    if (nSeq != seqs.size())
+    if (nSeq != tmp.size())
     {
       System.err
               .println("ERROR: Size still not right even after addStrays");
@@ -556,7 +550,8 @@ public class AlignmentSorter
    * 
    * @return DOCUMENT ME!
    */
-  private static Vector _sortByTree(SequenceNode node, Vector tmp,
+  private static List<SequenceI> _sortByTree(SequenceNode node,
+          List<SequenceI> tmp,
           List<SequenceI> seqset)
   {
     if (node == null)
@@ -577,7 +572,7 @@ public class AlignmentSorter
                                              // seqset.size()==0 ||
                                              // seqset.contains(tmp)))
           {
-            tmp.addElement(node.element());
+            tmp.add((SequenceI) node.element());
           }
         }
       }
index 2dbe015..f3e126d 100644 (file)
@@ -417,7 +417,9 @@ public class AlignmentUtils
 
   /**
    * Align sequence 'alignTo' the same way as 'alignFrom', using the mapping to
-   * match residues and codons.
+   * match residues and codons. Flags control whether existing gaps in unmapped
+   * (intron) and mapped (exon) regions are preserved or not. Gaps linking intro
+   * and exon are only retained if both flags are set.
    * 
    * @param alignTo
    * @param alignFrom
@@ -448,12 +450,13 @@ public class AlignmentUtils
     /*
      * Traverse the aligned protein sequence.
      */
-    int sourceGapLength = 0;
+    int sourceGapMappedLength = 0;
+    boolean inExon = false;
     for (char sourceChar : thatAligned)
     {
       if (sourceChar == sourceGap)
       {
-        sourceGapLength++;
+        sourceGapMappedLength += ratio;
         continue;
       }
 
@@ -476,7 +479,7 @@ public class AlignmentUtils
 
       int mappedCodonStart = mappedPos[0]; // position (1...) of codon start
       int mappedCodonEnd = mappedPos[mappedPos.length - 1]; // codon end pos
-      int trailingCopiedGapLength = 0;
+      StringBuilder trailingCopiedGap = new StringBuilder();
 
       /*
        * Copy dna sequence up to and including this codon. Optionally, include
@@ -486,7 +489,7 @@ public class AlignmentUtils
        * Note this only works for 'linear' splicing, not reverse or interleaved.
        * But then 'align dna as protein' doesn't make much sense otherwise.
        */
-      boolean inCodon = false;
+      int intronLength = 0;
       while (basesWritten < mappedCodonEnd && thisSeqPos < thisSeq.length)
       {
         final char c = thisSeq[thisSeqPos++];
@@ -494,40 +497,49 @@ public class AlignmentUtils
         {
           basesWritten++;
 
-          /*
-           * Is this the start of the mapped codon? If so, add in any extra gap
-           * due to the protein alignment.
-           */
-          if (basesWritten == mappedCodonStart)
+          if (basesWritten < mappedCodonStart)
           {
-            inCodon = true;
-            int gapsToAdd = Math.max(0, ratio * sourceGapLength
-                    - trailingCopiedGapLength);
+            /*
+             * Found an unmapped (intron) base. First add in any preceding gaps
+             * (if wanted).
+             */
+            if (preserveUnmappedGaps && trailingCopiedGap.length() > 0)
+            {
+              thisAligned.append(trailingCopiedGap.toString());
+              intronLength += trailingCopiedGap.length();
+              trailingCopiedGap = new StringBuilder();
+            }
+            intronLength++;
+            inExon = false;
+          }
+          else
+          {
+            final boolean startOfCodon = basesWritten == mappedCodonStart;
+            int gapsToAdd = calculateGapsToInsert(preserveMappedGaps,
+                    preserveUnmappedGaps, sourceGapMappedLength, inExon,
+                    trailingCopiedGap.length(), intronLength, startOfCodon);
             for (int i = 0; i < gapsToAdd; i++)
             {
               thisAligned.append(myGapChar);
             }
-            sourceGapLength = 0;
+            sourceGapMappedLength = 0;
+            inExon = true;
           }
           thisAligned.append(c);
-          trailingCopiedGapLength = 0;
+          trailingCopiedGap = new StringBuilder();
         }
-        else if ((!inCodon && preserveUnmappedGaps)
-                || (inCodon && preserveMappedGaps))
+        else
         {
-          thisAligned.append(c);
-          trailingCopiedGapLength++;
+          if (inExon && preserveMappedGaps)
+          {
+            trailingCopiedGap.append(myGapChar);
+          }
+          else if (!inExon && preserveUnmappedGaps)
+          {
+            trailingCopiedGap.append(myGapChar);
+          }
         }
       }
-
-      /*
-       * Expand (if necessary) the trailing gap to the size of the aligned gap.
-       */
-      int gapsToAdd = (ratio * sourceGapLength - trailingCopiedGapLength);
-      for (int i = 0; i < gapsToAdd; i++)
-      {
-        thisAligned.append(myGapChar);
-      }
     }
 
     /*
@@ -548,4 +560,69 @@ public class AlignmentUtils
      */
     alignTo.setSequence(new String(thisAligned));
   }
+
+  /**
+   * Helper method to work out how many gaps to insert when realigning.
+   * 
+   * @param preserveMappedGaps
+   * @param preserveUnmappedGaps
+   * @param sourceGapMappedLength
+   * @param inExon
+   * @param trailingCopiedGap
+   * @param intronLength
+   * @param startOfCodon
+   * @return
+   */
+  protected static int calculateGapsToInsert(boolean preserveMappedGaps,
+          boolean preserveUnmappedGaps, int sourceGapMappedLength,
+          boolean inExon, int trailingGapLength,
+          int intronLength, final boolean startOfCodon)
+  {
+    int gapsToAdd = 0;
+    if (startOfCodon)
+    {
+      /*
+       * Reached start of codon. Ignore trailing gaps in intron unless we are
+       * preserving gaps in both exon and intron. Ignore them anyway if the
+       * protein alignment introduces a gap at least as large as the intronic
+       * region.
+       */
+      if (inExon && !preserveMappedGaps)
+      {
+        trailingGapLength = 0;
+      }
+      if (!inExon && !(preserveMappedGaps && preserveUnmappedGaps))
+      {
+        trailingGapLength = 0;
+      }
+      if (inExon)
+      {
+        gapsToAdd = Math.max(sourceGapMappedLength, trailingGapLength);
+      }
+      else
+      {
+        if (intronLength + trailingGapLength <= sourceGapMappedLength)
+        {
+          gapsToAdd = sourceGapMappedLength - intronLength;
+        }
+        else
+        {
+          gapsToAdd = Math.min(intronLength + trailingGapLength
+                  - sourceGapMappedLength, trailingGapLength);
+        }
+      }
+    }
+    else
+    {
+      /*
+       * second or third base of codon; check for any gaps in dna
+       */
+      if (!preserveMappedGaps)
+      {
+        trailingGapLength = 0;
+      }
+      gapsToAdd = Math.max(sourceGapMappedLength, trailingGapLength);
+    }
+    return gapsToAdd;
+  }
 }
index d8ba30d..6753a29 100644 (file)
  */
 package jalview.api;
 
-import java.awt.Color;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
 import jalview.analysis.Conservation;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
@@ -36,6 +31,11 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
 
+import java.awt.Color;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
 /**
  * @author jimp
  * 
@@ -200,4 +200,13 @@ public interface AlignViewportI
   List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
           boolean selectedOnly);
 
+  /**
+   * Returns a viewport which is a 'slave of' this one i.e. updated in
+   * tandem in some sense, or null if none is set.
+   * 
+   * @return
+   */
+  AlignViewportI getSlave();
+  
+  void setSlave(AlignViewportI sl);
 }
index ff1b180..d11a8f9 100755 (executable)
@@ -924,7 +924,7 @@ public class Jalview
   private static FeatureFetcher startFeatureFetching(final Vector dasSources)
   {
     FeatureFetcher ff = new FeatureFetcher();
-    AlignFrame afs[] = Desktop.getAlignframes();
+    AlignFrame afs[] = Desktop.getAlignFrames();
     if (afs == null || afs.length == 0)
     {
       return null;
index ba01b1c..38a45ce 100644 (file)
@@ -63,7 +63,55 @@ public class EditCommand implements CommandI
 {
   public enum Action
   {
-    INSERT_GAP, DELETE_GAP, CUT, PASTE, REPLACE, INSERT_NUC
+    INSERT_GAP()
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return DELETE_GAP;
+      }
+    },
+    DELETE_GAP()
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return INSERT_GAP;
+      }
+    },
+    CUT()
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return PASTE;
+      }
+    },
+    PASTE()
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return CUT;
+      }
+    },
+    REPLACE
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return REPLACE;
+      }
+    },
+    INSERT_NUC
+    {
+      @Override
+      public Action getUndoAction()
+      {
+        return null;
+      }
+    };
+    public abstract Action getUndoAction();
   };
 
   private List<Edit> edits = new ArrayList<Edit>();
@@ -551,7 +599,7 @@ public class EditCommand implements CommandI
       if (command.seqs[i].getLength() < 1)
       {
         // ie this sequence was deleted, we need to
-        // read it to the alignment
+        // readd it to the alignment
         if (command.alIndex[i] < command.al.getHeight())
         {
           List<SequenceI> sequences;
@@ -1227,7 +1275,9 @@ public class EditCommand implements CommandI
           }
           else
           {
-            throw new IllegalStateException("Can't undo edit action " + action);
+            System.err.println("Can't undo edit action " + action);
+            // throw new IllegalStateException("Can't undo edit action " +
+            // action);
           }
         }
       }
index b610a54..f4010a1 100644 (file)
  */
 package jalview.commands;
 
-import jalview.analysis.*;
-import jalview.datamodel.*;
+import jalview.analysis.AlignmentSorter;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
 
+/**
+ * An undoable command to reorder the sequences in an alignment.
+ * 
+ * @author gmcarstairs
+ *
+ */
 public class OrderCommand implements CommandI
 {
   String description;
 
+  /*
+   * The sequence order before sorting (target order for an undo)
+   */
   SequenceI[] seqs;
 
+  /*
+   * The sequence order specified by this command
+   */
   SequenceI[] seqs2;
 
+  /*
+   * The alignment the command acts on
+   */
   AlignmentI al;
 
+  /**
+   * Constructor given the 'undo' sequence order, and the (already) sorted
+   * alignment.
+   * 
+   * @param description
+   *          a text label for the 'undo' menu option
+   * @param seqs
+   *          the sequence order for undo
+   * @param al
+   *          the alignment as ordered by this command
+   */
   public OrderCommand(String description, SequenceI[] seqs, AlignmentI al)
   {
     this.description = description;
@@ -61,4 +88,15 @@ public class OrderCommand implements CommandI
   {
     AlignmentSorter.setOrder(al, seqs);
   }
+
+  /**
+   * Returns the sequence order used to sort, or before sorting if undo=true.
+   * 
+   * @param undo
+   * @return
+   */
+  public SequenceI[] getSequenceOrder(boolean undo)
+  {
+    return undo ? seqs : seqs2;
+  }
 }
index cea5956..5c2bc22 100755 (executable)
@@ -90,6 +90,20 @@ public class Alignment implements AlignmentI
   }
 
   /**
+   * Make a 'copy' alignment - sequences have new copies of features and
+   * annotations, but share the original dataset sequences.
+   */
+  public Alignment(AlignmentI al)
+  {
+    SequenceI[] seqs = al.getSequencesArray();
+    for (int i = 0; i < seqs.length; i++)
+    {
+      seqs[i] = new Sequence(seqs[i]);
+    }
+    initAlignment(seqs);
+  }
+
+  /**
    * Make an alignment from an array of Sequences.
    * 
    * @param sequences
@@ -1610,10 +1624,17 @@ public class Alignment implements AlignmentI
     return dataset;
   }
 
+  /**
+   * Align this alignment like the given (mapped) one.
+   */
   @Override
   public int alignAs(AlignmentI al)
   {
-    return alignAs(al, true, true);
+    /*
+     * Currently retains unmapped gaps (in introns), regaps mapped regions
+     * (exons)
+     */
+    return alignAs(al, false, true);
   }
 
   /**
@@ -1625,7 +1646,16 @@ public class Alignment implements AlignmentI
    * nucleotide bases. Does nothing if alignment of protein from cDNA is
    * requested (not yet implemented).
    * 
+   * Parameters control whether gaps in exon (mapped) and intron (unmapped)
+   * regions are preserved. Gaps that connect introns to exons are treated
+   * conservatively, i.e. only preserved if both intron and exon gaps are
+   * preserved.
+   * 
    * @param al
+   * @param preserveMappedGaps
+   *          if true, gaps within and between mapped codons are preserved
+   * @param preserveUnmappedGaps
+   *          if true, gaps within and between unmapped codons are preserved
    */
 //  @Override
   public int alignAs(AlignmentI al, boolean preserveMappedGaps,
index c0f9ab4..92d5f6c 100755 (executable)
@@ -20,7 +20,9 @@
  */
 package jalview.datamodel;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 public class AlignmentOrder
 {
@@ -51,7 +53,7 @@ public class AlignmentOrder
 
   private String Name;
 
-  private Vector Order = null;
+  private List<SequenceI> Order = null;
 
   /**
    * Creates a new AlignmentOrder object.
@@ -64,9 +66,8 @@ public class AlignmentOrder
    * AlignmentOrder
    * 
    * @param anOrder
-   *          Vector
    */
-  public AlignmentOrder(Vector anOrder)
+  public AlignmentOrder(List<SequenceI> anOrder)
   {
     Order = anOrder;
   }
@@ -79,11 +80,11 @@ public class AlignmentOrder
    */
   public AlignmentOrder(AlignmentI orderFrom)
   {
-    Order = new Vector();
+    Order = new ArrayList<SequenceI>();
 
-    for (int i = 0, ns = orderFrom.getHeight(); i < ns; i++)
+    for (SequenceI seq : orderFrom.getSequences())
     {
-      Order.addElement(orderFrom.getSequenceAt(i));
+      Order.add(seq);
     }
   }
 
@@ -95,12 +96,7 @@ public class AlignmentOrder
    */
   public AlignmentOrder(SequenceI[] orderFrom)
   {
-    Order = new Vector();
-
-    for (int i = 0, ns = orderFrom.length; i < ns; i++)
-    {
-      Order.addElement(orderFrom[i]);
-    }
+    Order = new ArrayList<SequenceI>(Arrays.asList(orderFrom));
   }
 
   /**
@@ -151,7 +147,7 @@ public class AlignmentOrder
    * @param Order
    *          DOCUMENT ME!
    */
-  public void setOrder(Vector Order)
+  public void setOrder(List<SequenceI> Order)
   {
     this.Order = Order;
   }
@@ -161,7 +157,7 @@ public class AlignmentOrder
    * 
    * @return DOCUMENT ME!
    */
-  public Vector getOrder()
+  public List<SequenceI> getOrder()
   {
     return Order;
   }
@@ -178,7 +174,7 @@ public class AlignmentOrder
     int found = Order.indexOf(oldref);
     if (found > -1)
     {
-      Order.setElementAt(newref, found);
+      Order.set(found, newref);
     }
     return found > -1;
   }
@@ -189,9 +185,14 @@ public class AlignmentOrder
    * @param o
    * @return true if o orders the same sequenceI objects in the same way
    */
-  public boolean equals(AlignmentOrder o)
+  @Override
+  public boolean equals(Object o)
   {
-    return equals(o, true);
+    if (o == null || !(o instanceof AlignmentOrder))
+    {
+      return false;
+    }
+    return equals((AlignmentOrder) o, true);
   }
 
   /**
@@ -223,7 +224,7 @@ public class AlignmentOrder
         {
           for (int i = 0, j = o.Order.size(); i < j; i++)
           {
-            if (Order.elementAt(i) != o.Order.elementAt(i))
+            if (Order.get(i) != o.Order.get(i))
             {
               return false;
             }
@@ -271,7 +272,7 @@ public class AlignmentOrder
       }
       if (Order != null && o.Order != null)
       {
-        Vector c, s;
+        List<SequenceI> c, s;
         if (o.Order.size() > Order.size())
         {
           c = o.Order;
@@ -292,7 +293,7 @@ public class AlignmentOrder
           int last = -1;
           for (int i = 0, j = s.size(); i < j; i++)
           {
-            int pos = c.indexOf(s.elementAt(i)); // JBPNote - optimize by
+            int pos = c.indexOf(s.get(i)); // JBPNote - optimize by
             // incremental position search
             if (pos > last)
             {
index 752c6d4..9c046c6 100755 (executable)
@@ -27,7 +27,6 @@ import jalview.schemes.ResidueProperties;
 
 import java.awt.Color;
 import java.util.ArrayList;
-import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -160,13 +159,10 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     if (seqsel != null)
     {
-      sequences = new Vector();
-      Enumeration<SequenceI> sq = seqsel.sequences.elements();
-      while (sq.hasMoreElements())
+      for (SequenceI seq : seqsel.sequences)
       {
-        sequences.addElement(sq.nextElement());
+        sequences.addElement(seq);
       }
-      ;
       if (seqsel.groupName != null)
       {
         groupName = new String(seqsel.groupName);
@@ -989,12 +985,15 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     SequenceGroup sgroup = new SequenceGroup(this);
     SequenceI[] insect = getSequencesInOrder(alignment);
-    sgroup.sequences = new Vector();
-    for (int s = 0; insect != null && s < insect.length; s++)
+    sgroup.sequences = new Vector<SequenceI>();
+    if (insect != null)
     {
-      if (map == null || map.containsKey(insect[s]))
+      for (SequenceI seq : insect)
       {
-        sgroup.sequences.addElement(insect[s]);
+        if (map == null || map.containsKey(seq))
+        {
+          sgroup.sequences.addElement(seq);
+        }
       }
     }
     // Enumeration en =getSequences(hashtable).elements();
index 84872ca..b47e7d1 100644 (file)
@@ -27,11 +27,11 @@ import jalview.analysis.AlignmentUtils.MappingResult;
 import jalview.analysis.Conservation;
 import jalview.analysis.CrossRef;
 import jalview.analysis.Dna;
-import jalview.analysis.NJTree;
 import jalview.analysis.ParseProperties;
 import jalview.analysis.SequenceIdMatcher;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
+import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.analysis.ScoreModelI;
 import jalview.bin.Cache;
@@ -162,7 +162,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   public AlignViewControllerI avc;
 
-  Vector alignPanels = new Vector();
+  List<AlignmentPanel> alignPanels = new ArrayList<AlignmentPanel>();
 
   /**
    * Last format used to load or save alignments in this window
@@ -599,7 +599,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     avc = new jalview.controller.AlignViewController(this, viewport,
             alignPanel);
 
-    alignPanels.addElement(ap);
+    alignPanels.add(ap);
 
     PaintRefresher.Register(ap, ap.av.getSequenceSetId());
 
@@ -642,7 +642,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     expandViews.setEnabled(true);
     gatherViews.setEnabled(true);
     tabbedPane.setVisible(true);
-    AlignmentPanel first = (AlignmentPanel) alignPanels.firstElement();
+    AlignmentPanel first = alignPanels.get(0);
     tabbedPane.addTab(first.av.viewName, first);
     this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
   }
@@ -727,7 +727,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     int alreadyLinkedCount = 0;
     final AlignmentI thisAlignment = this.alignPanel.getAlignment();
 
-    for (AlignFrame af : Desktop.getAlignframes())
+    for (AlignFrame af : Desktop.getAlignFrames())
     {
       if (af.alignPanel != null)
       {
@@ -780,7 +780,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     int seqCount = 0;
     int alignCount = 0;
     final AlignmentI thisAlignment = this.alignPanel.getAlignment();
-    for (AlignFrame af : Desktop.getAlignframes())
+    for (AlignFrame af : Desktop.getAlignFrames())
     {
       if (af.alignPanel != null)
       {
@@ -1441,7 +1441,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             // setClosed(true) is called
             for (int i = 0; i < alignPanels.size(); i++)
             {
-              AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
+              AlignmentPanel ap = alignPanels.get(i);
               ap.closePanel();
             }
           }
@@ -1471,7 +1471,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     int index = tabbedPane.getSelectedIndex();
     int closedindex = tabbedPane.indexOfComponent(alignPanel2);
-    alignPanels.removeElement(alignPanel2);
+    alignPanels.remove(alignPanel2);
     // Unnecessary
     // if (viewport == alignPanel2.av)
     // {
@@ -1551,11 +1551,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (alignPanels != null)
     {
-      Enumeration e = alignPanels.elements();
       AlignmentI[] als = new AlignmentI[alignPanels.size()];
-      for (int i = 0; e.hasMoreElements(); i++)
+      int i = 0;
+      for (AlignmentPanel ap : alignPanels)
       {
-        als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
+        als[i++] = ap.av.getAlignment();
       }
       return als;
     }
@@ -1705,6 +1705,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.getAlignment().moveSelectedSequencesByOne(sg,
             viewport.getHiddenRepSequences(), up);
     alignPanel.paintAlignment(true);
+
+    final AlignViewportI slave = viewport.getSlave();
+    if (slave != null)
+    {
+      final SequenceGroup selectionGroup = viewport.getSlave()
+              .getSelectionGroup();
+      if (selectionGroup != null)
+      {
+        viewport.getSlave()
+                .getAlignment()
+                .moveSelectedSequencesByOne(
+                        viewport.getSlave().getSelectionGroup(),
+                        viewport.getSlave().getHiddenRepSequences(), up);
+      }
+    }
   }
 
   synchronized void slideSequences(boolean right, int size)
@@ -2174,7 +2189,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 alignment.getSequences());
         if (alignPanels != null)
         {
-          for (AlignmentPanel ap : ((Vector<AlignmentPanel>) alignPanels))
+          for (AlignmentPanel ap : alignPanels)
           {
             ap.validateAnnotationDimensions(false);
           }
@@ -4227,13 +4242,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       final TreePanel tp = (TreePanel) treePanels.elementAt(i);
       final JMenuItem item = new JMenuItem(tp.getTitle());
-      final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree();
       item.addActionListener(new java.awt.event.ActionListener()
       {
         @Override
         public void actionPerformed(ActionEvent e)
         {
-          tp.sortByTree_actionPerformed(null);
+          tp.sortByTree_actionPerformed();
           addHistoryItem(tp.sortAlignmentIn(alignPanel));
 
         }
@@ -5329,7 +5343,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (index > -1)
     {
-      alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
+      alignPanel = alignPanels.get(index);
       viewport = alignPanel.av;
       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
       setMenusFromViewport(viewport);
@@ -5878,7 +5892,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @return alignment panels in this alignment frame
    */
-  public List<AlignmentViewPanel> getAlignPanels()
+  public List<? extends AlignmentViewPanel> getAlignPanels()
   {
     return alignPanels == null ? Arrays.asList(alignPanel)
             : alignPanels;
index 705f53a..f00e4d3 100644 (file)
@@ -45,7 +45,7 @@ import jalview.analysis.NJTree;
 import jalview.api.AlignViewportI;
 import jalview.bin.Cache;
 import jalview.commands.CommandI;
-import jalview.commands.EditCommand;
+import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
@@ -70,13 +70,11 @@ import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.Hashtable;
-import java.util.List;
 import java.util.Set;
 import java.util.Vector;
 
 import javax.swing.JInternalFrame;
 import javax.swing.JOptionPane;
-import javax.swing.JSplitPane;
 
 /**
  * DOCUMENT ME!
@@ -1115,6 +1113,9 @@ public class AlignViewport extends AlignmentViewport implements
 
   boolean showSeqFeaturesHeight;
 
+  /**
+   * Send the current selection to be broadcast to any selection listeners.
+   */
   public void sendSelection()
   {
     jalview.structure.StructureSelectionManager
@@ -1325,23 +1326,30 @@ public class AlignViewport extends AlignmentViewport implements
    */
   @Override
   public void mirrorCommand(CommandI command, boolean undo,
-          StructureSelectionManager ssm)
+          StructureSelectionManager ssm, VamsasSource source)
   {
     /*
-     * Only EditCommand is currently handled by listeners.
+     * ...work in progress... do nothing unless we are a 'slave' of the source
+     * May replace this with direct calls not via SSM.
      */
-    if (!(command instanceof EditCommand))
+    if (source instanceof AlignViewportI
+            && ((AlignViewportI) source).getSlave() == this)
+    {
+      // ok to continue;
+    }
+    else
     {
       return;
     }
-    EditCommand edit = (EditCommand) command;
 
-    List<SequenceI> seqs = getAlignment().getSequences();
-    EditCommand mappedCommand = ssm.mapEditCommand(edit, undo, seqs,
+    CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(),
             getGapCharacter());
-    AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments();
-    mappedCommand.performEdit(0, views);
-    getAlignPanel().alignmentChanged();
+    if (mappedCommand != null)
+    {
+      AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments();
+      mappedCommand.doCommand(views);
+      getAlignPanel().alignmentChanged();
+    }
   }
 
   @Override
@@ -1416,8 +1424,8 @@ public class AlignViewport extends AlignmentViewport implements
 
   /**
    * Add the sequences from the given alignment to this viewport. Optionally,
-   * may give the user the option to open a new frame or panel linking cDNA and
-   * protein.
+   * may give the user the option to open a new frame, or split panel, with cDNA
+   * and protein linked.
    * 
    * @param al
    * @param title
@@ -1459,9 +1467,8 @@ public class AlignViewport extends AlignmentViewport implements
 
   /**
    * Show a dialog with the option to open and link (cDNA <-> protein) as a new
-   * alignment. Returns true if the new alignment was opened, false if not -
-   * either because the user declined the offer, or because no mapping could be
-   * made.
+   * alignment. Returns true if the new alignment was opened, false if not,
+   * because the user declined the offer.
    * 
    * @param title
    */
@@ -1477,11 +1484,6 @@ public class AlignViewport extends AlignmentViewport implements
             MessageManager.getString("label.open_linked_alignment"),
             JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null,
             options, options[0]);
-    // int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
-    // question,
-    // MessageManager.getString("label.open_linked_alignment"),
-    // JOptionPane.YES_NO_OPTION,
-    // JOptionPane.QUESTION_MESSAGE);
 
     if (response != 1 && response != 2)
     {
@@ -1494,13 +1496,19 @@ public class AlignViewport extends AlignmentViewport implements
      * Create the AlignFrame first (which creates the new alignment's datasets),
      * before attempting sequence mapping.
      */
-    AlignFrame alignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
+    AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
             AlignFrame.DEFAULT_HEIGHT);
 
-    final AlignmentI protein = al.isNucleotide() ? getAlignment() : al;
-    final AlignmentI cdna = al.isNucleotide() ? al : getAlignment();
+    /*
+     * Identify protein and dna alignments. Make a copy of this one if opening
+     * in a new split pane.
+     */
+    AlignmentI thisAlignment = openSplitPane ? new Alignment(getAlignment())
+            : getAlignment();
+    final AlignmentI protein = al.isNucleotide() ? thisAlignment : al;
+    final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
 
-    alignFrame.statusBar.setText(MessageManager.formatMessage(
+    newAlignFrame.statusBar.setText(MessageManager.formatMessage(
             "label.successfully_loaded_file", new Object[]
             { title }));
 
@@ -1510,12 +1518,11 @@ public class AlignViewport extends AlignmentViewport implements
     // {
     // alignFrame.setFileName(file, format);
     // }
+
     if (openInNewWindow)
     {
-      /*
-       * open in new window
-       */
-      Desktop.addInternalFrame(alignFrame, title, AlignFrame.DEFAULT_WIDTH,
+      Desktop.addInternalFrame(newAlignFrame, title,
+              AlignFrame.DEFAULT_WIDTH,
               AlignFrame.DEFAULT_HEIGHT);
     }
 
@@ -1523,6 +1530,8 @@ public class AlignViewport extends AlignmentViewport implements
      * Try to find mappings for at least one sequence.
      */
     MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna);
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
     if (mapped == MappingResult.Mapped)
     {
 
@@ -1530,15 +1539,7 @@ public class AlignViewport extends AlignmentViewport implements
        * Register the mappings (held on the protein alignment) with the
        * StructureSelectionManager (for mouseover linking).
        */
-      final StructureSelectionManager ssm = StructureSelectionManager
-              .getStructureSelectionManager(Desktop.instance);
       ssm.addMappings(protein.getCodonFrames());
-
-      /*
-       * Set the cDNA to listen for edits on the protein.
-       */
-      ssm.addCommandListener(al.isNucleotide() ? alignFrame.getViewport()
-              : this);
     }
     else
     {
@@ -1555,7 +1556,8 @@ public class AlignViewport extends AlignmentViewport implements
 
     try
     {
-      alignFrame.setMaximum(jalview.bin.Cache.getDefault("SHOW_FULLSCREEN",
+      newAlignFrame.setMaximum(jalview.bin.Cache.getDefault(
+              "SHOW_FULLSCREEN",
               false));
     } catch (java.beans.PropertyVetoException ex)
     {
@@ -1564,22 +1566,32 @@ public class AlignViewport extends AlignmentViewport implements
     if (openSplitPane)
     {
       /*
-       * Open in split pane. Original sequence above, new one below.
+       * Open in split pane. DNA sequence above, protein below.
        */
-      JInternalFrame splitFrame = new JInternalFrame();
-      splitFrame.setSize(AlignFrame.DEFAULT_WIDTH,
-              AlignFrame.DEFAULT_HEIGHT);
-      // TODO not quite right to 'move' AlignPanel from 'this' to the split
-      // pane
-      // TODO probably want linked editing set up here
-      JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
-              getAlignPanel(), alignFrame.alignPanel);
-      splitPane.setDividerLocation(0.5d);
-      splitFrame.setSize(AlignFrame.DEFAULT_WIDTH,
-              AlignFrame.DEFAULT_HEIGHT);
-      splitFrame.add(splitPane);
+      AlignFrame copyMe = new AlignFrame(thisAlignment,
+              AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+      copyMe.setTitle(""); // TODO would like this AlignFrame.title here
+      final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
+              : newAlignFrame;
+      final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame
+              : copyMe;
+      newAlignFrame.setTitle(title);
+
+      cdnaFrame.setVisible(true);
+      proteinFrame.setVisible(true);
+      JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame);
       Desktop.addInternalFrame(splitFrame, title, AlignFrame.DEFAULT_WIDTH,
               AlignFrame.DEFAULT_HEIGHT);
+
+      /*
+       * Set the cDNA to list for edits on the protein.
+       */
+      ssm.addCommandListener(cdnaFrame.getViewport());
+
+      /*
+       * cDNA is 'slaved' to edits, selection, sorting, show/hide on protein
+       */
+      proteinFrame.getViewport().setSlave(cdnaFrame.getViewport());
     }
 
     return true;
index 6eeb0c2..e2a57af 100644 (file)
@@ -68,6 +68,7 @@ import java.lang.reflect.Constructor;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.StringTokenizer;
 import java.util.Vector;
 import java.util.concurrent.ExecutorService;
@@ -1638,39 +1639,41 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
   {
-    int count = 0;
     if (Desktop.desktop == null)
     {
       // no frames created and in headless mode
       // TODO: verify that frames are recoverable when in headless mode
       return null;
     }
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
-    ArrayList aps = new ArrayList();
-    for (int t = 0; t < frames.length; t++)
+    List<AlignmentPanel> aps = new ArrayList<AlignmentPanel>();
+    AlignFrame[] frames = getAlignFrames();
+    if (frames == null)
+    {
+      return null;
+    }
+    for (AlignFrame af : frames)
     {
-      if (frames[t] instanceof AlignFrame)
+      for (AlignmentPanel ap : af.alignPanels)
       {
-        AlignFrame af = (AlignFrame) frames[t];
-        for (int a = 0; a < af.alignPanels.size(); a++)
+        if (alignmentId.equals(ap.av.getSequenceSetId()))
         {
-          if (alignmentId.equals(((AlignmentPanel) af.alignPanels
-                  .elementAt(a)).av.getSequenceSetId()))
-          {
-            aps.add(af.alignPanels.elementAt(a));
-          }
+          aps.add(ap);
         }
       }
+      // for (int a = 0; a < af.alignPanels.size(); a++)
+      // {
+      // if (alignmentId.equals(af.alignPanels
+      // .get(a).av.getSequenceSetId()))
+      // {
+      // aps.add(af.alignPanels.get(a));
+      // }
+      // }
     }
     if (aps.size() == 0)
     {
       return null;
     }
-    AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
-    for (int t = 0; t < vap.length; t++)
-    {
-      vap[t] = (AlignmentPanel) aps.get(t);
-    }
+    AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
     return vap;
   }
 
@@ -1699,11 +1702,10 @@ public class Desktop extends jalview.jbgui.GDesktop implements
             {
               for (int a = 0; a < afr.alignPanels.size(); a++)
               {
-                if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels
-                        .elementAt(a)).av.getSequenceSetId()))
+                if (sequenceSetId.equals(afr.alignPanels.get(a).av
+                        .getSequenceSetId()))
                 {
-                  viewp.addElement(((AlignmentPanel) afr.alignPanels
-                          .elementAt(a)).av);
+                  viewp.addElement(afr.alignPanels.get(a).av);
                 }
               }
             }
@@ -1734,7 +1736,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
     for (int i = 0; i < size; i++)
     {
-      AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
+      AlignmentPanel ap = af.alignPanels.get(i);
       AlignFrame newaf = new AlignFrame(ap);
       if (ap.av.explodedPosition != null
               && !ap.av.explodedPosition.equals(af.getBounds()))
@@ -1768,7 +1770,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         boolean gatherThis = false;
         for (int a = 0; a < af.alignPanels.size(); a++)
         {
-          AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
+          AlignmentPanel ap = af.alignPanels.get(a);
           if (viewId.equals(ap.av.getSequenceSetId()))
           {
             gatherThis = true;
@@ -2277,7 +2279,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   /**
    * Accessor method to quickly get all the AlignmentFrames loaded.
    */
-  public static AlignFrame[] getAlignframes()
+  public static AlignFrame[] getAlignFrames()
   {
     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
 
@@ -2285,35 +2287,43 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     {
       return null;
     }
-    Vector avp = new Vector();
-    try
+    List<AlignFrame> avp = new ArrayList<AlignFrame>();
+    // REVERSE ORDER
+    for (int i = frames.length - 1; i > -1; i--)
     {
-      // REVERSE ORDER
-      for (int i = frames.length - 1; i > -1; i--)
+      if (frames[i] instanceof AlignFrame)
       {
-        if (frames[i] instanceof AlignFrame)
+        avp.add((AlignFrame) frames[i]);
+      }
+      else if (frames[i] instanceof SplitFrame)
+      {
+        /*
+         * Also check for a split frame containing an AlignFrame
+         */
+        SplitFrame sf = (SplitFrame) frames[i];
+        if (sf.getTopComponent() instanceof AlignFrame)
         {
-          AlignFrame af = (AlignFrame) frames[i];
-          avp.addElement(af);
+          avp.add((AlignFrame) sf.getTopComponent());
+        }
+        if (sf.getBottomComponent() instanceof AlignFrame)
+        {
+          avp.add((AlignFrame) sf.getBottomComponent());
         }
       }
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
     }
     if (avp.size() == 0)
     {
       return null;
     }
-    AlignFrame afs[] = new AlignFrame[avp.size()];
-    for (int i = 0, j = avp.size(); i < j; i++)
-    {
-      afs[i] = (AlignFrame) avp.elementAt(i);
-    }
-    avp.clear();
+    AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
     return afs;
   }
 
+  /**
+   * Returns an array of any AppJmol frames in the Desktop (or null if none).
+   * 
+   * @return
+   */
   public AppJmol[] getJmols()
   {
     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
@@ -2322,32 +2332,21 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     {
       return null;
     }
-    Vector avp = new Vector();
-    try
+    List<AppJmol> avp = new ArrayList<AppJmol>();
+    // REVERSE ORDER
+    for (int i = frames.length - 1; i > -1; i--)
     {
-      // REVERSE ORDER
-      for (int i = frames.length - 1; i > -1; i--)
+      if (frames[i] instanceof AppJmol)
       {
-        if (frames[i] instanceof AppJmol)
-        {
-          AppJmol af = (AppJmol) frames[i];
-          avp.addElement(af);
-        }
+        AppJmol af = (AppJmol) frames[i];
+        avp.add(af);
       }
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
     }
     if (avp.size() == 0)
     {
       return null;
     }
-    AppJmol afs[] = new AppJmol[avp.size()];
-    for (int i = 0, j = avp.size(); i < j; i++)
-    {
-      afs[i] = (AppJmol) avp.elementAt(i);
-    }
-    avp.clear();
+    AppJmol afs[] = avp.toArray(new AppJmol[avp.size()]);
     return afs;
   }
 
@@ -2471,7 +2470,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
   /**
    * This will return the first AlignFrame viewing AlignViewport av. It will
-   * break if there are more than one AlignFrames viewing a particular av. This
+   * break if there are more than one AlignFrames viewing a particular av.
    * 
    * @param av
    * @return alignFrame for av
index 91b23e4..bf2415e 100755 (executable)
  */
 package jalview.gui;
 
-import java.awt.*;
-import java.awt.image.*;
+import jalview.datamodel.SequenceI;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
 import java.util.List;
 
-import javax.swing.*;
-
-import jalview.datamodel.*;
+import javax.swing.JPanel;
 
 /**
  * DOCUMENT ME!
index be89829..0e3d878 100644 (file)
@@ -402,8 +402,7 @@ public class Jalview2XML
 
           for (ap = 0; ap < apSize; ap++)
           {
-            AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
-                    .elementAt(ap);
+            AlignmentPanel apanel = af.alignPanels.get(ap);
             String fileName = apSize == 1 ? shortName : ap + shortName;
             if (!fileName.endsWith(".xml"))
             {
@@ -458,8 +457,8 @@ public class Jalview2XML
       Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
       for (ap = 0; ap < apSize; ap++)
       {
-        AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
-                .elementAt(ap);
+        AlignmentPanel apanel = af.alignPanels
+                .get(ap);
         String jfileName = apSize == 1 ? fileName : fileName + ap;
         if (!jfileName.endsWith(".xml"))
         {
index c2bee83..1778f27 100644 (file)
@@ -2474,15 +2474,7 @@ public class PopupMenu extends JPopupMenu
     }
 
     int gsize = sg.getSize();
-    SequenceI[] hseqs;
-
-    hseqs = new SequenceI[gsize];
-
-    int index = 0;
-    for (int i = 0; i < gsize; i++)
-    {
-      hseqs[index++] = sg.getSequenceAt(i);
-    }
+    SequenceI[] hseqs = sg.getSequences().toArray(new SequenceI[gsize]);
 
     ap.av.hideSequence(hseqs);
     // refresh(); TODO: ? needed ?
index e923be7..cca1685 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.AlignedCodonFrame;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
@@ -1869,52 +1871,67 @@ 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))
+    boolean iSentTheSelection = (av == source
             || (source instanceof AlignViewport && ((AlignViewport) source)
-                    .getSequenceSetId().equals(av.getSequenceSetId())))
+            .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 'slave'.
+     */
+    if (selectionAsSlave(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)
-        {
-          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;
-        }
-      }
-      if (sgroup != null && sgroup.getSize() > 0)
+      if (av.getAlignment() == null)
       {
-        av.setSelectionGroup(sgroup);
+        jalview.bin.Cache.log.warn("alignviewport av SeqSetId="
+                + av.getSequenceSetId() + " ViewId=" + av.getViewId()
+                + " 's alignment is NULL! returning immediately.");
+        return;
       }
-      else
+      sgroup = seqsel.intersect(av.getAlignment(),
+              (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
+      if ((sgroup == null || sgroup.getSize() == 0)
+              || (colsel == null || colsel.size() == 0))
       {
-        av.setSelectionGroup(null);
+        // don't copy columns if the region didn't intersect.
+        copycolsel = false;
       }
-      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
@@ -1942,6 +1959,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       av.isColSelChanged(true);
       repaint = true;
     }
+
     if (copycolsel
             && av.hasHiddenColumns()
             && (av.getColumnSelection() == null || av.getColumnSelection()
@@ -1949,11 +1967,73 @@ 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 'slave' 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 selectionAsSlave(SequenceGroup seqsel,
+          ColumnSelection colsel, SelectionSource source)
+  {
+    if (!(source instanceof AlignViewportI)) {
+      return false;
+    }
+    final AlignViewportI sourceAv = (AlignViewportI) source;
+    if (sourceAv.getSlave() != av)
+    {
+      return false;
+    }
+
+    /*
+     * Map sequence selection. Note the SequenceGroup holds aligned sequences,
+     * the mappings hold dataset sequences.
+     */
+    AlignedCodonFrame[] codonFrames = sourceAv.getAlignment()
+            .getCodonFrames();
+    SequenceGroup sg = new SequenceGroup();
+    for (SequenceI selected : seqsel.getSequences())
+    {
+      for (AlignedCodonFrame acf : codonFrames)
+      {
+        SequenceI dnaSeq = acf.getDnaForAaSeq(selected);
+        if (dnaSeq != null)
+        {
+          for (SequenceI seq : av.getAlignment().getSequences())
+          {
+            if (seq.getDatasetSequence() == dnaSeq)
+            {
+              sg.addSequence(seq, false);
+              break;
+            }
+          }
+        }
+      }
+    }
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+    // ((AlignmentViewport) slave).firePropertyChange("alignment", null, slave
+    // .getAlignment()
+    // .getSequences());
+
+    /*
+     * Map column selection
+     */
+    // TODO
+
+    AlignmentPanel ap = av.getAlignPanel();
+    PaintRefresher.Refresh(this, av.getSequenceSetId());
+
+    return true;
+  }
 }
diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java
new file mode 100644 (file)
index 0000000..a28c0cf
--- /dev/null
@@ -0,0 +1,49 @@
+package jalview.gui;
+
+import jalview.jbgui.GSplitFrame;
+
+import javax.swing.JComponent;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
+
+public class SplitFrame extends GSplitFrame
+{
+  private static final long serialVersionUID = 1L;
+
+  public SplitFrame(JComponent top, JComponent bottom)
+  {
+    super(top, bottom);
+    init();
+  }
+
+  /**
+   * Initialise this frame.
+   */
+  protected void init()
+  {
+    setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 10);
+
+    /*
+     * Add a listener to tidy up when the frame is closed.
+     */
+    addInternalFrameListener(new InternalFrameAdapter()
+    {
+      @Override
+      public void internalFrameClosed(InternalFrameEvent evt)
+      {
+        if (getTopComponent() instanceof AlignFrame)
+        {
+          ((AlignFrame) getTopComponent())
+                  .closeMenuItem_actionPerformed(true);
+        }
+        if (getBottomComponent() instanceof AlignFrame)
+        {
+          ((AlignFrame) getBottomComponent())
+                  .closeMenuItem_actionPerformed(true);
+        }
+      };
+    });
+
+  }
+
+}
index c5650f5..e700fb3 100755 (executable)
  */
 package jalview.gui;
 
-import java.util.*;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.print.*;
-import javax.swing.*;
-
-import jalview.analysis.*;
-import jalview.datamodel.*;
-import jalview.schemes.*;
-import jalview.util.*;
+import jalview.analysis.Conservation;
+import jalview.analysis.NJTree;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequenceNode;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.ResidueProperties;
+import jalview.schemes.UserColourScheme;
+import jalview.structure.SelectionSource;
+import jalview.util.Format;
+import jalview.util.MappingUtils;
+import jalview.util.MessageManager;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.awt.print.PrinterJob;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.swing.JColorChooser;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.ToolTipManager;
 
 /**
  * DOCUMENT ME!
@@ -39,7 +68,7 @@ import jalview.util.*;
  * @version $Revision$
  */
 public class TreeCanvas extends JPanel implements MouseListener, Runnable,
-        Printable, MouseMotionListener
+        Printable, MouseMotionListener, SelectionSource
 {
   /** DOCUMENT ME!! */
   public static final String PLACEHOLDER = " * ";
@@ -208,7 +237,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
 
       if (node.element() instanceof SequenceI)
       {
-        SequenceI seq = (SequenceI) ((SequenceNode) node).element();
+        SequenceI seq = (SequenceI) node.element();
 
         if (av.getSequenceColour(seq) == Color.white)
         {
@@ -258,14 +287,14 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       Rectangle rect = new Rectangle(xend + 10, ypos - charHeight / 2,
               charWidth, charHeight);
 
-      nameHash.put((SequenceI) node.element(), rect);
+      nameHash.put(node.element(), rect);
 
       // Colour selected leaves differently
       SequenceGroup selected = av.getSelectionGroup();
 
       if ((selected != null)
               && selected.getSequences(null).contains(
-                      (SequenceI) node.element()))
+                      node.element()))
       {
         g.setColor(Color.gray);
 
@@ -290,7 +319,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       int xend = (int) (height * scale) + offx;
       int ypos = (int) (node.ycount * chunk) + offy;
 
-      g.setColor(((SequenceNode) node).color.darker());
+      g.setColor(node.color.darker());
 
       // Draw horizontal line
       g.drawLine(xstart, ypos, xend, ypos);
@@ -493,7 +522,8 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
         {
           for (int a = 0; a < aps.length; a++)
           {
-            aps[a].av.setSequenceColour((SequenceI) node.element(), c);
+            final SequenceI seq = (SequenceI) node.element();
+            aps[a].av.setSequenceColour(seq, c);
           }
         }
       }
@@ -682,7 +712,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
 
     labelLength = fm.stringWidth(longestName) + 20; // 20 allows for scrollbar
 
-    float wscale = (float) (width - labelLength - (offx * 2))
+    float wscale = (width - labelLength - (offx * 2))
             / tree.getMaxHeight();
 
     SequenceNode top = tree.getTopNode();
@@ -708,7 +738,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
         g2.setColor(Color.gray);
       }
 
-      int x = (int) ((threshold * (float) (getWidth() - labelLength - (2 * offx))) + offx);
+      int x = (int) ((threshold * (getWidth() - labelLength - (2 * offx))) + offx);
 
       g2.drawLine(x, 0, x, getHeight());
     }
@@ -854,12 +884,20 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
 
         AlignmentPanel[] aps = getAssociatedPanels();
 
+        // TODO push calls below into a single AlignViewportI method?
+        // see also AlignViewController.deleteGroups
         for (int a = 0; a < aps.length; a++)
         {
           aps[a].av.setSelectionGroup(null);
           aps[a].av.getAlignment().deleteAllGroups();
           aps[a].av.clearSequenceColours();
         }
+        if (av.getSlave() != null)
+        {
+          av.getSlave().setSelectionGroup(null);
+          av.getSlave().getAlignment().deleteAllGroups();
+          av.getSlave().clearSequenceColours();
+        }
         colourGroups();
       }
 
@@ -923,6 +961,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       // sg.recalcConservation();
       sg.setName("JTreeGroup:" + sg.hashCode());
       sg.setIdColour(col);
+
       for (int a = 0; a < aps.length; a++)
       {
         if (aps[a].av.getGlobalColourScheme() != null
@@ -939,7 +978,26 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
 
         aps[a].av.getAlignment().addGroup(new SequenceGroup(sg));
       }
+
+      // TODO can we push all of the below into AlignViewportI?
+      av.getAlignment().addGroup(sg);
+      if (av.getSlave() != null)
+      {
+        SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av,
+                av.getSlave());
+        if (mappedGroup.getSequences().size() > 0)
+        {
+          av.getSlave().getAlignment().addGroup(mappedGroup);
+          for (SequenceI seq : mappedGroup.getSequences())
+          {
+            av.getSlave().setSequenceColour(seq,
+                    mappedGroup.getIdColour().brighter());
+          }
+        }
+      }
+
     }
+
     // notify the panel to redo any group specific stuff.
     for (int a = 0; a < aps.length; a++)
     {
@@ -948,6 +1006,13 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       // to any Jmols listening in
     }
 
+    if (av.getSlave() != null)
+    {
+      ((AlignViewport) av.getSlave()).getAlignPanel().updateAnnotation();
+      /*
+       * idPanel. repaint ()
+       */
+    }
   }
 
   /**
index ee0bfaf..ce28d39 100755 (executable)
  */
 package jalview.gui;
 
-import java.beans.*;
-import java.io.*;
-import java.util.*;
-import java.util.List;
-
-import javax.imageio.*;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import javax.swing.*;
-
-import org.jibble.epsgraphics.*;
-import jalview.analysis.*;
+import jalview.analysis.AlignmentSorter;
+import jalview.analysis.NJTree;
 import jalview.commands.CommandI;
 import jalview.commands.OrderCommand;
-import jalview.datamodel.*;
-import jalview.io.*;
-import jalview.jbgui.*;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.BinaryNode;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.NodeTransformI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequenceNode;
+import jalview.io.JalviewFileChooser;
+import jalview.io.JalviewFileView;
+import jalview.io.NewickFile;
+import jalview.jbgui.GTreePanel;
 import jalview.util.MessageManager;
 
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeEvent;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+import javax.swing.ButtonGroup;
+import javax.swing.JMenuItem;
+import javax.swing.JRadioButtonMenuItem;
+
+import org.jibble.epsgraphics.EpsGraphics2D;
+
 /**
  * DOCUMENT ME!
  * 
@@ -222,7 +238,8 @@ public class TreePanel extends GTreePanel
       associateLeavesMenu.add(item);
     }
 
-    final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem("All Views");
+    final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
+            MessageManager.getString("label.all_views"));
     buttonGroup.add(itemf);
     itemf.setSelected(treeCanvas.applyToAllViews);
     itemf.addActionListener(new ActionListener()
@@ -305,7 +322,7 @@ public class TreePanel extends GTreePanel
       av.setCurrentTree(tree);
       if (av.getSortByTree())
       {
-        sortByTree_actionPerformed(null);
+        sortByTree_actionPerformed();
       }
     }
   }
@@ -517,7 +534,8 @@ public class TreePanel extends GTreePanel
    * 
    * @param e
    */
-  public void sortByTree_actionPerformed(ActionEvent e)
+  @Override
+  public void sortByTree_actionPerformed()
   {
 
     if (treeCanvas.applyToAllViews)
index 4a74c9c..0772781 100644 (file)
@@ -1306,7 +1306,7 @@ public class WsJobParameters extends JPanel implements ItemListener,
    */
   protected void updateWebServiceMenus()
   {
-    for (AlignFrame alignFrame : Desktop.getAlignframes())
+    for (AlignFrame alignFrame : Desktop.getAlignFrames())
     {
       alignFrame.BuildWebServiceMenu();
     }
index 6293a1b..ae3093c 100644 (file)
@@ -127,7 +127,7 @@ public class VamsasAppDatastore
   private void buildSkipList()
   {
     skipList = new Hashtable();
-    AlignFrame[] al = Desktop.getAlignframes();
+    AlignFrame[] al = Desktop.getAlignFrames();
     for (int f = 0; al != null && f < al.length; f++)
     {
       skipList.put(al[f].getViewport().getSequenceSetId(), al[f]);
diff --git a/src/jalview/jbgui/GSplitFrame.java b/src/jalview/jbgui/GSplitFrame.java
new file mode 100644 (file)
index 0000000..281a93e
--- /dev/null
@@ -0,0 +1,43 @@
+package jalview.jbgui;
+
+import javax.swing.JComponent;
+import javax.swing.JInternalFrame;
+import javax.swing.JSplitPane;
+
+public class GSplitFrame extends JInternalFrame
+{
+  private static final long serialVersionUID = 1L;
+
+  private JComponent topComponent;
+
+  private JComponent bottomComponent;
+
+  /**
+   * Constructor
+   * 
+   * @param top
+   * @param bottom
+   */
+  public GSplitFrame(JComponent top, JComponent bottom)
+  {
+    this.topComponent = top;
+    this.bottomComponent = bottom;
+    JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, top,
+            bottom);
+    splitPane.setVisible(true);
+    add(splitPane);
+    splitPane.setDividerLocation(0.5d);
+    splitPane.setResizeWeight(0.5d);
+    splitPane.setDividerSize(0);
+  }
+
+  public JComponent getTopComponent()
+  {
+    return topComponent;
+  }
+
+  public JComponent getBottomComponent()
+  {
+    return bottomComponent;
+  }
+}
index b492c21..83a9866 100755 (executable)
@@ -22,10 +22,19 @@ package jalview.jbgui;
 
 import jalview.util.MessageManager;
 
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.event.*;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JInternalFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JScrollPane;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
 
 public class GTreePanel extends JInternalFrame
 {
@@ -124,7 +133,7 @@ public class GTreePanel extends JInternalFrame
     {
       public void actionPerformed(ActionEvent e)
       {
-        sortByTree_actionPerformed(e);
+        sortByTree_actionPerformed();
       }
     });
     font.setText(MessageManager.getString("action.font"));
@@ -282,7 +291,7 @@ public class GTreePanel extends JInternalFrame
   {
   }
 
-  public void sortByTree_actionPerformed(ActionEvent e)
+  public void sortByTree_actionPerformed()
   {
 
   }
index 66f39fe..e5f3e36 100644 (file)
@@ -20,9 +20,11 @@ public interface CommandListener
    * @param command
    * @param undo
    * @param ssm
+   * @param source
+   *          the originator of the command
    */
   public void mirrorCommand(CommandI command, boolean undo,
-          StructureSelectionManager ssm);
+          StructureSelectionManager ssm, VamsasSource source);
 
   /**
    * Temporary workaround to make check for source == listener work.
index 5a3beb4..945e08d 100644 (file)
@@ -24,28 +24,25 @@ import jalview.analysis.AlignSeq;
 import jalview.api.StructureSelectionManagerProvider;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
-import jalview.commands.EditCommand.Action;
-import jalview.commands.EditCommand.Edit;
+import jalview.commands.OrderCommand;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.io.AppletFormatAdapter;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
-import jalview.util.StringUtils;
 
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
-import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.Vector;
 
@@ -56,7 +53,7 @@ public class StructureSelectionManager
 {
   static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
 
-  StructureMapping[] mappings;
+  private List<StructureMapping> mappings = new ArrayList<StructureMapping>();
 
   private boolean processSecondaryStructure = false;
 
@@ -68,6 +65,8 @@ public class StructureSelectionManager
 
   private List<CommandListener> commandListeners = new ArrayList<CommandListener>();
 
+  private List<SelectionListener> sel_listeners = new ArrayList<SelectionListener>();
+
   /**
    * @return true if will try to use external services for processing secondary
    *         structure
@@ -134,17 +133,18 @@ public class StructureSelectionManager
    */
   public void reportMapping()
   {
-    if (mappings == null)
+    if (mappings.isEmpty())
     {
       System.err.println("reportMapping: No PDB/Sequence mappings.");
     }
     else
     {
-      System.err.println("reportMapping: There are " + mappings.length
+      System.err.println("reportMapping: There are " + mappings.size()
               + " mappings.");
-      for (int m = 0; m < mappings.length; m++)
+      int i = 0;
+      for (StructureMapping sm : mappings)
       {
-        System.err.println("mapping " + m + " : " + mappings[m].pdbfile);
+        System.err.println("mapping " + i++ + " : " + sm.pdbfile);
       }
     }
   }
@@ -265,16 +265,19 @@ public class StructureSelectionManager
     }
   }
 
+  /**
+   * Returns the file name for a mapped PDB id (or null if not mapped).
+   * 
+   * @param pdbid
+   * @return
+   */
   public String alreadyMappedToFile(String pdbid)
   {
-    if (mappings != null)
+    for (StructureMapping sm : mappings)
     {
-      for (int i = 0; i < mappings.length; i++)
+      if (sm.getPdbId().equals(pdbid))
       {
-        if (mappings[i].getPdbId().equals(pdbid))
-        {
-          return mappings[i].pdbfile;
-        }
+        return sm.pdbfile;
       }
     }
     return null;
@@ -501,19 +504,7 @@ public class StructureSelectionManager
               mappingDetails.toString());
       if (forStructureView)
       {
-
-        if (mappings == null)
-        {
-          mappings = new StructureMapping[1];
-        }
-        else
-        {
-          StructureMapping[] tmp = new StructureMapping[mappings.length + 1];
-          System.arraycopy(mappings, 0, tmp, 0, mappings.length);
-          mappings = tmp;
-        }
-
-        mappings[mappings.length - 1] = newMapping;
+        mappings.add(newMapping);
       }
       maxChain.transferResidueAnnotation(newMapping, sqmpping);
     }
@@ -565,19 +556,18 @@ public class StructureSelectionManager
       }
     }
 
-    if (pdbs.size() > 0 && mappings != null)
+    if (pdbs.size() > 0)
     {
-      Vector tmp = new Vector();
-      for (int i = 0; i < mappings.length; i++)
+      List<StructureMapping> tmp = new ArrayList<StructureMapping>();
+      for (StructureMapping sm : mappings)
       {
-        if (!pdbs.contains(mappings[i].pdbfile))
+        if (!pdbs.contains(sm.pdbfile))
         {
-          tmp.addElement(mappings[i]);
+          tmp.add(sm);
         }
       }
 
-      mappings = new StructureMapping[tmp.size()];
-      tmp.copyInto(mappings);
+      mappings = tmp;
     }
   }
 
@@ -588,7 +578,6 @@ public class StructureSelectionManager
       // old or prematurely sent event
       return;
     }
-    boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
     SearchResults results = null;
     SequenceI lastseq = null;
     int lastipos = -1, indexpos;
@@ -600,28 +589,20 @@ public class StructureSelectionManager
         {
           results = new SearchResults();
         }
-        if (mappings != null)
+        for (StructureMapping sm : mappings)
         {
-          for (int j = 0; j < mappings.length; j++)
+          if (sm.pdbfile.equals(pdbfile) && sm.pdbchain.equals(chain))
           {
-            if (mappings[j].pdbfile.equals(pdbfile)
-                    && mappings[j].pdbchain.equals(chain))
+            indexpos = sm.getSeqPos(pdbResNum);
+            if (lastipos != indexpos && lastseq != sm.sequence)
             {
-              indexpos = mappings[j].getSeqPos(pdbResNum);
-              if (lastipos != indexpos && lastseq != mappings[j].sequence)
+              results.addResult(sm.sequence, indexpos, indexpos);
+              lastipos = indexpos;
+              lastseq = sm.sequence;
+              // construct highlighted sequence list
+              for (AlignedCodonFrame acf : seqmappings)
               {
-                results.addResult(mappings[j].sequence, indexpos, indexpos);
-                lastipos = indexpos;
-                lastseq = mappings[j].sequence;
-                // construct highlighted sequence list
-                if (seqmappings != null)
-                {
-                  for (AlignedCodonFrame acf : seqmappings)
-                  {
-                    acf.markMappedRegion(mappings[j].sequence, indexpos,
-                            results);
-                  }
-                }
+                acf.markMappedRegion(sm.sequence, indexpos, results);
               }
             }
           }
@@ -655,7 +636,8 @@ public class StructureSelectionManager
   public void mouseOverSequence(SequenceI seq, int indexpos, int index,
           VamsasSource source)
   {
-    boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
+    boolean hasSequenceListeners = handlingVamsasMo
+            || !seqmappings.isEmpty();
     SearchResults results = null;
     if (index == -1)
     {
@@ -686,7 +668,13 @@ public class StructureSelectionManager
             {
               if (results == null)
               {
-                results = buildSearchResults(seq, index);
+                results = MappingUtils.buildSearchResults(seq, index,
+                        seqmappings);
+              }
+              if (handlingVamsasMo)
+              {
+                results.addResult(seq, index, index);
+
               }
               seqListener.highlightSequence(results);
             }
@@ -707,39 +695,6 @@ public class StructureSelectionManager
   }
 
   /**
-   * Returns a SearchResults object describing the mapped region corresponding
-   * to the specified sequence position.
-   * 
-   * @param seq
-   * @param index
-   * @return
-   */
-  protected SearchResults buildSearchResults(SequenceI seq, int index)
-  {
-    SearchResults results;
-    results = new SearchResults();
-    if (index >= seq.getStart() && index <= seq.getEnd())
-    {
-      if (seqmappings != null)
-      {
-        for (AlignedCodonFrame acf : seqmappings)
-        {
-          acf.markMappedRegion(seq, index, results);
-        }
-      }
-      // hasSequenceListeners = results.getSize() > 0;
-      if (handlingVamsasMo)
-      {
-        // maybe have to resolve seq to a dataset sequence...
-        // add in additional direct sequence and/or dataset sequence
-        // highlighting
-        results.addResult(seq, index, index);
-      }
-    }
-    return results;
-  }
-
-  /**
    * Send suitable messages to a StructureListener to highlight atoms
    * corresponding to the given sequence position.
    * 
@@ -751,26 +706,21 @@ public class StructureSelectionManager
           int index)
   {
     int atomNo;
-    if (mappings != null)
+    List<AtomSpec> atoms = new ArrayList<AtomSpec>();
+    for (StructureMapping sm : mappings)
     {
-      List<AtomSpec> atoms = new ArrayList<AtomSpec>();
-      for (int j = 0; j < mappings.length; j++)
+      if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence())
       {
-        if (mappings[j].sequence == seq
-                || mappings[j].sequence == seq.getDatasetSequence())
-        {
-          atomNo = mappings[j].getAtomNum(index);
+        atomNo = sm.getAtomNum(index);
 
-          if (atomNo > 0)
-          {
-            atoms.add(new AtomSpec(mappings[j].pdbfile,
-                    mappings[j].pdbchain, mappings[j].getPDBResNum(index),
-                    atomNo));
-          }
+        if (atomNo > 0)
+        {
+          atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, sm
+                  .getPDBResNum(index), atomNo));
         }
       }
-      sl.highlightAtoms(atoms);
     }
+    sl.highlightAtoms(atoms);
   }
 
   /**
@@ -859,34 +809,25 @@ public class StructureSelectionManager
 
   public StructureMapping[] getMapping(String pdbfile)
   {
-    Vector tmp = new Vector();
-    if (mappings != null)
-    {
-      for (int i = 0; i < mappings.length; i++)
+    List<StructureMapping> tmp = new ArrayList<StructureMapping>();
+    for (StructureMapping sm : mappings)
       {
-        if (mappings[i].pdbfile.equals(pdbfile))
+      if (sm.pdbfile.equals(pdbfile))
         {
-          tmp.addElement(mappings[i]);
+        tmp.add(sm);
         }
-      }
-    }
-    StructureMapping[] ret = new StructureMapping[tmp.size()];
-    for (int i = 0; i < tmp.size(); i++)
-    {
-      ret[i] = (StructureMapping) tmp.elementAt(i);
     }
-
-    return ret;
+    return tmp.toArray(new StructureMapping[tmp.size()]);
   }
 
   public String printMapping(String pdbfile)
   {
-    StringBuffer sb = new StringBuffer();
-    for (int i = 0; i < mappings.length; i++)
+    StringBuilder sb = new StringBuilder(64);
+    for (StructureMapping sm : mappings)
     {
-      if (mappings[i].pdbfile.equals(pdbfile))
+      if (sm.pdbfile.equals(pdbfile))
       {
-        sb.append(mappings[i].mappingDetails);
+        sb.append(sm.mappingDetails);
       }
     }
 
@@ -926,13 +867,11 @@ public class StructureSelectionManager
     }
   }
 
-  Vector<SelectionListener> sel_listeners = new Vector<SelectionListener>();
-
   public void addSelectionListener(SelectionListener selecter)
   {
     if (!sel_listeners.contains(selecter))
     {
-      sel_listeners.addElement(selecter);
+      sel_listeners.add(selecter);
     }
   }
 
@@ -940,7 +879,7 @@ public class StructureSelectionManager
   {
     if (sel_listeners.contains(toremove))
     {
-      sel_listeners.removeElement(toremove);
+      sel_listeners.remove(toremove);
     }
   }
 
@@ -948,18 +887,11 @@ public class StructureSelectionManager
           jalview.datamodel.SequenceGroup selection,
           jalview.datamodel.ColumnSelection colsel, SelectionSource source)
   {
-    if (sel_listeners != null && sel_listeners.size() > 0)
+    for (SelectionListener slis : sel_listeners)
     {
-      Enumeration listeners = sel_listeners.elements();
-      while (listeners.hasMoreElements())
+      if (slis != source)
       {
-        SelectionListener slis = ((SelectionListener) listeners
-                .nextElement());
-        if (slis != source)
-        {
-          slis.selection(selection, colsel, source);
-        }
-        ;
+        slis.selection(selection, colsel, source);
       }
     }
   }
@@ -1056,143 +988,36 @@ public class StructureSelectionManager
   {
     for (CommandListener listener : commandListeners)
     {
-      if (listener.getVamsasSource() != source)
-      {
-        listener.mirrorCommand(command, undo, this);
-      }
+      listener.mirrorCommand(command, undo, this, source);
     }
   }
 
   /**
-   * Returns a new EditCommand representing the given command as mapped to the
-   * given sequences. If there is no mapping, returns an empty EditCommand.
+   * Returns a new CommandI representing the given command as mapped to the
+   * given sequences. If no mapping could be made, or the command is not of a
+   * mappable kind, returns null.
    * 
    * @param command
    * @param undo
-   * @param targetSeqs
+   * @param alignmentI
    * @param gapChar
    * @return
    */
-  public EditCommand mapEditCommand(EditCommand command, boolean undo,
-          final List<SequenceI> targetSeqs, char gapChar)
+  public CommandI mapCommand(CommandI command,
+          boolean undo,
+          final AlignmentI alignmentI, char gapChar)
   {
-    /*
-     * Cache a copy of the target sequences so we can mimic successive edits on
-     * them. This lets us compute mappings for all edits in the set.
-     */
-    Map<SequenceI, SequenceI> targetCopies = new HashMap<SequenceI, SequenceI>();
-    for (SequenceI seq : targetSeqs)
+    if (command instanceof EditCommand)
     {
-      SequenceI ds = seq.getDatasetSequence();
-      if (ds != null)
-      {
-        final Sequence copy = new Sequence("", new String(seq.getSequence()));
-        copy.setDatasetSequence(ds);
-        targetCopies.put(ds, copy);
-      }
+      return MappingUtils.mapEditCommand((EditCommand) command, undo,
+              alignmentI, gapChar,
+            seqmappings);
     }
-
-    /*
-     * Compute 'source' sequences as they were before applying edits:
-     */
-    Map<SequenceI, SequenceI> originalSequences = command.priorState(undo);
-
-    EditCommand result = new EditCommand();
-    Iterator<Edit> edits = command.getEditIterator(!undo);
-    while (edits.hasNext())
+    else if (command instanceof OrderCommand)
     {
-      Edit edit = edits.next();
-      Action action = edit.getAction();
-
-      /*
-       * Invert sense of action if an Undo.
-       */
-      if (undo)
-      {
-        action = action == Action.INSERT_GAP ? Action.DELETE_GAP
-                : (action == Action.DELETE_GAP ? Action.INSERT_GAP : action);
-      }
-      final int count = edit.getNumber();
-      final int editPos = edit.getPosition();
-      for (SequenceI seq : edit.getSequences())
-      {
-        /*
-         * Get residue position at (or to right of) edit location. Note we use
-         * our 'copy' of the sequence before editing for this.
-         */
-        SequenceI ds = seq.getDatasetSequence();
-        if (ds == null)
-        {
-          continue;
-        }
-        final SequenceI actedOn = originalSequences.get(ds);
-        final int seqpos = actedOn.findPosition(editPos);
-
-        /*
-         * Determine all mappings from this position to mapped sequences.
-         */
-        SearchResults sr = buildSearchResults(seq, seqpos);
-
-        if (!sr.isEmpty())
-        {
-          for (SequenceI targetSeq : targetSeqs)
-          {
-            ds = targetSeq.getDatasetSequence();
-            if (ds == null)
-            {
-              continue;
-            }
-            SequenceI copyTarget = targetCopies.get(ds);
-            final int[] match = sr.getResults(copyTarget, 0,
-                    copyTarget.getLength());
-            if (match != null)
-            {
-              final int ratio = 3; // TODO: compute this - how?
-              final int mappedCount = count * ratio;
-
-              /*
-               * Shift Delete start position left, as it acts on positions to
-               * its right.
-               */
-              int mappedEditPos = action == Action.DELETE_GAP ? match[0]
-                      - mappedCount : match[0];
-              Edit e = new EditCommand().new Edit(action, new SequenceI[]
-              { targetSeq }, mappedEditPos, mappedCount, gapChar);
-              result.addEdit(e);
-
-              /*
-               * and 'apply' the edit to our copy of its target sequence
-               */
-              if (action == Action.INSERT_GAP)
-              {
-                copyTarget.setSequence(new String(StringUtils.insertCharAt(
-                        copyTarget.getSequence(), mappedEditPos,
-                        mappedCount,
-                        gapChar)));
-              }
-              else if (action == Action.DELETE_GAP)
-              {
-                copyTarget.setSequence(new String(StringUtils.deleteChars(
-                        copyTarget.getSequence(), mappedEditPos,
-                        mappedEditPos + mappedCount)));
-              }
-            }
-          }
-        }
-        /*
-         * and 'apply' the edit to our copy of its source sequence
-         */
-        if (action == Action.INSERT_GAP) {
-          actedOn.setSequence(new String(StringUtils.insertCharAt(
-                  actedOn.getSequence(), editPos, count, gapChar)));
-        }
-        else if (action == Action.DELETE_GAP)
-        {
-          actedOn.setSequence(new String(StringUtils.deleteChars(
-                  actedOn.getSequence(), editPos, editPos + count)));
-        }
-      }
+      return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
+              alignmentI, seqmappings);
     }
-    return result;
+    return null;
   }
 }
diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java
new file mode 100644 (file)
index 0000000..466aeb8
--- /dev/null
@@ -0,0 +1,381 @@
+package jalview.util;
+
+import jalview.analysis.AlignmentSorter;
+import jalview.api.AlignViewportI;
+import jalview.commands.CommandI;
+import jalview.commands.EditCommand;
+import jalview.commands.EditCommand.Action;
+import jalview.commands.EditCommand.Edit;
+import jalview.commands.OrderCommand;
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper methods for manipulations involving sequence mappings.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public final class MappingUtils
+{
+
+  /**
+   * Helper method to map a CUT or PASTE command.
+   * 
+   * @param edit
+   *          the original command
+   * @param undo
+   *          if true, the command is to be undone
+   * @param targetSeqs
+   *          the mapped sequences to apply the mapped command to
+   * @param result
+   *          the mapped EditCommand to add to
+   * @param mappings
+   */
+  protected static void mapCutOrPaste(Edit edit, boolean undo,
+          List<SequenceI> targetSeqs, EditCommand result,
+          Set<AlignedCodonFrame> mappings)
+  {
+    Action action = edit.getAction();
+    if (undo)
+    {
+      action = action.getUndoAction();
+    }
+    // TODO write this
+    System.err.println("MappingUtils.mapCutOrPaste not yet implemented");
+  }
+
+  /**
+   * Returns a new EditCommand representing the given command as mapped to the
+   * given sequences. If there is no mapping, returns null.
+   * 
+   * @param command
+   * @param undo
+   * @param alignment
+   * @param gapChar
+   * @param mappings
+   * @return
+   */
+  public static EditCommand mapEditCommand(EditCommand command,
+          boolean undo, final AlignmentI alignment, char gapChar,
+          Set<AlignedCodonFrame> mappings)
+  {
+    /*
+     * Cache a copy of the target sequences so we can mimic successive edits on
+     * them. This lets us compute mappings for all edits in the set.
+     */
+    Map<SequenceI, SequenceI> targetCopies = new HashMap<SequenceI, SequenceI>();
+    for (SequenceI seq : alignment.getSequences())
+    {
+      SequenceI ds = seq.getDatasetSequence();
+      if (ds != null)
+      {
+        final SequenceI copy = new Sequence("", new String(
+                seq.getSequence()));
+        copy.setDatasetSequence(ds);
+        targetCopies.put(ds, copy);
+      }
+    }
+  
+    /*
+     * Compute 'source' sequences as they were before applying edits:
+     */
+    Map<SequenceI, SequenceI> originalSequences = command.priorState(undo);
+  
+    EditCommand result = new EditCommand();
+    Iterator<Edit> edits = command.getEditIterator(!undo);
+    while (edits.hasNext())
+    {
+      Edit edit = edits.next();
+      if (edit.getAction() == Action.CUT
+              || edit.getAction() == Action.PASTE)
+      {
+        mapCutOrPaste(edit, undo, alignment.getSequences(), result,
+                mappings);
+      }
+      else if (edit.getAction() == Action.INSERT_GAP
+              || edit.getAction() == Action.DELETE_GAP)
+      {
+        mapInsertOrDelete(edit, undo, originalSequences,
+                alignment.getSequences(),
+                targetCopies, gapChar, result, mappings);
+      }
+    }
+    return result.getSize() > 0 ? result : null;
+  }
+
+  /**
+   * Helper method to map an edit command to insert or delete gaps.
+   * 
+   * @param edit
+   *          the original command
+   * @param undo
+   *          if true, the action is to undo the command
+   * @param originalSequences
+   *          the sequences the command acted on
+   * @param targetSeqs
+   * @param targetCopies
+   * @param gapChar
+   * @param result
+   *          the new EditCommand to add mapped commands to
+   * @param mappings
+   */
+  protected static void mapInsertOrDelete(Edit edit, boolean undo,
+          Map<SequenceI, SequenceI> originalSequences,
+          final List<SequenceI> targetSeqs,
+          Map<SequenceI, SequenceI> targetCopies, char gapChar,
+          EditCommand result, Set<AlignedCodonFrame> mappings)
+  {
+    Action action = edit.getAction();
+  
+    /*
+     * Invert sense of action if an Undo.
+     */
+    if (undo)
+    {
+      action = action.getUndoAction();
+    }
+    final int count = edit.getNumber();
+    final int editPos = edit.getPosition();
+    for (SequenceI seq : edit.getSequences())
+    {
+      /*
+       * Get residue position at (or to right of) edit location. Note we use our
+       * 'copy' of the sequence before editing for this.
+       */
+      SequenceI ds = seq.getDatasetSequence();
+      if (ds == null)
+      {
+        continue;
+      }
+      final SequenceI actedOn = originalSequences.get(ds);
+      final int seqpos = actedOn.findPosition(editPos);
+  
+      /*
+       * Determine all mappings from this position to mapped sequences.
+       */
+      SearchResults sr = buildSearchResults(seq, seqpos, mappings);
+  
+      if (!sr.isEmpty())
+      {
+        for (SequenceI targetSeq : targetSeqs)
+        {
+          ds = targetSeq.getDatasetSequence();
+          if (ds == null)
+          {
+            continue;
+          }
+          SequenceI copyTarget = targetCopies.get(ds);
+          final int[] match = sr.getResults(copyTarget, 0,
+                  copyTarget.getLength());
+          if (match != null)
+          {
+            final int ratio = 3; // TODO: compute this - how?
+            final int mappedCount = count * ratio;
+  
+            /*
+             * Shift Delete start position left, as it acts on positions to its
+             * right.
+             */
+            int mappedEditPos = action == Action.DELETE_GAP ? match[0]
+                    - mappedCount : match[0];
+            Edit e = result.new Edit(action, new SequenceI[]
+            { targetSeq }, mappedEditPos, mappedCount, gapChar);
+            result.addEdit(e);
+  
+            /*
+             * and 'apply' the edit to our copy of its target sequence
+             */
+            if (action == Action.INSERT_GAP)
+            {
+              copyTarget.setSequence(new String(StringUtils.insertCharAt(
+                      copyTarget.getSequence(), mappedEditPos, mappedCount,
+                      gapChar)));
+            }
+            else if (action == Action.DELETE_GAP)
+            {
+              copyTarget.setSequence(new String(StringUtils.deleteChars(
+                      copyTarget.getSequence(), mappedEditPos,
+                      mappedEditPos + mappedCount)));
+            }
+          }
+        }
+      }
+      /*
+       * and 'apply' the edit to our copy of its source sequence
+       */
+      if (action == Action.INSERT_GAP)
+      {
+        actedOn.setSequence(new String(StringUtils.insertCharAt(
+                actedOn.getSequence(), editPos, count, gapChar)));
+      }
+      else if (action == Action.DELETE_GAP)
+      {
+        actedOn.setSequence(new String(StringUtils.deleteChars(
+                actedOn.getSequence(), editPos, editPos + count)));
+      }
+    }
+  }
+
+  /**
+   * Returns a SearchResults object describing the mapped region corresponding
+   * to the specified sequence position.
+   * 
+   * @param seq
+   * @param index
+   * @param seqmappings
+   * @return
+   */
+  public static SearchResults buildSearchResults(SequenceI seq, int index,
+          Set<AlignedCodonFrame> seqmappings)
+  {
+    SearchResults results;
+    results = new SearchResults();
+    if (index >= seq.getStart() && index <= seq.getEnd())
+    {
+      for (AlignedCodonFrame acf : seqmappings)
+      {
+        acf.markMappedRegion(seq, index, results);
+      }
+      results.addResult(seq, index, index);
+    }
+    return results;
+  }
+
+  /**
+   * Returns a (possibly empty) SequenceGroup containing any sequences the
+   * mapped viewport corresponding to the given group in the source viewport.
+   * 
+   * @param sg
+   * @param av
+   * @param mapped
+   * @return
+   */
+  public static SequenceGroup mapSequenceGroup(SequenceGroup sg,
+          AlignViewportI av, AlignViewportI mapped)
+  {
+    /*
+     * Map sequence selection. Note the SequenceGroup holds aligned sequences,
+     * the mappings hold dataset sequences.
+     */
+    AlignedCodonFrame[] codonFrames = av.getAlignment()
+            .getCodonFrames();
+
+    /*
+     * Copy group name, colours, but not sequences
+     */
+    SequenceGroup mappedGroup = new SequenceGroup(sg);
+    mappedGroup.clear();
+    // TODO set width of mapped group
+
+    for (SequenceI selected : sg.getSequences())
+    {
+      for (AlignedCodonFrame acf : codonFrames)
+      {
+        SequenceI dnaSeq = acf.getDnaForAaSeq(selected);
+        if (dnaSeq != null)
+        {
+          for (SequenceI seq : mapped.getAlignment().getSequences())
+          {
+            if (seq.getDatasetSequence() == dnaSeq)
+            {
+              mappedGroup.addSequence(seq, false);
+              break;
+            }
+          }
+        }
+      }
+    }
+    return mappedGroup;
+  }
+
+  /**
+   * Returns an OrderCommand equivalent to the given one, but acting on mapped
+   * sequences as described by the mappings, or null if no mapping can be made.
+   * 
+   * @param command
+   *          the original order command
+   * @param undo
+   *          if true, the action is to undo the sort
+   * @param mapTo
+   *          the alignment we are mapping to
+   * @param mappings
+   *          the mappings available
+   * @return
+   */
+  public static CommandI mapOrderCommand(OrderCommand command,
+          boolean undo, AlignmentI mapTo,
+          Set<AlignedCodonFrame> mappings)
+  {
+    SequenceI[] sortOrder = command.getSequenceOrder(undo);
+    List<SequenceI> mappedOrder = new ArrayList<SequenceI>();
+    int j = 0;
+    for (SequenceI seq : sortOrder)
+    {
+      for (AlignedCodonFrame acf : mappings)
+      {
+        SequenceI dnaSeq = acf.getDnaForAaSeq(seq);
+        if (dnaSeq != null)
+        {
+          for (SequenceI seq2 : mapTo.getSequences())
+          {
+            if (seq2.getDatasetSequence() == dnaSeq)
+            {
+              mappedOrder.add(seq2);
+              j++;
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    /*
+     * Return null if no mappings made.
+     */
+    if (j == 0)
+    {
+      return null;
+    }
+
+    /*
+     * Add any unmapped sequences on the end of the sort in their original
+     * ordering.
+     */
+    if (j < mapTo.getHeight())
+    {
+      for (SequenceI seq : mapTo.getSequences())
+      {
+        if (!mappedOrder.contains(seq))
+        {
+          mappedOrder.add(seq);
+        }
+      }
+    }
+
+    /*
+     * Have to align the sequences before constructing the OrderCommand - which
+     * then realigns them?!?
+     */
+    final SequenceI[] mappedOrderArray = mappedOrder
+            .toArray(new SequenceI[mappedOrder.size()]);
+    SequenceI[] oldOrder = mapTo.getSequencesArray();
+    AlignmentSorter.sortBy(mapTo, new AlignmentOrder(mappedOrderArray));
+    final OrderCommand result = new OrderCommand(command.getDescription(),
+            oldOrder, mapTo);
+    return result;
+  }
+
+}
index 1b42faf..ed63e0f 100644 (file)
@@ -44,6 +44,7 @@ import jalview.workers.StrucConsensusThread;
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -58,6 +59,11 @@ import java.util.Vector;
  */
 public abstract class AlignmentViewport implements AlignViewportI
 {
+  /*
+   * A viewport that is a slave of (driven by) this one in some sense.
+   */
+  AlignViewportI slave = null;
+
   /**
    * alignment displayed in the viewport. Please use get/setter
    */
@@ -803,7 +809,7 @@ public abstract class AlignmentViewport implements AlignViewportI
 
   protected boolean showConsensus = true;
 
-  Hashtable sequenceColours;
+  private Map<SequenceI, Color> sequenceColours = new HashMap<SequenceI, Color>();
 
   /**
    * Property change listener for changes in alignment
@@ -1605,26 +1611,13 @@ public abstract class AlignmentViewport implements AlignViewportI
   @Override
   public Color getSequenceColour(SequenceI seq)
   {
-    Color sqc = Color.white;
-    if (sequenceColours != null)
-    {
-      sqc = (Color) sequenceColours.get(seq);
-      if (sqc == null)
-      {
-        sqc = Color.white;
-      }
-    }
-    return sqc;
+    Color sqc = sequenceColours.get(seq);
+    return (sqc == null ? Color.white : sqc);
   }
 
   @Override
   public void setSequenceColour(SequenceI seq, Color col)
   {
-    if (sequenceColours == null)
-    {
-      sequenceColours = new Hashtable();
-    }
-
     if (col == null)
     {
       sequenceColours.remove(seq);
@@ -1638,10 +1631,6 @@ public abstract class AlignmentViewport implements AlignViewportI
   @Override
   public void updateSequenceIdColours()
   {
-    if (sequenceColours == null)
-    {
-      sequenceColours = new Hashtable();
-    }
     for (SequenceGroup sg : alignment.getGroups())
     {
       if (sg.idColour != null)
@@ -1657,6 +1646,25 @@ public abstract class AlignmentViewport implements AlignViewportI
   @Override
   public void clearSequenceColours()
   {
-    sequenceColours = null;
+    sequenceColours.clear();
   };
+
+  @Override
+  public AlignViewportI getSlave()
+  {
+    return this.slave;
+  }
+
+  @Override
+  public void setSlave(AlignViewportI sl)
+  {
+    if (this == sl.getSlave())
+    {
+      System.err.println("Ignoring recursive setSlave request");
+    }
+    else
+    {
+      this.slave = sl;
+    }
+  }
 }
index c436818..66f99f3 100644 (file)
@@ -324,62 +324,44 @@ public class AlignmentUtilsTests
   @Test
   public void testAlignSequenceAs_withMapping_noIntrons()
   {
-    /*
-     * Simple case: no gaps in dna
-     */
-    SequenceI dna = new Sequence("Seq1", "GGGAAA");
-    dna.createDatasetSequence();
-    SequenceI protein = new Sequence("Seq1", "-A-L-");
-    protein.createDatasetSequence();
-    AlignedCodonFrame acf = new AlignedCodonFrame();
     MapList map = new MapList(new int[]
     { 1, 6 }, new int[]
     { 1, 2 }, 3, 1);
-    acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
 
     /*
      * No existing gaps in dna:
      */
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false,
-            false);
-    assertEquals("---GGG---AAA", dna.getSequenceAsString());
+    checkAlignSequenceAs("GGGAAA", "-A-L-", false, false, map,
+            "---GGG---AAA");
 
     /*
      * Now introduce gaps in dna but ignore them when realigning.
      */
-    dna.setSequence("-G-G-G-A-A-A-");
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false,
-            false);
-    assertEquals("---GGG---AAA", dna.getSequenceAsString());
+    checkAlignSequenceAs("-G-G-G-A-A-A-", "-A-L-", false, false, map,
+            "---GGG---AAA");
 
     /*
      * Now include gaps in dna when realigning. First retaining 'mapped' gaps
      * only, i.e. those within the exon region.
      */
-    dna.setSequence("-G-G--G-A--A-A-");
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', true,
-            false);
-    assertEquals("---G-G--G---A--A-A", dna.getSequenceAsString());
+    checkAlignSequenceAs("-G-G--G-A--A-A-", "-A-L-", true, false, map,
+            "---G-G--G---A--A-A");
 
     /*
      * Include all gaps in dna when realigning (within and without the exon
      * region). The leading gap, and the gaps between codons, are subsumed by
      * the protein alignment gap.
      */
-    dna.setSequence("-G-GG--AA-A-");
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', true,
-            true);
-    assertEquals("---G-GG---AA-A-", dna.getSequenceAsString());
+    checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", true, true, map,
+            "---G-GG---AA-A-");
 
     /*
      * Include only unmapped gaps in dna when realigning (outside the exon
      * region). The leading gap, and the gaps between codons, are subsumed by
      * the protein alignment gap.
      */
-    dna.setSequence("-G-GG--AA-A-");
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false,
-            true);
-    assertEquals("---GGG---AAA-", dna.getSequenceAsString());
+    checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", false, true, map,
+            "---GGG---AAA-");
   }
 
   /**
@@ -389,62 +371,47 @@ public class AlignmentUtilsTests
   public void testAlignSequenceAs_withMapping_withIntrons()
   {
     /*
-     * Simple case: no gaps in dna
-     */
-    SequenceI dna = new Sequence("Seq1", "GGGAAACCCTTTGGG");
-    dna.createDatasetSequence();
-    SequenceI protein = new Sequence("Seq1", "-A-L-");
-    protein.createDatasetSequence();
-    AlignedCodonFrame acf = new AlignedCodonFrame();
-
-    /*
      * Exons at codon 2 (AAA) and 4 (TTT)
      */
     MapList map = new MapList(new int[]
     { 4, 6, 10, 12 }, new int[]
     { 1, 2 }, 3, 1);
-    acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
 
     /*
-     * Align dna as "-A-L-". The protein 'gaps' follow the introns, i.e are
-     * placed immediately before the mapped codons.
+     * Simple case: no gaps in dna
      */
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false,
-            false);
-    assertEquals("GGG---AAACCC---TTTGGG", dna.getSequenceAsString());
+    checkAlignSequenceAs("GGGAAACCCTTTGGG", "--A-L-", false, false, map,
+            "GGG---AAACCCTTTGGG");
 
     /*
      * Add gaps to dna - but ignore when realigning.
      */
-    dna.setSequence("-G-G-G--A--A---AC-CC-T-TT-GG-G-");
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false,
-            false);
-    assertEquals("GGG---AAACCC---TTTGGG", dna.getSequenceAsString());
+    checkAlignSequenceAs("-G-G-G--A--A---AC-CC-T-TT-GG-G-", "--A-L-",
+            false, false, map, "GGG---AAACCCTTTGGG");
 
     /*
      * Add gaps to dna - include within exons only when realigning.
      */
-    dna.setSequence("-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', true,
-            false);
-    assertEquals("GGG---A--A---ACCC---T-TTGGG", dna.getSequenceAsString());
+    checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
+            true, false, map, "GGG---A--A---ACCCT-TTGGG");
 
     /*
      * Include gaps outside exons only when realigning.
      */
-    dna.setSequence("-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false,
-            true);
-    assertEquals("-G-G-G---AAA-C-CC---TTT-GG-G-", dna.getSequenceAsString());
+    checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
+            false, true, map, "-G-G-GAAAC-CCTTT-GG-G-");
+
+    /*
+     * Include gaps following first intron if we are 'preserving mapped gaps'
+     */
+    checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
+            true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
 
     /*
      * Include all gaps in dna when realigning.
      */
-    dna.setSequence("-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', true,
-            true);
-    assertEquals("-G-G-G---A--A---A-C-CC---T-TT-GG-G-",
-            dna.getSequenceAsString());
+    checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-",
+            true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-");
   }
 
   /**
@@ -453,26 +420,65 @@ public class AlignmentUtilsTests
   @Test
   public void testAlignSequenceAs_withMapping_withUnmappedProtein()
   {
-    SequenceI dna = new Sequence("Seq1", "GGGAAACCCTTTGGG");
-    dna.createDatasetSequence();
-    SequenceI protein = new Sequence("Seq1", "-A-L-P-");
-    protein.createDatasetSequence();
-    AlignedCodonFrame acf = new AlignedCodonFrame();
-
+    
     /*
      * Exons at codon 2 (AAA) and 4 (TTT) mapped to A and P
      */
-    MapList map = new MapList(new int[]
+    final MapList map = new MapList(new int[]
     { 4, 6, 10, 12 }, new int[]
     { 1, 1, 3, 3 }, 3, 1);
+    
+
+    /*
+     * Expect alignment does nothing (aborts realignment). Change this test
+     * first if different behaviour wanted.
+     */
+    checkAlignSequenceAs("GGGAAACCCTTTGGG", "-A-L-P-", false,
+            false, map, "GGGAAACCCTTTGGG");
+  }
+
+  /**
+   * Helper method that performs and verifies the method under test.
+   * 
+   * @param dnaSeq
+   * @param proteinSeq
+   * @param preserveMappedGaps
+   * @param preserveUnmappedGaps
+   * @param map
+   * @param expected
+   */
+  protected void checkAlignSequenceAs(final String dnaSeq,
+          final String proteinSeq, final boolean preserveMappedGaps,
+          final boolean preserveUnmappedGaps, MapList map,
+          final String expected)
+  {
+    SequenceI dna = new Sequence("Seq1", dnaSeq);
+    dna.createDatasetSequence();
+    SequenceI protein = new Sequence("Seq1", proteinSeq);
+    protein.createDatasetSequence();
+    AlignedCodonFrame acf = new AlignedCodonFrame();
     acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
 
+    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-',
+            preserveMappedGaps, preserveUnmappedGaps);
+    assertEquals(expected, dna.getSequenceAsString());
+  }
+
+  /**
+   * Test for the alignSequenceAs method where we preserve gaps in introns only.
+   */
+  @Test
+  public void testAlignSequenceAs_keepIntronGapsOnly()
+  {
+
     /*
-     * Align dna as "-A-L-P-". Currently, does nothing (aborts realignment).
-     * Change this test first if different behaviour wanted.
+     * Intron GGGAAA followed by exon CCCTTT
      */
-    AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false,
-            false);
-    assertEquals("GGGAAACCCTTTGGG", dna.getSequenceAsString());
+    MapList map = new MapList(new int[]
+    { 7, 12 }, new int[]
+    { 1, 2 }, 3, 1);
+    
+    checkAlignSequenceAs("GG-G-AA-A-C-CC-T-TT", "AL",
+            false, true, map, "GG-G-AA-ACCCTTT");
   }
 }
index 84671ae..6ea05e6 100644 (file)
@@ -405,7 +405,7 @@ public class EditCommandTest
    * </ul>
    */
   @Test
-  public void testUnwindCommand_multipleInserts()
+  public void testPriorState_multipleInserts()
   {
     EditCommand command = new EditCommand();
     SequenceI seq = new Sequence("", "--A--B-CDEF");
@@ -433,7 +433,7 @@ public class EditCommandTest
    * </ul>
    */
   @Test
-  public void testUnwindCommand_removeAllGaps()
+  public void testPriorState_removeAllGaps()
   {
     EditCommand command = new EditCommand();
     SequenceI seq = new Sequence("", "ABC");
@@ -454,7 +454,7 @@ public class EditCommandTest
    * Test for 'undoing' a single delete edit.
    */
   @Test
-  public void testUnwindCommand_singleDelete()
+  public void testPriorState_singleDelete()
   {
     EditCommand command = new EditCommand();
     SequenceI seq = new Sequence("", "ABCDEF");
@@ -470,11 +470,10 @@ public class EditCommandTest
   }
 
   /**
-   * Test method that 'restores' edit commands to hold the sequence as it was
-   * before the edit was applied.
+   * Test 'undoing' a single gap insertion edit command.
    */
   @Test
-  public void testUnwindCommand_singleInsert()
+  public void testPriorState_singleInsert()
   {
     EditCommand command = new EditCommand();
     SequenceI seq = new Sequence("", "AB---CDEF");
@@ -494,7 +493,7 @@ public class EditCommandTest
    * for contiguous gaps in each sequence separately.
    */
   @Test
-  public void testUnwindCommand_removeGapsMultipleSeqs()
+  public void testPriorState_removeGapsMultipleSeqs()
   {
     EditCommand command = new EditCommand();
     String original1 = "--ABC-DEF";
@@ -558,7 +557,7 @@ public class EditCommandTest
    * column region.
    */
   @Test
-  public void testUnwindCommand_removeGappedCols()
+  public void testPriorState_removeGappedCols()
   {
     EditCommand command = new EditCommand();
     String original1 = "--ABC--DEF";
index 75626e9..b2fab71 100644 (file)
@@ -239,13 +239,13 @@ public class Jalview2xmlTests
   @Test
   public void gatherViewsHere() throws Exception
   {
-    int origCount = Desktop.getAlignframes() == null ? 0 : Desktop
-            .getAlignframes().length;
+    int origCount = Desktop.getAlignFrames() == null ? 0 : Desktop
+            .getAlignFrames().length;
     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
             "examples/exampleFile_2_7.jar", FormatAdapter.FILE);
     assertTrue("Didn't read in the example file correctly.", af != null);
     assertTrue("Didn't gather the views in the example file.",
-            Desktop.getAlignframes().length == 1 + origCount);
+            Desktop.getAlignFrames().length == 1 + origCount);
 
   }