Merge remote-tracking branch 'origin/merge/JAL-845_JAL-1640' into
[jalview.git] / src / jalview / gui / AlignViewport.java
index 705f53a..6208597 100644 (file)
@@ -43,9 +43,10 @@ import jalview.analysis.AlignmentUtils.MappingResult;
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.NJTree;
 import jalview.api.AlignViewportI;
+import jalview.api.ViewStyleI;
 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;
@@ -62,21 +63,20 @@ import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.AutoCalcSetting;
 
-import java.awt.Color;
 import java.awt.Container;
+import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Rectangle;
+import java.io.File;
 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!
@@ -95,74 +95,28 @@ public class AlignViewport extends AlignmentViewport implements
 
   int endSeq;
 
-  boolean showJVSuffix = true;
-
-  boolean showText = true;
-
-  boolean showColourText = false;
-
-  boolean showBoxes = true;
-
-  boolean wrapAlignment = false;
-
-  boolean renderGaps = true;
-
-  boolean showSequenceFeatures = false;
-
-  boolean showAnnotation = true;
 
   SequenceAnnotationOrder sortAnnotationsBy = null;
 
-  int charHeight;
-
-  int charWidth;
-
-  boolean validCharWidth;
-
-  int wrappedWidth;
-
   Font font;
 
-  boolean seqNameItalics;
-
   NJTree currentTree = null;
 
-  boolean scaleAboveWrapped = false;
-
-  boolean scaleLeftWrapped = true;
-
-  boolean scaleRightWrapped = true;
-
-  boolean showHiddenMarkers = true;
-
   boolean cursorMode = false;
 
-  /**
-   * Keys are the feature types which are currently visible. Note: Values are
-   * not used!
-   */
-  Hashtable featuresDisplayed = null;
-
   boolean antiAlias = false;
 
-  Rectangle explodedPosition;
+  private Rectangle explodedGeometry;
 
   String viewName;
 
-  boolean gatherViewsHere = false;
+  private boolean gatherViewsHere = false;
 
   private Deque<CommandI> historyList = new ArrayDeque<CommandI>();
 
   private Deque<CommandI> redoList = new ArrayDeque<CommandI>();
 
-  int thresholdTextColour = 0;
-
-  Color textColour = Color.black;
-
-  Color textColour2 = Color.white;
-
-  boolean rightAlignIds = false;
-
+  private AnnotationColumnChooser annotationColumnSelectionState;
   /**
    * Creates a new AlignViewport object.
    * 
@@ -218,16 +172,7 @@ public class AlignViewport extends AlignmentViewport implements
     setAlignment(al);
     if (hiddenColumns != null)
     {
-      this.colSel = hiddenColumns;
-      if (hiddenColumns.getHiddenColumns() != null
-              && hiddenColumns.getHiddenColumns().size() > 0)
-      {
-        hasHiddenColumns = true;
-      }
-      else
-      {
-        hasHiddenColumns = false;
-      }
+      colSel = hiddenColumns;
     }
     init();
   }
@@ -274,46 +219,51 @@ public class AlignViewport extends AlignmentViewport implements
     setAlignment(al);
     if (hiddenColumns != null)
     {
-      this.colSel = hiddenColumns;
-      if (hiddenColumns.getHiddenColumns() != null
-              && hiddenColumns.getHiddenColumns().size() > 0)
-      {
-        hasHiddenColumns = true;
-      }
-      else
-      {
-        hasHiddenColumns = false;
-      }
+      colSel = hiddenColumns;
     }
     init();
   }
 
-  void init()
+  private void applyViewProperties()
   {
-    this.startRes = 0;
-    this.endRes = alignment.getWidth() - 1;
-    this.startSeq = 0;
-    this.endSeq = alignment.getHeight() - 1;
-
     antiAlias = Cache.getDefault("ANTI_ALIAS", false);
 
-    showJVSuffix = Cache.getDefault("SHOW_JVSUFFIX", true);
-    showAnnotation = Cache.getDefault("SHOW_ANNOTATIONS", true);
+    viewStyle.setShowJVSuffix(Cache.getDefault("SHOW_JVSUFFIX", true));
+    setShowAnnotation(Cache.getDefault("SHOW_ANNOTATIONS", true));
 
-    rightAlignIds = Cache.getDefault("RIGHT_ALIGN_IDS", false);
-    centreColumnLabels = Cache.getDefault("CENTRE_COLUMN_LABELS", false);
+    setRightAlignIds(Cache.getDefault("RIGHT_ALIGN_IDS", false));
+    setCentreColumnLabels(Cache.getDefault("CENTRE_COLUMN_LABELS", false));
     autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
 
     setPadGaps(Cache.getDefault("PAD_GAPS", true));
-    shownpfeats = Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true);
-    showdbrefs = Cache.getDefault("SHOW_DBREFS_TOOLTIP", true);
+    setShowNPFeats(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
+    setShowDBRefs(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true));
+    viewStyle.setSeqNameItalics(Cache.getDefault("ID_ITALICS", true));
+    viewStyle.setWrapAlignment(Cache.getDefault("WRAP_ALIGNMENT", false));
+    viewStyle.setShowUnconserved(Cache
+            .getDefault("SHOW_UNCONSERVED", false));
+    sortByTree = Cache.getDefault("SORT_BY_TREE", false);
+    followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true);
+    sortAnnotationsBy = SequenceAnnotationOrder.valueOf(Cache.getDefault(
+            Preferences.SORT_ANNOTATIONS,
+            SequenceAnnotationOrder.NONE.name()));
+    showAutocalculatedAbove = Cache.getDefault(
+            Preferences.SHOW_AUTOCALC_ABOVE, false);
+
+  }
+
+  void init()
+  {
+    this.startRes = 0;
+    this.endRes = alignment.getWidth() - 1;
+    this.startSeq = 0;
+    this.endSeq = alignment.getHeight() - 1;
+    applyViewProperties();
 
     String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
     String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "");
     String fontSize = Cache.getDefault("FONT_SIZE", "10");
 
-    seqNameItalics = Cache.getDefault("ID_ITALICS", true);
-
     int style = 0;
 
     if (fontStyle.equals("bold"))
@@ -325,7 +275,7 @@ public class AlignViewport extends AlignmentViewport implements
       style = 2;
     }
 
-    setFont(new Font(fontName, style, Integer.parseInt(fontSize)));
+    setFont(new Font(fontName, style, Integer.parseInt(fontSize)), true);
 
     alignment
             .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
@@ -359,7 +309,7 @@ public class AlignViewport extends AlignmentViewport implements
       {
         globalColourScheme = UserDefinedColours.loadDefaultColours();
         ((UserColourScheme) globalColourScheme).setThreshold(0,
-                getIgnoreGapsConsensus());
+                isIgnoreGapsConsensus());
       }
 
       if (globalColourScheme != null)
@@ -367,47 +317,9 @@ public class AlignViewport extends AlignmentViewport implements
         globalColourScheme.setConsensus(hconsensus);
       }
     }
-
-    wrapAlignment = Cache.getDefault("WRAP_ALIGNMENT", false);
-    showUnconserved = Cache.getDefault("SHOW_UNCONSERVED", false);
-    sortByTree = Cache.getDefault("SORT_BY_TREE", false);
-    followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true);
-    sortAnnotationsBy = SequenceAnnotationOrder.valueOf(Cache.getDefault(
-            Preferences.SORT_ANNOTATIONS,
-            SequenceAnnotationOrder.NONE.name()));
-    showAutocalculatedAbove = Cache.getDefault(
-            Preferences.SHOW_AUTOCALC_ABOVE, false);
-  }
-
-  /**
-   * set the flag
-   * 
-   * @param b
-   *          features are displayed if true
-   */
-  public void setShowSequenceFeatures(boolean b)
-  {
-    showSequenceFeatures = b;
-  }
-
-  public boolean getShowSequenceFeatures()
-  {
-    return showSequenceFeatures;
   }
 
   /**
-   * centre columnar annotation labels in displayed alignment annotation TODO:
-   * add to jalviewXML and annotation display settings
-   */
-  boolean centreColumnLabels = false;
-
-  private boolean showdbrefs;
-
-  private boolean shownpfeats;
-
-  // --------END Structure Conservation
-
-  /**
    * get the consensus sequence as displayed under the PID consensus annotation
    * row.
    * 
@@ -551,105 +463,52 @@ public class AlignViewport extends AlignmentViewport implements
     return endSeq;
   }
 
+  boolean validCharWidth;
+
   /**
-   * DOCUMENT ME!
+   * update view settings with the given font. You may need to call
+   * alignPanel.fontChanged to update the layout geometry
    * 
-   * @param f
-   *          DOCUMENT ME!
+   * @param setGrid
+   *          when true, charWidth/height is set according to font mentrics
    */
-  public void setFont(Font f)
+  public void setFont(Font f, boolean setGrid)
   {
     font = f;
 
     Container c = new Container();
 
     java.awt.FontMetrics fm = c.getFontMetrics(font);
-    setCharHeight(fm.getHeight());
-    setCharWidth(fm.charWidth('M'));
-    validCharWidth = true;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public Font getFont()
-  {
-    return font;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param w
-   *          DOCUMENT ME!
-   */
-  public void setCharWidth(int w)
-  {
-    this.charWidth = w;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getCharWidth()
-  {
-    return charWidth;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param h
-   *          DOCUMENT ME!
-   */
-  public void setCharHeight(int h)
-  {
-    this.charHeight = h;
-  }
+    int w = viewStyle.getCharWidth(), ww = fm.charWidth('M'), h = viewStyle
+            .getCharHeight();
+    if (setGrid)
+    {
+      setCharHeight(fm.getHeight());
+      setCharWidth(ww);
+    }
+    viewStyle.setFontName(font.getName());
+    viewStyle.setFontStyle(font.getStyle());
+    viewStyle.setFontSize(font.getSize());
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getCharHeight()
-  {
-    return charHeight;
+    validCharWidth = true;
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param w
-   *          DOCUMENT ME!
-   */
-  public void setWrappedWidth(int w)
+  @Override
+  public void setViewStyle(ViewStyleI settingsForView)
   {
-    this.wrappedWidth = w;
-  }
+    super.setViewStyle(settingsForView);
+    setFont(new Font(viewStyle.getFontName(), viewStyle.getFontStyle(),
+            viewStyle.getFontSize()), false);
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getWrappedWidth()
-  {
-    return wrappedWidth;
   }
-
   /**
    * DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
    */
-  public AlignmentI getAlignment()
+  public Font getFont()
   {
-    return alignment;
+    return font;
   }
 
   /**
@@ -676,101 +535,6 @@ public class AlignViewport extends AlignmentViewport implements
   /**
    * DOCUMENT ME!
    * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setWrapAlignment(boolean state)
-  {
-    wrapAlignment = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setShowText(boolean state)
-  {
-    showText = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setRenderGaps(boolean state)
-  {
-    renderGaps = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getColourText()
-  {
-    return showColourText;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setColourText(boolean state)
-  {
-    showColourText = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param state
-   *          DOCUMENT ME!
-   */
-  public void setShowBoxes(boolean state)
-  {
-    showBoxes = state;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getWrapAlignment()
-  {
-    return wrapAlignment;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getShowText()
-  {
-    return showText;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getShowBoxes()
-  {
-    return showBoxes;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
    * @return DOCUMENT ME!
    */
   public char getGapCharacter()
@@ -824,131 +588,6 @@ public class AlignViewport extends AlignmentViewport implements
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getShowJVSuffix()
-  {
-    return showJVSuffix;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setShowJVSuffix(boolean b)
-  {
-    showJVSuffix = b;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getShowAnnotation()
-  {
-    return showAnnotation;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setShowAnnotation(boolean b)
-  {
-    showAnnotation = b;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getScaleAboveWrapped()
-  {
-    return scaleAboveWrapped;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getScaleLeftWrapped()
-  {
-    return scaleLeftWrapped;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean getScaleRightWrapped()
-  {
-    return scaleRightWrapped;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setScaleAboveWrapped(boolean b)
-  {
-    scaleAboveWrapped = b;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setScaleLeftWrapped(boolean b)
-  {
-    scaleLeftWrapped = b;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param b
-   *          DOCUMENT ME!
-   */
-  public void setScaleRightWrapped(boolean b)
-  {
-    scaleRightWrapped = b;
-  }
-
-  public void setDataset(boolean b)
-  {
-    isDataset = b;
-  }
-
-  public boolean isDataset()
-  {
-    return isDataset;
-  }
-
-  public boolean getShowHiddenMarkers()
-  {
-    return showHiddenMarkers;
-  }
-
-  public void setShowHiddenMarkers(boolean show)
-  {
-    showHiddenMarkers = show;
-  }
-
-  /**
    * returns the visible column regions of the alignment
    * 
    * @param selectedRegionOnly
@@ -1021,72 +660,6 @@ public class AlignViewport extends AlignmentViewport implements
     return false;
   }
 
-  public boolean getCentreColumnLabels()
-  {
-    return centreColumnLabels;
-  }
-
-  public void setCentreColumnLabels(boolean centrecolumnlabels)
-  {
-    centreColumnLabels = centrecolumnlabels;
-  }
-
-  /**
-   * enable or disable the display of Database Cross References in the sequence
-   * ID tooltip
-   */
-  public void setShowDbRefs(boolean show)
-  {
-    showdbrefs = show;
-  }
-
-  /**
-   * 
-   * @return true if Database References are to be displayed on tooltips.
-   */
-  public boolean isShowDbRefs()
-  {
-    return showdbrefs;
-  }
-
-  /**
-   * 
-   * @return true if Non-positional features are to be displayed on tooltips.
-   */
-  public boolean isShowNpFeats()
-  {
-    return shownpfeats;
-  }
-
-  /**
-   * enable or disable the display of Non-Positional sequence features in the
-   * sequence ID tooltip
-   * 
-   * @param show
-   */
-  public void setShowNpFeats(boolean show)
-  {
-    shownpfeats = show;
-  }
-
-  /**
-   * 
-   * @return true if view has hidden rows
-   */
-  public boolean hasHiddenRows()
-  {
-    return hasHiddenRows;
-  }
-
-  /**
-   * 
-   * @return true if view has hidden columns
-   */
-  public boolean hasHiddenColumns()
-  {
-    return hasHiddenColumns;
-  }
-
   /**
    * when set, view will scroll to show the highlighted position
    */
@@ -1113,8 +686,9 @@ public class AlignViewport extends AlignmentViewport implements
     return followSelection;
   }
 
-  boolean showSeqFeaturesHeight;
-
+  /**
+   * Send the current selection to be broadcast to any selection listeners.
+   */
   public void sendSelection()
   {
     jalview.structure.StructureSelectionManager
@@ -1123,16 +697,6 @@ public class AlignViewport extends AlignmentViewport implements
                     new ColumnSelection(getColumnSelection()), this);
   }
 
-  public void setShowSequenceFeaturesHeight(boolean selected)
-  {
-    showSeqFeaturesHeight = selected;
-  }
-
-  public boolean getShowSequenceFeaturesHeight()
-  {
-    return showSeqFeaturesHeight;
-  }
-
   /**
    * return the alignPanel containing the given viewport. Use this to get the
    * components currently handling the given viewport.
@@ -1145,7 +709,6 @@ public class AlignViewport extends AlignmentViewport implements
   {
     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
             .getSequenceSetId());
-    AlignmentPanel ap = null;
     for (int p = 0; aps != null && p < aps.length; p++)
     {
       if (aps[p].av == this)
@@ -1325,23 +888,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 'complement' of the
+     * source May replace this with direct calls not via SSM.
      */
-    if (!(command instanceof EditCommand))
+    if (source instanceof AlignViewportI
+            && ((AlignViewportI) source).getCodingComplement() == 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 +986,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
@@ -1426,6 +996,10 @@ public class AlignViewport extends AlignmentViewport implements
   {
     // TODO: promote to AlignViewportI? applet CutAndPasteTransfer is different
 
+    // JBPComment: title is a largely redundant parameter at the moment
+    // JBPComment: this really should be an 'insert/pre/append' controller
+    // JBPComment: but the DNA/Protein check makes it a bit more complex
+
     // refactored from FileLoader / CutAndPasteTransfer / SequenceFetcher with
     // this comment:
     // TODO: create undo object for this JAL-1101
@@ -1436,6 +1010,7 @@ public class AlignViewport extends AlignmentViewport implements
      */
     if (getAlignment().isNucleotide() != al.isNucleotide())
     {
+      // TODO: JAL-845 try a bit harder to link up imported sequences
       final Set<String> sequenceNames = getAlignment().getSequenceNames();
       sequenceNames.retainAll(al.getSequenceNames());
       if (!sequenceNames.isEmpty()) // at least one sequence name in both
@@ -1446,6 +1021,8 @@ public class AlignViewport extends AlignmentViewport implements
         }
       }
     }
+    // TODO: JAL-407 regardless of above - identical sequences (based on ID and
+    // provenance) should share the same dataset sequence
 
     for (int i = 0; i < al.getHeight(); i++)
     {
@@ -1453,15 +1030,16 @@ public class AlignViewport extends AlignmentViewport implements
     }
     // TODO this call was done by SequenceFetcher but not FileLoader or
     // CutAndPasteTransfer. Is it needed?
+    // JBPComment: this repositions the view to show the new sequences
+    // JBPComment: so it is needed for UX
     setEndSeq(getAlignment().getHeight());
     firePropertyChange("alignment", null, getAlignment().getSequences());
   }
 
   /**
    * 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 +1055,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 +1067,20 @@ 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);
+    newAlignFrame.setTitle(title);
 
-    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();
+    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,39 +1090,23 @@ 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);
     }
 
     /*
-     * Try to find mappings for at least one sequence.
+     * Try to find mappings for at least one sequence. Any mappings made will be
+     * added to the protein alignment.
      */
     MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna);
-    if (mapped == MappingResult.Mapped)
-    {
-
-      /*
-       * 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
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
+    if (mapped != MappingResult.Mapped)
     {
-
       /*
        * No mapping possible - warn the user, but leave window open.
        */
@@ -1555,7 +1119,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)
     {
@@ -1563,25 +1128,98 @@ public class AlignViewport extends AlignmentViewport implements
 
     if (openSplitPane)
     {
+      // TODO: move this kind of constructor stuff to a factory/controller
+      // method ?
       /*
-       * 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);
-      Desktop.addInternalFrame(splitFrame, title, AlignFrame.DEFAULT_WIDTH,
-              AlignFrame.DEFAULT_HEIGHT);
+      AlignFrame copyMe = new AlignFrame(thisAlignment,
+              AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+      copyMe.setTitle(getAlignPanel().alignFrame.getTitle());
+      final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
+              : newAlignFrame;
+      final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame
+              : copyMe;
+      protein = proteinFrame.viewport.getAlignment();
+
+      cdnaFrame.setVisible(true);
+      proteinFrame.setVisible(true);
+      String sep = String.valueOf(File.separatorChar);
+      String proteinShortName = proteinFrame.getTitle().substring(
+              proteinFrame.getTitle().lastIndexOf(sep) + 1);
+      String dnaShortName = cdnaFrame.getTitle().substring(
+              cdnaFrame.getTitle().lastIndexOf(sep) + 1);
+      String linkedTitle = MessageManager.formatMessage(
+              "label.linked_view_title", dnaShortName, proteinShortName);
+      JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame);
+      Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
+
+      /*
+       * Set the frames to listen for each other's edit and sort commands.
+       */
+      ssm.addCommandListener(cdnaFrame.getViewport());
+      ssm.addCommandListener(proteinFrame.getViewport());
+
+      /*
+       * 'Coding complement' (dna/protein) views will mirror each others' edits,
+       * selections, sorting etc as decided from time to time by the relevant
+       * authorities.
+       */
+      proteinFrame.getViewport().setCodingComplement(cdnaFrame.getViewport());
     }
 
+    /*
+     * Register the mappings (held on the protein alignment) with the
+     * StructureSelectionManager (for mouseover linking).
+     */
+    ssm.addMappings(protein.getCodonFrames());
+
     return true;
   }
+
+  public AnnotationColumnChooser getAnnotationColumnSelectionState()
+  {
+    return annotationColumnSelectionState;
+  }
+
+  public void setAnnotationColumnSelectionState(
+          AnnotationColumnChooser currentAnnotationColumnSelectionState)
+  {
+    this.annotationColumnSelectionState = currentAnnotationColumnSelectionState;
+  }
+
+  @Override
+  public void setIdWidth(int i)
+  {
+    super.setIdWidth(i);
+    AlignmentPanel ap = getAlignPanel();
+    if (ap != null)
+    {
+      // modify GUI elements to reflect geometry change
+      Dimension idw = getAlignPanel().getIdPanel().getIdCanvas()
+              .getPreferredSize();
+      idw.width = i;
+      getAlignPanel().getIdPanel().getIdCanvas().setPreferredSize(idw);
+    }
+  }
+
+  public Rectangle getExplodedGeometry()
+  {
+    return explodedGeometry;
+  }
+
+  public void setExplodedGeometry(Rectangle explodedPosition)
+  {
+    this.explodedGeometry = explodedPosition;
+  }
+
+  public boolean isGatherViewsHere()
+  {
+    return gatherViewsHere;
+  }
+
+  public void setGatherViewsHere(boolean gatherViewsHere)
+  {
+    this.gatherViewsHere = gatherViewsHere;
+  }
 }