JAL-1645 Version-Rel Version 2.9 Year-Rel 2015 Licensing glob
[jalview.git] / src / jalview / gui / AlignFrame.java
index 42460dc..b484118 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
+ * Copyright (C) 2015 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  */
 package jalview.gui;
 
-import jalview.analysis.AAFrequency;
 import jalview.analysis.AlignmentSorter;
 import jalview.analysis.AlignmentUtils;
-import jalview.analysis.Conservation;
 import jalview.analysis.CrossRef;
-import jalview.analysis.NJTree;
+import jalview.analysis.Dna;
 import jalview.analysis.ParseProperties;
 import jalview.analysis.SequenceIdMatcher;
+import jalview.api.AlignExportSettingI;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureSettingsControllerI;
+import jalview.api.SplitContainerI;
+import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
 import jalview.bin.Cache;
+import jalview.bin.Jalview;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
@@ -43,18 +48,21 @@ import jalview.commands.TrimRegionCommand;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentExportData;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SeqCigar;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.gui.ViewSelectionMenu.ViewSetProvider;
 import jalview.io.AlignmentProperties;
 import jalview.io.AnnotationFile;
-import jalview.io.FeaturesFile;
+import jalview.io.BioJsHTMLOutput;
 import jalview.io.FileLoader;
 import jalview.io.FormatAdapter;
 import jalview.io.HtmlSvgOutput;
@@ -83,7 +91,9 @@ import jalview.schemes.TaylorColourScheme;
 import jalview.schemes.TurnColourScheme;
 import jalview.schemes.UserColourScheme;
 import jalview.schemes.ZappoColourScheme;
+import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
@@ -91,7 +101,6 @@ import jalview.ws.seqfetcher.DbSourceProxy;
 
 import java.awt.BorderLayout;
 import java.awt.Component;
-import java.awt.GridLayout;
 import java.awt.Rectangle;
 import java.awt.Toolkit;
 import java.awt.datatransfer.Clipboard;
@@ -105,6 +114,8 @@ import java.awt.dnd.DropTargetEvent;
 import java.awt.dnd.DropTargetListener;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.awt.event.KeyAdapter;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
@@ -115,22 +126,21 @@ import java.beans.PropertyChangeEvent;
 import java.io.File;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Set;
 import java.util.Vector;
 
-import javax.swing.JButton;
 import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JEditorPane;
 import javax.swing.JInternalFrame;
-import javax.swing.JLabel;
 import javax.swing.JLayeredPane;
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JProgressBar;
 import javax.swing.JRadioButtonMenuItem;
 import javax.swing.JScrollPane;
 import javax.swing.SwingUtilities;
@@ -145,19 +155,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         IProgressIndicator, AlignViewControllerGuiI
 {
 
-  /** DOCUMENT ME!! */
   public static final int DEFAULT_WIDTH = 700;
 
-  /** DOCUMENT ME!! */
   public static final int DEFAULT_HEIGHT = 500;
 
+  /*
+   * The currently displayed panel (selected tabbed view if more than one)
+   */
   public AlignmentPanel alignPanel;
 
   AlignViewport viewport;
 
   public AlignViewControllerI avc;
 
-  Vector alignPanels = new Vector();
+  List<AlignmentPanel> alignPanels = new ArrayList<AlignmentPanel>();
 
   /**
    * Last format used to load or save alignments in this window
@@ -274,7 +285,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     alignPanel = new AlignmentPanel(this, viewport);
 
+    addAlignmentPanel(alignPanel, true);
+    init();
+  }
+
+  public AlignFrame(AlignmentI al, SequenceI[] hiddenSeqs,
+          ColumnSelection hiddenColumns, int width, int height)
+  {
+    setSize(width, height);
 
+    if (al.getDataset() == null)
+    {
+      al.setDataset(null);
+    }
+
+    viewport = new AlignViewport(al, hiddenColumns);
+
+    if (hiddenSeqs != null && hiddenSeqs.length > 0)
+    {
+      viewport.hideSequence(hiddenSeqs);
+    }
+    alignPanel = new AlignmentPanel(this, viewport);
     addAlignmentPanel(alignPanel, true);
     init();
   }
@@ -301,6 +332,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   void init()
   {
+    if (!Jalview.isHeadlessMode())
+    {
+      progressBar = new ProgressBar(this.statusPanel, this.statusBar);
+    }
+
     avc = new jalview.controller.AlignViewController(this, viewport,
             alignPanel);
     if (viewport.getAlignmentConservationAnnotation() == null)
@@ -335,8 +371,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     setMenusFromViewport(viewport);
     buildSortByAnnotationScoresMenu();
     buildTreeMenu();
-    
-    if (viewport.wrapAlignment)
+
+    if (viewport.getWrapAlignment())
     {
       wrapMenuItem_actionPerformed(null);
     }
@@ -348,6 +384,82 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     addKeyListener();
 
+    final List<AlignmentPanel> selviews = new ArrayList<AlignmentPanel>();
+    final List<AlignmentPanel> origview = new ArrayList<AlignmentPanel>();
+    final String menuLabel = MessageManager
+            .getString("label.copy_format_from");
+    ViewSelectionMenu vsel = new ViewSelectionMenu(menuLabel,
+            new ViewSetProvider()
+            {
+
+              @Override
+              public AlignmentPanel[] getAllAlignmentPanels()
+              {
+                origview.clear();
+                origview.add(alignPanel);
+                // make an array of all alignment panels except for this one
+                List<AlignmentPanel> aps = new ArrayList<AlignmentPanel>(
+                        Arrays.asList(Desktop.getAlignmentPanels(null)));
+                aps.remove(AlignFrame.this.alignPanel);
+                return aps.toArray(new AlignmentPanel[aps.size()]);
+              }
+            }, selviews, new ItemListener()
+            {
+
+              @Override
+              public void itemStateChanged(ItemEvent e)
+              {
+                if (origview.size() > 0)
+                {
+                  final AlignmentPanel ap = origview.get(0);
+
+                  /*
+                   * Copy the ViewStyle of the selected panel to 'this one'.
+                   * Don't change value of 'scaleProteinAsCdna' unless copying
+                   * from a SplitFrame.
+                   */
+                  ViewStyleI vs = selviews.get(0).getAlignViewport()
+                          .getViewStyle();
+                  boolean fromSplitFrame = selviews.get(0)
+                          .getAlignViewport().getCodingComplement() != null;
+                  if (!fromSplitFrame)
+                  {
+                    vs.setScaleProteinAsCdna(ap.getAlignViewport()
+                            .getViewStyle().isScaleProteinAsCdna());
+                  }
+                  ap.getAlignViewport().setViewStyle(vs);
+
+                  /*
+                   * Also rescale ViewStyle of SplitFrame complement if there is
+                   * one _and_ it is set to 'scaledProteinAsCdna'; we don't copy
+                   * the whole ViewStyle (allow cDNA protein to have different
+                   * fonts)
+                   */
+                  AlignViewportI complement = ap.getAlignViewport()
+                          .getCodingComplement();
+                  if (complement != null && vs.isScaleProteinAsCdna())
+                  {
+                    AlignFrame af = Desktop.getAlignFrameFor(complement);
+                    ((SplitFrame) af.getSplitViewContainer())
+                            .adjustLayout();
+                    af.setMenusForViewport();
+                  }
+
+                  ap.updateLayout();
+                  ap.setSelected(true);
+                  ap.alignFrame.setMenusForViewport();
+
+                }
+              }
+            });
+    if (Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase()
+            .indexOf("devel") > -1
+            || Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase()
+                    .indexOf("test") > -1)
+    {
+      formatMenu.add(vsel);
+    }
+
   }
 
   /**
@@ -362,10 +474,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void setFileName(String file, String format)
   {
     fileName = file;
-    currentFileFormat = format;
+    setFileFormat(format);
     reload.setEnabled(true);
   }
 
+  /**
+   * Add a KeyListener with handlers for various KeyPressed and KeyReleased
+   * events
+   */
   void addKeyListener()
   {
     addKeyListener(new KeyAdapter()
@@ -439,8 +555,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         case KeyEvent.VK_SPACE:
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().insertGapAtCursor(evt.isControlDown()
-                    || evt.isShiftDown() || evt.isAltDown());
+            alignPanel.getSeqPanel().insertGapAtCursor(
+                    evt.isControlDown() || evt.isShiftDown()
+                            || evt.isAltDown());
           }
           break;
 
@@ -463,8 +580,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.getSeqPanel().deleteGapAtCursor(evt.isControlDown()
-                    || evt.isShiftDown() || evt.isAltDown());
+            alignPanel.getSeqPanel().deleteGapAtCursor(
+                    evt.isControlDown() || evt.isShiftDown()
+                            || evt.isAltDown());
           }
 
           break;
@@ -512,8 +630,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         case KeyEvent.VK_F2:
           viewport.cursorMode = !viewport.cursorMode;
           statusBar.setText(MessageManager.formatMessage(
-                  "label.keyboard_editing_mode", new String[]
-                  { (viewport.cursorMode ? "on" : "off") }));
+                  "label.keyboard_editing_mode",
+                  new String[] { (viewport.cursorMode ? "on" : "off") }));
           if (viewport.cursorMode)
           {
             alignPanel.getSeqPanel().seqCanvas.cursorX = viewport.startRes;
@@ -539,7 +657,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           break;
         }
         case KeyEvent.VK_PAGE_UP:
-          if (viewport.wrapAlignment)
+          if (viewport.getWrapAlignment())
           {
             alignPanel.scrollUp(true);
           }
@@ -550,7 +668,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           break;
         case KeyEvent.VK_PAGE_DOWN:
-          if (viewport.wrapAlignment)
+          if (viewport.getWrapAlignment())
           {
             alignPanel.scrollUp(false);
           }
@@ -594,7 +712,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());
 
@@ -637,7 +755,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);
   }
@@ -681,7 +799,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       public void internalFrameClosed(
               javax.swing.event.InternalFrameEvent evt)
       {
-        System.out.println("deregistering discoverer listener");
+        // System.out.println("deregistering discoverer listener");
         Desktop.instance.removeJalviewPropertyChangeListener("services",
                 thisListener);
         closeMenuItem_actionPerformed(true);
@@ -698,6 +816,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }).start();
   }
 
+  /**
+   * Configure menu items that vary according to whether the alignment is
+   * nucleotide or protein
+   * 
+   * @param nucleotide
+   */
   public void setGUINucleotide(boolean nucleotide)
   {
     showTranslation.setVisible(nucleotide);
@@ -706,16 +830,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     showGroupConservation.setEnabled(!nucleotide);
     rnahelicesColour.setEnabled(nucleotide);
     purinePyrimidineColour.setEnabled(nucleotide);
-    // Remember AlignFrame always starts as protein
-    // if (!nucleotide)
-    // {
-    // showTr
-    // calculateMenu.remove(calculateMenu.getItemCount() - 2);
-    // }
+    showComplementMenuItem.setText(MessageManager
+            .getString(nucleotide ? "label.protein" : "label.nucleotide"));
+    setColourSelected(jalview.bin.Cache.getDefault(
+            nucleotide ? Preferences.DEFAULT_COLOUR_NUC
+                    : Preferences.DEFAULT_COLOUR_PROT, "None"));
   }
 
   /**
-   * set up menus for the currently viewport. This may be called after any
+   * set up menus for the current viewport. This may be called after any
    * operation that affects the data in the current view (selection changed,
    * etc) to update the menus to reflect the new state.
    */
@@ -734,17 +857,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   void setMenusFromViewport(AlignViewport av)
   {
     padGapsMenuitem.setSelected(av.isPadGaps());
-    colourTextMenuItem.setSelected(av.showColourText);
+    colourTextMenuItem.setSelected(av.isShowColourText());
     abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
     conservationMenuItem.setSelected(av.getConservationSelected());
     seqLimits.setSelected(av.getShowJVSuffix());
     idRightAlign.setSelected(av.isRightAlignIds());
-    centreColumnLabelsMenuItem.setState(av.centreColumnLabels);
-    renderGapsMenuItem.setSelected(av.renderGaps);
-    wrapMenuItem.setSelected(av.wrapAlignment);
-    scaleAbove.setVisible(av.wrapAlignment);
-    scaleLeft.setVisible(av.wrapAlignment);
-    scaleRight.setVisible(av.wrapAlignment);
+    centreColumnLabelsMenuItem.setState(av.isCentreColumnLabels());
+    renderGapsMenuItem.setSelected(av.isRenderGaps());
+    wrapMenuItem.setSelected(av.getWrapAlignment());
+    scaleAbove.setVisible(av.getWrapAlignment());
+    scaleLeft.setVisible(av.getWrapAlignment());
+    scaleRight.setVisible(av.getWrapAlignment());
     annotationPanelMenuItem.setState(av.isShowAnnotation());
     /*
      * Show/hide annotations only enabled if annotation panel is shown
@@ -753,8 +876,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
     showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
     hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
-    viewBoxesMenuItem.setSelected(av.showBoxes);
-    viewTextMenuItem.setSelected(av.showText);
+    viewBoxesMenuItem.setSelected(av.getShowBoxes());
+    viewTextMenuItem.setSelected(av.getShowText());
     showNonconservedMenuItem.setSelected(av.getShowUnconserved());
     showGroupConsensus.setSelected(av.isShowGroupConsensus());
     showGroupConservation.setSelected(av.isShowGroupConservation());
@@ -765,11 +888,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     setColourSelected(ColourSchemeProperty.getColourName(av
             .getGlobalColourScheme()));
 
-    showSeqFeatures.setSelected(av.showSequenceFeatures);
-    hiddenMarkers.setState(av.showHiddenMarkers);
+    showSeqFeatures.setSelected(av.isShowSequenceFeatures());
+    hiddenMarkers.setState(av.getShowHiddenMarkers());
     applyToAllGroups.setState(av.getColourAppliesToAllGroups());
-    showNpFeatsMenuitem.setSelected(av.isShowNpFeats());
-    showDbRefsMenuitem.setSelected(av.isShowDbRefs());
+    showNpFeatsMenuitem.setSelected(av.isShowNPFeats());
+    showDbRefsMenuitem.setSelected(av.isShowDBRefs());
     autoCalculate.setSelected(av.autoCalculateConsensus);
     sortByTree.setSelected(av.sortByTree);
     listenToViewSelections.setSelected(av.followSelection);
@@ -780,9 +903,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     updateEditMenuBar();
   }
 
-  // methods for implementing IProgressIndicator
-  // need to refactor to a reusable stub class
-  Hashtable progressBars, progressBarHandlers;
+  private IProgressIndicator progressBar;
 
   /*
    * (non-Javadoc)
@@ -792,78 +913,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void setProgressBar(String message, long id)
   {
-    if (progressBars == null)
-    {
-      progressBars = new Hashtable();
-      progressBarHandlers = new Hashtable();
-    }
-
-    JPanel progressPanel;
-    Long lId = new Long(id);
-    GridLayout layout = (GridLayout) statusPanel.getLayout();
-    if (progressBars.get(lId) != null)
-    {
-      progressPanel = (JPanel) progressBars.get(new Long(id));
-      statusPanel.remove(progressPanel);
-      progressBars.remove(lId);
-      progressPanel = null;
-      if (message != null)
-      {
-        statusBar.setText(message);
-      }
-      if (progressBarHandlers.contains(lId))
-      {
-        progressBarHandlers.remove(lId);
-      }
-      layout.setRows(layout.getRows() - 1);
-    }
-    else
-    {
-      progressPanel = new JPanel(new BorderLayout(10, 5));
-
-      JProgressBar progressBar = new JProgressBar();
-      progressBar.setIndeterminate(true);
-
-      progressPanel.add(new JLabel(message), BorderLayout.WEST);
-      progressPanel.add(progressBar, BorderLayout.CENTER);
-
-      layout.setRows(layout.getRows() + 1);
-      statusPanel.add(progressPanel);
-
-      progressBars.put(lId, progressPanel);
-    }
-    // update GUI
-    // setMenusForViewport();
-    validate();
+    progressBar.setProgressBar(message, id);
   }
 
   @Override
   public void registerHandler(final long id,
           final IProgressIndicatorHandler handler)
   {
-    if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
-    {
-      throw new Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
-    }
-    progressBarHandlers.put(new Long(id), handler);
-    final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
-    if (handler.canCancel())
-    {
-      JButton cancel = new JButton(
-              MessageManager.getString("action.cancel"));
-      final IProgressIndicator us = this;
-      cancel.addActionListener(new ActionListener()
-      {
-
-        @Override
-        public void actionPerformed(ActionEvent e)
-        {
-          handler.cancelActivity(id);
-          us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
-        }
-      });
-      progressPanel.add(cancel, BorderLayout.EAST);
-    }
+    progressBar.registerHandler(id, handler);
   }
 
   /**
@@ -873,18 +930,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public boolean operationInProgress()
   {
-    if (progressBars != null && progressBars.size() > 0)
-    {
-      return true;
-    }
-    return false;
+    return progressBar.operationInProgress();
   }
 
   @Override
   public void setStatus(String text)
   {
     statusBar.setText(text);
-  };
+  }
 
   /*
    * Added so Castor Mapping file can obtain Jalview Version
@@ -981,7 +1034,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void addFromText_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.inputTextboxMenuItem_actionPerformed(viewport);
+    Desktop.instance.inputTextboxMenuItem_actionPerformed(viewport
+            .getAlignPanel());
   }
 
   @Override
@@ -1022,7 +1076,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             currentFileFormat, false);
 
     chooser.setFileView(new JalviewFileView());
-    chooser.setDialogTitle(MessageManager.getString("label.save_alignment_to_file"));
+    chooser.setDialogTitle(MessageManager
+            .getString("label.save_alignment_to_file"));
     chooser.setToolTipText(MessageManager.getString("action.save"));
 
     int value = chooser.showSaveDialog(this);
@@ -1030,7 +1085,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
       currentFileFormat = chooser.getSelectedFormat();
-      if (currentFileFormat == null)
+      while (currentFileFormat == null)
       {
         JOptionPane
                 .showInternalMessageDialog(
@@ -1040,8 +1095,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         MessageManager
                                 .getString("label.file_format_not_specified"),
                         JOptionPane.WARNING_MESSAGE);
+        currentFileFormat = chooser.getSelectedFormat();
         value = chooser.showSaveDialog(this);
-        return;
+        if (value != JalviewFileChooser.APPROVE_OPTION)
+        {
+          return;
+        }
       }
 
       fileName = chooser.getSelectedFile().getPath();
@@ -1073,11 +1132,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 .lastIndexOf(java.io.File.separatorChar) + 1);
       }
 
-      success = new Jalview2XML().SaveAlignment(this, file, shortName);
+      success = new Jalview2XML().saveAlignment(this, file, shortName);
 
       statusBar.setText(MessageManager.formatMessage(
-              "label.successfully_saved_to_file_in_format", new String[]
-              { fileName, format }));
+              "label.successfully_saved_to_file_in_format", new Object[] {
+                  fileName, format }));
 
     }
     else
@@ -1086,35 +1145,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         warningMessage("Cannot save file " + fileName + " using format "
                 + format, "Alignment output format not supported");
-        saveAs_actionPerformed(null);
-        // JBPNote need to have a raise_gui flag here
+        if (!Jalview.isHeadlessMode())
+        {
+          saveAs_actionPerformed(null);
+        }
         return false;
       }
 
-      String[] omitHidden = null;
-
-      if (viewport.hasHiddenColumns())
+      AlignmentExportData exportData = getAlignmentForExport(format,
+              viewport, null);
+      if (exportData.getSettings().isCancelled())
       {
-        int reply = JOptionPane
-                .showInternalConfirmDialog(
-                        Desktop.desktop,
-                        MessageManager
-                                .getString("label.alignment_contains_hidden_columns"),
-                        MessageManager
-                                .getString("action.save_omit_hidden_columns"),
-                        JOptionPane.YES_NO_OPTION,
-                        JOptionPane.QUESTION_MESSAGE);
-
-        if (reply == JOptionPane.YES_OPTION)
-        {
-          omitHidden = viewport.getViewAsString(false);
-        }
+        return false;
       }
-      FormatAdapter f = new FormatAdapter();
-      String output = f.formatSequences(format,
-              viewport.getAlignment(), // class cast exceptions will
+      FormatAdapter f = new FormatAdapter(alignPanel,
+              exportData.getSettings());
+      String output = f.formatSequences(
+              format,
+              exportData.getAlignment(), // class cast exceptions will
               // occur in the distant future
-              omitHidden, f.getCacheSuffixDefault(format),
+              exportData.getOmitHidden(), exportData.getStartEndPostions(),
+              f.getCacheSuffixDefault(format),
               viewport.getColumnSelection());
 
       if (output == null)
@@ -1133,8 +1184,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           this.setTitle(file);
           statusBar.setText(MessageManager.formatMessage(
                   "label.successfully_saved_to_file_in_format",
-                  new String[]
-                  { fileName, format }));
+                  new Object[] { fileName, format }));
         } catch (Exception ex)
         {
           success = false;
@@ -1146,8 +1196,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (!success)
     {
       JOptionPane.showInternalMessageDialog(this, MessageManager
-              .formatMessage("label.couldnt_save_file", new String[]
-              { fileName }), MessageManager
+              .formatMessage("label.couldnt_save_file",
+                      new Object[] { fileName }), MessageManager
               .getString("label.error_saving_file"),
               JOptionPane.WARNING_MESSAGE);
     }
@@ -1179,37 +1229,26 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void outputText_actionPerformed(ActionEvent e)
   {
-    String[] omitHidden = null;
 
-    if (viewport.hasHiddenColumns())
+    AlignmentExportData exportData = getAlignmentForExport(
+            e.getActionCommand(), viewport, null);
+    if (exportData.getSettings().isCancelled())
     {
-      int reply = JOptionPane
-              .showInternalConfirmDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.alignment_contains_hidden_columns"),
-                      MessageManager
-                              .getString("action.save_omit_hidden_columns"),
-                      JOptionPane.YES_NO_OPTION,
-                      JOptionPane.QUESTION_MESSAGE);
-
-      if (reply == JOptionPane.YES_OPTION)
-      {
-        omitHidden = viewport.getViewAsString(false);
-      }
+      return;
     }
-
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
     cap.setForInput(null);
-
     try
     {
-      cap.setText(new FormatAdapter().formatSequences(e.getActionCommand(),
-              viewport.getAlignment(), omitHidden,
-              viewport.getColumnSelection()));
+      cap.setText(new FormatAdapter(alignPanel, exportData.getSettings())
+              .formatSequences(e.getActionCommand(),
+                      exportData.getAlignment(),
+                      exportData.getOmitHidden(),
+                      exportData.getStartEndPostions(),
+                      viewport.getColumnSelection()));
       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
-              "label.alignment_output_command", new String[]
-              { e.getActionCommand() }), 600, 500);
+              "label.alignment_output_command",
+              new Object[] { e.getActionCommand() }), 600, 500);
     } catch (OutOfMemoryError oom)
     {
       new OOMWarning("Outputting alignment as " + e.getActionCommand(), oom);
@@ -1218,6 +1257,102 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   }
 
+  public static AlignmentExportData getAlignmentForExport(
+          String exportFormat, AlignViewportI viewport,
+          AlignExportSettingI exportSettings)
+  {
+    AlignmentI alignmentToExport = null;
+    AlignExportSettingI settings = exportSettings;
+    String[] omitHidden = null;
+    int[] alignmentStartEnd = new int[2];
+
+    HiddenSequences hiddenSeqs = viewport.getAlignment()
+            .getHiddenSequences();
+
+    alignmentToExport = viewport.getAlignment();
+    alignmentStartEnd = new int[] { 0, alignmentToExport.getWidth() - 1 };
+
+    boolean hasHiddenSeqs = hiddenSeqs.getSize() > 0;
+    if (settings == null)
+    {
+      settings = new AlignExportSettings(hasHiddenSeqs,
+              viewport.hasHiddenColumns(), exportFormat);
+    }
+    // settings.isExportAnnotations();
+
+    if (viewport.hasHiddenColumns() && !settings.isExportHiddenColumns())
+    {
+      omitHidden = viewport.getViewAsString(false);
+    }
+
+    if (hasHiddenSeqs && settings.isExportHiddenSequences())
+    {
+      alignmentToExport = hiddenSeqs.getFullAlignment();
+    }
+    else
+    {
+      alignmentToExport = viewport.getAlignment();
+      alignmentStartEnd = getStartEnd(alignmentStartEnd, viewport
+              .getColumnSelection().getHiddenColumns());
+    }
+    AlignmentExportData ed = new AlignmentExportData(alignmentToExport,
+            omitHidden, alignmentStartEnd, settings);
+    return ed;
+  }
+
+  public static int[] getStartEnd(int[] aligmentStartEnd,
+          List<int[]> hiddenCols)
+  {
+    int startPos = aligmentStartEnd[0];
+    int endPos = aligmentStartEnd[1];
+
+    int[] lowestRange = new int[2];
+    int[] higestRange = new int[2];
+
+    for (int[] hiddenCol : hiddenCols)
+    {
+      // System.out.println("comparing : " + hiddenCol[0] + "-" + hiddenCol[1]);
+      lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
+      higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
+    }
+    // System.out.println("min : " + lowestRange[0] + "-" + lowestRange[1]);
+    // System.out.println("max : " + higestRange[0] + "-" + higestRange[1]);
+
+    if (lowestRange[0] == 0 && lowestRange[1] == 0)
+    {
+      startPos = aligmentStartEnd[0];
+    }
+    else
+    {
+      startPos = lowestRange[1] + 1;
+    }
+
+    if (higestRange[0] == 0 && higestRange[1] == 0)
+    {
+      endPos = aligmentStartEnd[1];
+    }
+    else
+    {
+      endPos = higestRange[0];
+    }
+
+    // System.out.println("Export range : " + minPos + " - " + maxPos);
+    return new int[] { startPos, endPos };
+  }
+
+  public static void main(String[] args)
+  {
+    ArrayList<int[]> hiddenCols = new ArrayList<int[]>();
+    hiddenCols.add(new int[] { 0, 4 });
+    hiddenCols.add(new int[] { 6, 9 });
+    hiddenCols.add(new int[] { 11, 12 });
+    hiddenCols.add(new int[] { 33, 33 });
+    hiddenCols.add(new int[] { 45, 50 });
+
+    int[] x = getStartEnd(new int[] { 0, 50 }, hiddenCols);
+    // System.out.println("Export range : " + x[0] + " - " + x[1]);
+  }
+
   /**
    * DOCUMENT ME!
    * 
@@ -1227,12 +1362,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void htmlMenuItem_actionPerformed(ActionEvent e)
   {
-    // new HTMLOutput(alignPanel,
-    // alignPanel.getSeqPanel().seqCanvas.getSequenceRenderer(),
-    // alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer());
     new HtmlSvgOutput(null, alignPanel);
   }
 
+  @Override
+  public void bioJSMenuItem_actionPerformed(ActionEvent e)
+  {
+    BioJsHTMLOutput bjs = new BioJsHTMLOutput(alignPanel);
+    bjs.exportJalviewAlignmentAsBioJsHtmlFile();
+  }
+
   public void createImageMap(File file, String image)
   {
     alignPanel.makePNGImageMap(file, image);
@@ -1266,6 +1405,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     alignPanel.makeSVG(f);
   }
+
   @Override
   public void pageSetup_actionPerformed(ActionEvent e)
   {
@@ -1296,11 +1436,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void exportAnnotations_actionPerformed(ActionEvent e)
   {
-    new AnnotationExporter().exportAnnotations(alignPanel,
-            viewport.isShowAnnotation() ? viewport.getAlignment()
-                    .getAlignmentAnnotation() : null, viewport
-                    .getAlignment().getGroups(), ((Alignment) viewport
-                    .getAlignment()).alignmentProperties);
+    new AnnotationExporter().exportAnnotations(alignPanel);
   }
 
   @Override
@@ -1352,7 +1488,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();
             }
           }
@@ -1365,6 +1501,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       if (closeAllTabs)
       {
+        /*
+         * this will raise an INTERNAL_FRAME_CLOSED event and this method will
+         * be called recursively, with the frame now in 'closed' state
+         */
         this.setClosed(true);
       }
     } catch (Exception ex)
@@ -1374,22 +1514,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * close alignPanel2 and shuffle tabs appropriately.
+   * Close the specified panel and close up tabs appropriately.
    * 
-   * @param alignPanel2
+   * @param panelToClose
    */
-  public void closeView(AlignmentPanel alignPanel2)
+  public void closeView(AlignmentPanel panelToClose)
   {
     int index = tabbedPane.getSelectedIndex();
-    int closedindex = tabbedPane.indexOfComponent(alignPanel2);
-    alignPanels.removeElement(alignPanel2);
-    // Unnecessary
-    // if (viewport == alignPanel2.av)
-    // {
-    // viewport = null;
-    // }
-    alignPanel2.closePanel();
-    alignPanel2 = null;
+    int closedindex = tabbedPane.indexOfComponent(panelToClose);
+    alignPanels.remove(panelToClose);
+    panelToClose.closePanel();
+    panelToClose = null;
 
     tabbedPane.removeTabAt(closedindex);
     tabbedPane.validate();
@@ -1409,13 +1544,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   void updateEditMenuBar()
   {
 
-    if (viewport.historyList.size() > 0)
+    if (viewport.getHistoryList().size() > 0)
     {
       undoMenuItem.setEnabled(true);
-      CommandI command = viewport.historyList.peek();
+      CommandI command = viewport.getHistoryList().peek();
       undoMenuItem.setText(MessageManager.formatMessage(
-              "label.undo_command", new String[]
-              { command.getDescription() }));
+              "label.undo_command",
+              new Object[] { command.getDescription() }));
     }
     else
     {
@@ -1423,14 +1558,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       undoMenuItem.setText(MessageManager.getString("action.undo"));
     }
 
-    if (viewport.redoList.size() > 0)
+    if (viewport.getRedoList().size() > 0)
     {
       redoMenuItem.setEnabled(true);
 
-      CommandI command = viewport.redoList.peek();
+      CommandI command = viewport.getRedoList().peek();
       redoMenuItem.setText(MessageManager.formatMessage(
-              "label.redo_command", new String[]
-              { command.getDescription() }));
+              "label.redo_command",
+              new Object[] { command.getDescription() }));
     }
     else
     {
@@ -1443,8 +1578,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (command.getSize() > 0)
     {
-      viewport.historyList.push(command);
-      viewport.redoList.clear();
+      viewport.addToHistoryList(command);
+      viewport.clearRedoList();
       updateEditMenuBar();
       viewport.updateHiddenColumns();
       // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
@@ -1462,18 +1597,17 @@ 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;
     }
     if (viewport != null)
     {
-      return new AlignmentI[]
-      { viewport.getAlignment() };
+      return new AlignmentI[] { viewport.getAlignment() };
     }
     return null;
   }
@@ -1487,15 +1621,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void undoMenuItem_actionPerformed(ActionEvent e)
   {
-    if (viewport.historyList.empty())
+    if (viewport.getHistoryList().isEmpty())
     {
       return;
     }
-    CommandI command = viewport.historyList.pop();
-    viewport.redoList.push(command);
+    CommandI command = viewport.getHistoryList().pop();
+    viewport.addToRedoList(command);
     command.undoCommand(getViewAlignments());
 
-    AlignViewport originalSource = getOriginatingSource(command);
+    AlignmentViewport originalSource = getOriginatingSource(command);
     updateEditMenuBar();
 
     if (originalSource != null)
@@ -1525,16 +1659,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void redoMenuItem_actionPerformed(ActionEvent e)
   {
-    if (viewport.redoList.size() < 1)
+    if (viewport.getRedoList().size() < 1)
     {
       return;
     }
 
-    CommandI command = viewport.redoList.pop();
-    viewport.historyList.push(command);
+    CommandI command = viewport.getRedoList().pop();
+    viewport.addToHistoryList(command);
     command.doCommand(getViewAlignments());
 
-    AlignViewport originalSource = getOriginatingSource(command);
+    AlignmentViewport originalSource = getOriginatingSource(command);
     updateEditMenuBar();
 
     if (originalSource != null)
@@ -1556,9 +1690,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  AlignViewport getOriginatingSource(CommandI command)
+  AlignmentViewport getOriginatingSource(CommandI command)
   {
-    AlignViewport originalSource = null;
+    AlignmentViewport originalSource = null;
     // For sequence removal and addition, we need to fire
     // the property change event FROM the viewport where the
     // original alignment was altered
@@ -1567,16 +1701,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       EditCommand editCommand = (EditCommand) command;
       al = editCommand.getAlignment();
-      Vector comps = (Vector) PaintRefresher.components.get(viewport
+      List<Component> comps = PaintRefresher.components.get(viewport
               .getSequenceSetId());
 
-      for (int i = 0; i < comps.size(); i++)
+      for (Component comp : comps)
       {
-        if (comps.elementAt(i) instanceof AlignmentPanel)
+        if (comp instanceof AlignmentPanel)
         {
-          if (al == ((AlignmentPanel) comps.elementAt(i)).av.getAlignment())
+          if (al == ((AlignmentPanel) comp).av.getAlignment())
           {
-            originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
+            originalSource = ((AlignmentPanel) comp).av;
             break;
           }
         }
@@ -1619,7 +1753,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   synchronized void slideSequences(boolean right, int size)
   {
-    List<SequenceI> sg = new Vector();
+    List<SequenceI> sg = new ArrayList<SequenceI>();
     if (viewport.cursorMode)
     {
       sg.add(viewport.getAlignment().getSequenceAt(
@@ -1638,13 +1772,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    Vector invertGroup = new Vector();
+    List<SequenceI> invertGroup = new ArrayList<SequenceI>();
 
-    for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
+    for (SequenceI seq : viewport.getAlignment().getSequences())
     {
-      if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
+      if (!sg.contains(seq))
       {
-        invertGroup.add(viewport.getAlignment().getSequenceAt(i));
+        invertGroup.add(seq);
       }
     }
 
@@ -1653,7 +1787,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
     for (int i = 0; i < invertGroup.size(); i++)
     {
-      seqs2[i] = (SequenceI) invertGroup.elementAt(i);
+      seqs2[i] = invertGroup.get(i);
     }
 
     SlideSequencesCommand ssc;
@@ -1700,12 +1834,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               viewport.getSelectionGroup().getEndRes() + groupAdjustment);
     }
 
+    /*
+     * just extend the last slide command if compatible; but not if in
+     * SplitFrame mode (to ensure all edits are broadcast - JAL-1802)
+     */
     boolean appendHistoryItem = false;
-    if (viewport.historyList != null && viewport.historyList.size() > 0
-            && viewport.historyList.peek() instanceof SlideSequencesCommand)
+    Deque<CommandI> historyList = viewport.getHistoryList();
+    boolean inSplitFrame = getSplitViewContainer() != null;
+    if (!inSplitFrame && historyList != null && historyList.size() > 0
+            && historyList.peek() instanceof SlideSequencesCommand)
     {
       appendHistoryItem = ssc
-              .appendSlideCommand((SlideSequencesCommand) viewport.historyList
+              .appendSlideCommand((SlideSequencesCommand) historyList
                       .peek());
     }
 
@@ -1742,7 +1882,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     String output = new FormatAdapter().formatSequences("Fasta", seqs,
-            omitHidden);
+            omitHidden, null);
 
     StringSelection ss = new StringSelection(output);
 
@@ -1762,30 +1902,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    Vector hiddenColumns = null;
+    ArrayList<int[]> hiddenColumns = null;
     if (viewport.hasHiddenColumns())
     {
-      hiddenColumns = new Vector();
+      hiddenColumns = new ArrayList<int[]>();
       int hiddenOffset = viewport.getSelectionGroup().getStartRes(), hiddenCutoff = viewport
               .getSelectionGroup().getEndRes();
-      for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns()
-              .size(); i++)
+      for (int[] region : viewport.getColumnSelection().getHiddenColumns())
       {
-        int[] region = (int[]) viewport.getColumnSelection()
-                .getHiddenColumns().elementAt(i);
         if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
         {
-          hiddenColumns.addElement(new int[]
-          { region[0] - hiddenOffset, region[1] - hiddenOffset });
+          hiddenColumns.add(new int[] { region[0] - hiddenOffset,
+              region[1] - hiddenOffset });
         }
       }
     }
 
-    Desktop.jalviewClipboard = new Object[]
-    { seqs, viewport.getAlignment().getDataset(), hiddenColumns };
+    Desktop.jalviewClipboard = new Object[] { seqs,
+        viewport.getAlignment().getDataset(), hiddenColumns };
     statusBar.setText(MessageManager.formatMessage(
-            "label.copied_sequences_to_clipboard", new String[]
-            { Integer.valueOf(seqs.length).toString() }));
+            "label.copied_sequences_to_clipboard", new Object[] { Integer
+                    .valueOf(seqs.length).toString() }));
   }
 
   /**
@@ -1994,8 +2131,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         //
         addHistoryItem(new EditCommand(
                 MessageManager.getString("label.add_sequences"),
-                Action.PASTE,
-                sequences, 0, alignment.getWidth(), alignment));
+                Action.PASTE, sequences, 0, alignment.getWidth(), alignment));
       }
       // Add any annotations attached to sequences
       for (int i = 0; i < sequences.length; i++)
@@ -2082,7 +2218,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);
           }
@@ -2102,10 +2238,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         if (Desktop.jalviewClipboard != null
                 && Desktop.jalviewClipboard[2] != null)
         {
-          Vector hc = (Vector) Desktop.jalviewClipboard[2];
-          for (int i = 0; i < hc.size(); i++)
+          List<int[]> hc = (List<int[]>) Desktop.jalviewClipboard[2];
+          for (int[] region : hc)
           {
-            int[] region = (int[]) hc.elementAt(i);
             af.viewport.hideColumns(region[0], region[1]);
           }
         }
@@ -2114,7 +2249,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         // found!!<<<
         af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
                 .transferSettings(
-                        alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer());
+                        alignPanel.getSeqPanel().seqCanvas
+                                .getFeatureRenderer());
 
         // TODO: maintain provenance of an alignment, rather than just make the
         // title a concatenation of operations.
@@ -2162,10 +2298,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       if (Desktop.jalviewClipboard != null
               && Desktop.jalviewClipboard[2] != null)
       {
-        Vector hc = (Vector) Desktop.jalviewClipboard[2];
-        for (int i = 0; i < hc.size(); i++)
+        List<int[]> hc = (List<int[]>) Desktop.jalviewClipboard[2];
+        for (int region[] : hc)
         {
-          int[] region = (int[]) hc.elementAt(i);
           af.viewport.hideColumns(region[0], region[1]);
         }
       }
@@ -2174,7 +2309,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // found!!<<<
       af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
               .transferSettings(
-                      alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer());
+                      alignPanel.getSeqPanel().seqCanvas
+                              .getFeatureRenderer());
 
       // TODO: maintain provenance of an alignment, rather than just make the
       // title a concatenation of operations.
@@ -2231,15 +2367,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    List<SequenceI> seqs = new ArrayList<SequenceI>(sg.getSize());
-    SequenceI seq;
-    for (int i = 0; i < sg.getSize(); i++)
-    {
-      seq = sg.getSequenceAt(i);
-      seqs.add(seq);
-    }
-
-    // If the cut affects all sequences, warn, remove highlighted columns
+    /*
+     * If the cut affects all sequences, warn, remove highlighted columns
+     */
     if (sg.getSize() == viewport.getAlignment().getHeight())
     {
       int confirm = JOptionPane.showConfirmDialog(this,
@@ -2256,15 +2386,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               sg.getEndRes() + 1);
     }
 
-    SequenceI[] cut = new SequenceI[seqs.size()];
-    for (int i = 0; i < seqs.size(); i++)
-    {
-      cut[i] = seqs.get(i);
-    }
+    SequenceI[] cut = sg.getSequences()
+            .toArray(new SequenceI[sg.getSize()]);
 
-    /*
-     * //ADD HISTORY ITEM
-     */
     addHistoryItem(new EditCommand(
             MessageManager.getString("label.cut_sequences"), Action.CUT,
             cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
@@ -2456,8 +2580,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       }
 
       statusBar.setText(MessageManager.formatMessage(
-              "label.removed_columns", new String[]
-              { Integer.valueOf(trimRegion.getSize()).toString() }));
+              "label.removed_columns",
+              new String[] { Integer.valueOf(trimRegion.getSize())
+                      .toString() }));
 
       addHistoryItem(trimRegion);
 
@@ -2506,8 +2631,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     addHistoryItem(removeGapCols);
 
     statusBar.setText(MessageManager.formatMessage(
-            "label.removed_empty_columns", new String[]
-            { Integer.valueOf(removeGapCols.getSize()).toString() }));
+            "label.removed_empty_columns",
+            new Object[] { Integer.valueOf(removeGapCols.getSize())
+                    .toString() }));
 
     // This is to maintain viewport position on first residue
     // of first sequence
@@ -2577,16 +2703,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             .getSequences());
   }
 
-  // else
-  {
-    // if (justifySeqs>0)
-    {
-      // alignment.justify(justifySeqs!=RIGHT_JUSTIFY);
-    }
-  }
-
-  // }
-
   /**
    * DOCUMENT ME!
    * 
@@ -2599,74 +2715,85 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     new Finder();
   }
 
-  @Override
-  public void newView_actionPerformed(ActionEvent e)
-  {
-    newView(true);
-  }
-
-  /**
-   * 
-   * @param copyAnnotation
-   *          if true then duplicate all annnotation, groups and settings
-   * @return new alignment panel, already displayed.
-   */
-  public AlignmentPanel newView(boolean copyAnnotation)
-  {
-    return newView(null, copyAnnotation);
-  }
-
   /**
-   * 
-   * @param viewTitle
-   *          title of newly created view
-   * @return new alignment panel, already displayed.
+   * Create a new view of the current alignment.
    */
-  public AlignmentPanel newView(String viewTitle)
+  @Override
+  public void newView_actionPerformed(ActionEvent e)
   {
-    return newView(viewTitle, true);
+    newView(null, true);
   }
 
   /**
+   * Creates and shows a new view of the current alignment.
    * 
    * @param viewTitle
-   *          title of newly created view
+   *          title of newly created view; if null, one will be generated
    * @param copyAnnotation
    *          if true then duplicate all annnotation, groups and settings
    * @return new alignment panel, already displayed.
    */
   public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
   {
+    /*
+     * Create a new AlignmentPanel (with its own, new Viewport)
+     */
     AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
             true);
     if (!copyAnnotation)
     {
-      // just remove all the current annotation except for the automatic stuff
+      /*
+       * remove all groups and annotation except for the automatic stuff
+       */
       newap.av.getAlignment().deleteAllGroups();
-      for (AlignmentAnnotation alan : newap.av.getAlignment()
-              .getAlignmentAnnotation())
-      {
-        if (!alan.autoCalculated)
-        {
-          newap.av.getAlignment().deleteAnnotation(alan);
-        }
-        ;
-      }
+      newap.av.getAlignment().deleteAllAnnotations(false);
     }
 
-    newap.av.gatherViewsHere = false;
+    newap.av.setGatherViewsHere(false);
 
     if (viewport.viewName == null)
     {
-      viewport.viewName = "Original";
+      viewport.viewName = MessageManager
+              .getString("label.view_name_original");
     }
 
-    newap.av.historyList = viewport.historyList;
-    newap.av.redoList = viewport.redoList;
+    /*
+     * Views share the same edits undo and redo stacks
+     */
+    newap.av.setHistoryList(viewport.getHistoryList());
+    newap.av.setRedoList(viewport.getRedoList());
+
+    /*
+     * Views share the same mappings; need to deregister any new mappings
+     * created by copyAlignPanel, and register the new reference to the shared
+     * mappings
+     */
+    newap.av.replaceMappings(viewport.getAlignment());
+
+    newap.av.viewName = getNewViewName(viewTitle);
+
+    addAlignmentPanel(newap, true);
+    newap.alignmentChanged();
+
+    if (alignPanels.size() == 2)
+    {
+      viewport.setGatherViewsHere(true);
+    }
+    tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
+    return newap;
+  }
 
+  /**
+   * Make a new name for the view, ensuring it is unique within the current
+   * sequenceSetId. (This used to be essential for Jalview Project archives, but
+   * these now use viewId. Unique view names are still desirable for usability.)
+   * 
+   * @param viewTitle
+   * @return
+   */
+  protected String getNewViewName(String viewTitle)
+  {
     int index = Desktop.getViewCount(viewport.getSequenceSetId());
-    // make sure the new view has a unique name - this is essential for Jalview
-    // 2 archives
     boolean addFirstIndex = false;
     if (viewTitle == null || viewTitle.trim().length() == 0)
     {
@@ -2678,45 +2805,55 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       index = 1;// we count from 1 if given a specific name
     }
     String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
-    Vector comps = (Vector) PaintRefresher.components.get(viewport
+
+    List<Component> comps = PaintRefresher.components.get(viewport
             .getSequenceSetId());
-    Vector existingNames = new Vector();
-    for (int i = 0; i < comps.size(); i++)
-    {
-      if (comps.elementAt(i) instanceof AlignmentPanel)
-      {
-        AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i);
-        if (!existingNames.contains(ap.av.viewName))
-        {
-          existingNames.addElement(ap.av.viewName);
-        }
-      }
-    }
+
+    List<String> existingNames = getExistingViewNames(comps);
 
     while (existingNames.contains(newViewName))
     {
       newViewName = viewTitle + " " + (++index);
     }
+    return newViewName;
+  }
 
-    newap.av.viewName = newViewName;
-
-    addAlignmentPanel(newap, true);
-    newap.alignmentChanged();
-
-    if (alignPanels.size() == 2)
+  /**
+   * Returns a list of distinct view names found in the given list of
+   * components. View names are held on the viewport of an AlignmentPanel.
+   * 
+   * @param comps
+   * @return
+   */
+  protected List<String> getExistingViewNames(List<Component> comps)
+  {
+    List<String> existingNames = new ArrayList<String>();
+    for (Component comp : comps)
     {
-      viewport.gatherViewsHere = true;
+      if (comp instanceof AlignmentPanel)
+      {
+        AlignmentPanel ap = (AlignmentPanel) comp;
+        if (!existingNames.contains(ap.av.viewName))
+        {
+          existingNames.add(ap.av.viewName);
+        }
+      }
     }
-    tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
-    return newap;
+    return existingNames;
   }
 
+  /**
+   * Explode tabbed views into separate windows.
+   */
   @Override
   public void expandViews_actionPerformed(ActionEvent e)
   {
     Desktop.instance.explodeViews(this);
   }
 
+  /**
+   * Gather views in separate windows back into a tabbed presentation.
+   */
   @Override
   public void gatherViews_actionPerformed(ActionEvent e)
   {
@@ -2746,8 +2883,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.setShowJVSuffix(seqLimits.isSelected());
 
-    alignPanel.getIdPanel().getIdCanvas().setPreferredSize(alignPanel
-            .calculateIdWidth());
+    alignPanel.getIdPanel().getIdCanvas()
+            .setPreferredSize(alignPanel.calculateIdWidth());
     alignPanel.paintAlignment(true);
   }
 
@@ -2761,7 +2898,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void centreColumnLabels_actionPerformed(ActionEvent e)
   {
-    viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
+    viewport.setCentreColumnLabels(centreColumnLabelsMenuItem.getState());
     alignPanel.paintAlignment(true);
   }
 
@@ -2773,7 +2910,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void followHighlight_actionPerformed()
   {
-    if (viewport.followHighlight = this.followHighlightMenuItem.getState())
+    /*
+     * Set the 'follow' flag on the Viewport (and scroll to position if now
+     * true).
+     */
+    final boolean state = this.followHighlightMenuItem.getState();
+    viewport.setFollowHighlight(state);
+    if (state)
     {
       alignPanel.scrollToPosition(
               alignPanel.getSeqPanel().seqCanvas.searchResults, false);
@@ -2806,7 +2949,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     scaleLeft.setVisible(wrapMenuItem.isSelected());
     scaleRight.setVisible(wrapMenuItem.isSelected());
     viewport.setWrapAlignment(wrapMenuItem.isSelected());
-    alignPanel.setWrapAlignment(wrapMenuItem.isSelected());
+    alignPanel.updateLayout();
   }
 
   @Override
@@ -2826,7 +2969,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void hideSelSequences_actionPerformed(ActionEvent e)
   {
     viewport.hideAllSelectedSeqs();
-    alignPanel.paintAlignment(true);
+    // alignPanel.paintAlignment(true);
   }
 
   /**
@@ -3039,6 +3182,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public FeatureSettings featureSettings;
 
   @Override
+  public FeatureSettingsControllerI getFeatureSettingsUI()
+  {
+    return featureSettings;
+  }
+
+  @Override
   public void featureSettings_actionPerformed(ActionEvent e)
   {
     if (featureSettings != null)
@@ -3083,7 +3232,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
             .isSelected());
-    if (viewport.getShowSequenceFeaturesHeight())
+    if (viewport.isShowSequenceFeaturesHeight())
     {
       // ensure we're actually displaying features
       viewport.setShowSequenceFeatures(true);
@@ -3110,11 +3259,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     final boolean setVisible = annotationPanelMenuItem.isSelected();
     viewport.setShowAnnotation(setVisible);
-    alignPanel.setAnnotationVisible(setVisible);
     this.showAllSeqAnnotations.setEnabled(setVisible);
     this.hideAllSeqAnnotations.setEnabled(setVisible);
     this.showAllAlAnnotations.setEnabled(setVisible);
     this.hideAllAlAnnotations.setEnabled(setVisible);
+    alignPanel.updateLayout();
   }
 
   @Override
@@ -3125,14 +3274,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
             .formatAsHtml();
     editPane.setText(MessageManager.formatMessage("label.html_content",
-            new String[]
-            { contents.toString() }));
+            new Object[] { contents.toString() }));
     JInternalFrame frame = new JInternalFrame();
     frame.getContentPane().add(new JScrollPane(editPane));
 
-    Desktop.instance.addInternalFrame(frame, MessageManager.formatMessage(
-            "label.alignment_properties", new String[]
-            { getTitle() }), 500, 400);
+    Desktop.addInternalFrame(frame, MessageManager.formatMessage(
+            "label.alignment_properties", new Object[] { getTitle() }),
+            500, 400);
   }
 
   /**
@@ -3153,8 +3301,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     OverviewPanel overview = new OverviewPanel(alignPanel);
     frame.setContentPane(overview);
     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
-            "label.overview_params", new String[]
-            { this.getTitle() }), frame.getWidth(), frame.getHeight());
+            "label.overview_params", new Object[] { this.getTitle() }),
+            frame.getWidth(), frame.getHeight());
     frame.pack();
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
@@ -3316,6 +3464,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   @Override
+  public void annotationColumn_actionPerformed(ActionEvent e)
+  {
+    new AnnotationColumnChooser(viewport, alignPanel);
+  }
+
+  @Override
   public void rnahelicesColour_actionPerformed(ActionEvent e)
   {
     new RNAHelicesColourChooser(viewport, alignPanel);
@@ -3341,115 +3495,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   public void changeColour(ColourSchemeI cs)
   {
-    // TODO: compare with applet and pull up to model method
-    int threshold = 0;
+    // TODO: pull up to controller method
 
     if (cs != null)
     {
+      // Make sure viewport is up to date w.r.t. any sliders
       if (viewport.getAbovePIDThreshold())
       {
-        threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
+        int threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
                 "Background");
-        cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
-      }
-      else
-      {
-        cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
+        viewport.setThreshold(threshold);
       }
 
       if (viewport.getConservationSelected())
       {
-
-        Alignment al = (Alignment) viewport.getAlignment();
-        Conservation c = new Conservation("All",
-                ResidueProperties.propHash, 3, al.getSequences(), 0,
-                al.getWidth() - 1);
-
-        c.calculate();
-        c.verdict(false, viewport.getConsPercGaps());
-
-        cs.setConservation(c);
-
         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
                 cs, "Background"));
       }
-      else
-      {
-        cs.setConservation(null);
-      }
-
-      cs.setConsensus(viewport.getSequenceConsensusHash());
     }
 
     viewport.setGlobalColourScheme(cs);
 
-    if (viewport.getColourAppliesToAllGroups())
-    {
-
-      for (SequenceGroup sg : viewport.getAlignment().getGroups())
-      {
-        if (cs == null)
-        {
-          sg.cs = null;
-          continue;
-        }
-
-        if (cs instanceof ClustalxColourScheme)
-        {
-          sg.cs = new ClustalxColourScheme(sg,
-                  viewport.getHiddenRepSequences());
-        }
-        else if (cs instanceof UserColourScheme)
-        {
-          sg.cs = new UserColourScheme(((UserColourScheme) cs).getColours());
-        }
-        else
-        {
-          try
-          {
-            sg.cs = cs.getClass().newInstance();
-          } catch (Exception ex)
-          {
-          }
-        }
-
-        if (viewport.getAbovePIDThreshold()
-                || cs instanceof PIDColourScheme
-                || cs instanceof Blosum62ColourScheme)
-        {
-          sg.cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
-
-          sg.cs.setConsensus(AAFrequency.calculate(
-                  sg.getSequences(viewport.getHiddenRepSequences()),
-                  sg.getStartRes(), sg.getEndRes() + 1));
-        }
-        else
-        {
-          sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
-        }
-
-        if (viewport.getConservationSelected())
-        {
-          Conservation c = new Conservation("Group",
-                  ResidueProperties.propHash, 3, sg.getSequences(viewport
-                          .getHiddenRepSequences()), sg.getStartRes(),
-                  sg.getEndRes() + 1);
-          c.calculate();
-          c.verdict(false, viewport.getConsPercGaps());
-          sg.cs.setConservation(c);
-        }
-        else
-        {
-          sg.cs.setConservation(null);
-        }
-      }
-    }
-
-    if (alignPanel.getOverviewPanel() != null)
-    {
-      alignPanel.getOverviewPanel().updateOverviewImage();
-    }
-
     alignPanel.paintAlignment(true);
   }
 
@@ -3554,8 +3620,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
 
     Component[] menuItems = colourMenu.getMenuComponents();
-    int i, iSize = menuItems.length;
-    for (i = 0; i < iSize; i++)
+    int iSize = menuItems.length;
+    for (int i = 0; i < iSize; i++)
     {
       if (menuItems[i].getName() != null
               && menuItems[i].getName().equals("USER_DEFINED"))
@@ -3816,7 +3882,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("AV", "PID", "Average distance tree using PID");
+    newTreePanel("AV", "PID", "Average distance tree using PID");
   }
 
   /**
@@ -3828,7 +3894,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
+    newTreePanel("NJ", "PID", "Neighbour joining tree using PID");
   }
 
   /**
@@ -3840,7 +3906,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
+    newTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
   }
 
   /**
@@ -3852,7 +3918,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
+    newTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
   }
 
   /**
@@ -3865,7 +3931,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param title
    *          DOCUMENT ME!
    */
-  void NewTreePanel(String type, String pwType, String title)
+  void newTreePanel(String type, String pwType, String title)
   {
     TreePanel tp;
 
@@ -3956,7 +4022,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void addSortByOrderMenuItem(String title,
           final AlignmentOrder order)
   {
-    final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new String[]{title}));
+    final JMenuItem item = new JMenuItem(MessageManager.formatMessage(
+            "action.by_title_param", new Object[] { title }));
     sort.add(item);
     item.addActionListener(new java.awt.event.ActionListener()
     {
@@ -4075,12 +4142,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     calculateTree.removeAll();
     // build the calculate menu
 
-    for (final String type : new String[]
-    { "NJ", "AV" })
+    for (final String type : new String[] { "NJ", "AV" })
     {
       String treecalcnm = MessageManager.getString("label.tree_calc_"
               + type.toLowerCase());
-      for (final Object pwtype : ResidueProperties.scoreMatrices.keySet())
+      for (final String pwtype : ResidueProperties.scoreMatrices.keySet())
       {
         JMenuItem tm = new JMenuItem();
         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
@@ -4096,7 +4162,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             @Override
             public void actionPerformed(ActionEvent e)
             {
-              NewTreePanel(type, (String) pwtype, title);
+              newTreePanel(type, pwtype, title);
             }
           });
           calculateTree.add(tm);
@@ -4106,21 +4172,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     sortByTreeMenu.removeAll();
 
-    Vector comps = (Vector) PaintRefresher.components.get(viewport
+    List<Component> comps = PaintRefresher.components.get(viewport
             .getSequenceSetId());
-    Vector treePanels = new Vector();
-    int i, iSize = comps.size();
-    for (i = 0; i < iSize; i++)
+    List<TreePanel> treePanels = new ArrayList<TreePanel>();
+    for (Component comp : comps)
     {
-      if (comps.elementAt(i) instanceof TreePanel)
+      if (comp instanceof TreePanel)
       {
-        treePanels.add(comps.elementAt(i));
+        treePanels.add((TreePanel) comp);
       }
     }
 
-    iSize = treePanels.size();
-
-    if (iSize < 1)
+    if (treePanels.size() < 1)
     {
       sortByTreeMenu.setVisible(false);
       return;
@@ -4128,17 +4191,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     sortByTreeMenu.setVisible(true);
 
-    for (i = 0; i < treePanels.size(); i++)
+    for (final TreePanel tp : treePanels)
     {
-      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));
 
         }
@@ -4188,28 +4249,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     else if (viewport.getSelectionGroup() != null
             && viewport.getSelectionGroup().getSize() == 1)
     {
-      int option = JOptionPane
-              .showConfirmDialog(
-this,
-                      "More than one sequece group selection is required for this Job, click \n'Cancel' to edit your selection or 'Ok' to submit the entire sequence.",
-                      "Invalid selection",
-                      JOptionPane.OK_CANCEL_OPTION);
+      int option = JOptionPane.showConfirmDialog(this,
+              MessageManager.getString("warn.oneseq_msainput_selection"),
+              MessageManager.getString("label.invalid_selection"),
+              JOptionPane.OK_CANCEL_OPTION);
       if (option == JOptionPane.OK_OPTION)
       {
         msa = viewport.getAlignmentView(false);
       }
-
     }
     else
     {
-      /*
-       * Vector seqs = viewport.getAlignment().getSequences();
-       * 
-       * if (seqs.size() > 1) { msa = new SequenceI[seqs.size()];
-       * 
-       * for (int i = 0; i < seqs.size(); i++) { msa[i] = (SequenceI)
-       * seqs.elementAt(i); } }
-       */
       msa = viewport.getAlignmentView(false);
     }
     return msa;
@@ -4241,8 +4291,7 @@ this,
     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
     if (!viewport.getAlignment().isAligned(false))
     {
-      seqs.setSequences(new SeqCigar[]
-      { seqs.getSequences()[0] });
+      seqs.setSequences(new SeqCigar[] { seqs.getSequences()[0] });
       // TODO: if seqs.getSequences().length>1 then should really have warned
       // user!
 
@@ -4257,7 +4306,7 @@ this,
    *          DOCUMENT ME!
    */
   @Override
-  protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
+  protected void loadTreeMenuItem_actionPerformed(ActionEvent e)
   {
     // Pick the tree file
     JalviewFileChooser chooser = new JalviewFileChooser(
@@ -4389,7 +4438,6 @@ this,
       } catch (Exception e)
       {
       }
-      ;
     }
     final AlignFrame me = this;
     buildingMenu = true;
@@ -4401,8 +4449,8 @@ this,
         final List<JMenuItem> legacyItems = new ArrayList<JMenuItem>();
         try
         {
-          System.err.println("Building ws menu again "
-                  + Thread.currentThread());
+          // System.err.println("Building ws menu again "
+          // + Thread.currentThread());
           // TODO: add support for context dependent disabling of services based
           // on
           // alignment and current selection
@@ -4544,14 +4592,11 @@ this,
                         .debug("Exception during web service menu building process.",
                                 e);
               }
-              ;
             }
           });
         } catch (Exception e)
         {
         }
-        ;
-
         buildingMenu = false;
       }
     }).start();
@@ -4663,7 +4708,7 @@ this,
           public void actionPerformed(ActionEvent e)
           {
             // TODO: new thread for this call with vis-delay
-            af.showProductsFor(af.viewport.getSequenceSelection(), ds,
+            af.showProductsFor(af.viewport.getSequenceSelection(),
                     isRegSel, dna, source);
           }
 
@@ -4682,14 +4727,9 @@ this,
     return showp;
   }
 
-  protected void showProductsFor(SequenceI[] sel, Alignment ds,
-          boolean isRegSel, boolean dna, String source)
+  protected void showProductsFor(final SequenceI[] sel,
+          final boolean isRegSel, final boolean dna, final String source)
   {
-    final boolean fisRegSel = isRegSel;
-    final boolean fdna = dna;
-    final String fsrc = source;
-    final AlignFrame ths = this;
-    final SequenceI[] fsel = sel;
     Runnable foo = new Runnable()
     {
 
@@ -4697,15 +4737,16 @@ this,
       public void run()
       {
         final long sttime = System.currentTimeMillis();
-        ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new String[]{fsrc}), sttime);
+        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
+                "status.searching_for_sequences_from",
+                new Object[] { source }), sttime);
         try
         {
-          Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
-          // our local
-          // dataset
-          // reference
+          // update our local dataset reference
+          Alignment ds = AlignFrame.this.getViewport().getAlignment()
+                  .getDataset();
           Alignment prods = CrossRef
-                  .findXrefSequences(fsel, fdna, fsrc, ds);
+                  .findXrefSequences(sel, dna, source, ds);
           if (prods != null)
           {
             SequenceI[] sprods = new SequenceI[prods.getHeight()];
@@ -4721,25 +4762,75 @@ this,
               sprods[s].updatePDBIds();
             }
             Alignment al = new Alignment(sprods);
-            AlignedCodonFrame[] cf = prods.getCodonFrames();
             al.setDataset(ds);
-            for (int s = 0; cf != null && s < cf.length; s++)
+
+            /*
+             * Copy dna-to-protein mappings to new alignment
+             */
+            // TODO 1: no mappings are set up for EMBL product
+            // TODO 2: if they were, should add them to protein alignment, not
+            // dna
+            Set<AlignedCodonFrame> cf = prods.getCodonFrames();
+            for (AlignedCodonFrame acf : cf)
             {
-              al.addCodonFrame(cf[s]);
-              cf[s] = null;
+              al.addCodonFrame(acf);
             }
             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
                     DEFAULT_HEIGHT);
-            String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
-                    + " for " + ((fisRegSel) ? "selected region of " : "")
+            String newtitle = "" + ((dna) ? "Proteins" : "Nucleotides")
+                    + " for " + ((isRegSel) ? "selected region of " : "")
                     + getTitle();
-            Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
-                    DEFAULT_HEIGHT);
+            naf.setTitle(newtitle);
+
+            // temporary flag until SplitFrame is released
+            boolean asSplitFrame = Cache.getDefault(
+                    Preferences.ENABLE_SPLIT_FRAME, true);
+            if (asSplitFrame)
+            {
+              /*
+               * Make a copy of this alignment (sharing the same dataset
+               * sequences). If we are DNA, drop introns and update mappings
+               */
+              AlignmentI copyAlignment = null;
+              final SequenceI[] sequenceSelection = AlignFrame.this.viewport
+                      .getSequenceSelection();
+              if (dna)
+              {
+                copyAlignment = AlignmentUtils.makeExonAlignment(
+                        sequenceSelection, cf);
+                al.getCodonFrames().clear();
+                al.getCodonFrames().addAll(cf);
+                final StructureSelectionManager ssm = StructureSelectionManager
+                        .getStructureSelectionManager(Desktop.instance);
+                ssm.registerMappings(cf);
+              }
+              else
+              {
+                copyAlignment = new Alignment(new Alignment(
+                        sequenceSelection));
+              }
+              AlignFrame copyThis = new AlignFrame(copyAlignment,
+                      AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+              copyThis.setTitle(AlignFrame.this.getTitle());
+              // SplitFrame with dna above, protein below
+              SplitFrame sf = new SplitFrame(dna ? copyThis : naf,
+                      dna ? naf : copyThis);
+              naf.setVisible(true);
+              copyThis.setVisible(true);
+              String linkedTitle = MessageManager
+                      .getString("label.linked_view_title");
+              Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
+            }
+            else
+            {
+              Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
+                      DEFAULT_HEIGHT);
+            }
           }
           else
           {
             System.err.println("No Sequences generated for xRef type "
-                    + fsrc);
+                    + source);
           }
         } catch (Exception e)
         {
@@ -4753,8 +4844,9 @@ this,
           jalview.bin.Cache.log.error("Error when finding crossreferences",
                   e);
         }
-        ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new String[]{fsrc}),
-                sttime);
+        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
+                "status.finished_searching_for_sequences_from",
+                new Object[] { source }), sttime);
       }
 
     };
@@ -4779,135 +4871,100 @@ this,
     }
   }
 
-  @Override
-  public void showProducts_actionPerformed(ActionEvent e)
-  {
-    // /////////////////////////////
-    // Collect Data to be translated/transferred
-
-    SequenceI[] selection = viewport.getSequenceSelection();
-    AlignmentI al = null;
-    try
-    {
-      al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
-              .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
-              viewport.getAlignment().getDataset());
-    } catch (Exception ex)
-    {
-      al = null;
-      jalview.bin.Cache.log.debug("Exception during translation.", ex);
-    }
-    if (al == null)
-    {
-      JOptionPane
-              .showMessageDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
-                      MessageManager.getString("label.translation_failed"),
-                      JOptionPane.WARNING_MESSAGE);
-    }
-    else
-    {
-      AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-      Desktop.addInternalFrame(af, MessageManager.formatMessage(
-              "label.translation_of_params", new String[]
-              { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
-    }
-  }
-
+  /**
+   * Construct and display a new frame containing the translation of this
+   * frame's DNA sequences to their aligned protein (amino acid) equivalents.
+   */
   @Override
   public void showTranslation_actionPerformed(ActionEvent e)
   {
-    // /////////////////////////////
-    // Collect Data to be translated/transferred
-
-    SequenceI[] selection = viewport.getSequenceSelection();
-    String[] seqstring = viewport.getViewAsString(true);
     AlignmentI al = null;
     try
     {
-      al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
-              viewport.getViewAsVisibleContigs(true), viewport
-                      .getGapCharacter(), viewport.getAlignment()
-                      .getAlignmentAnnotation(), viewport.getAlignment()
-                      .getWidth(), viewport.getAlignment().getDataset());
+      Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
+
+      al = dna.translateCdna();
     } catch (Exception ex)
     {
-      al = null;
       jalview.bin.Cache.log.error(
               "Exception during translation. Please report this !", ex);
-      JOptionPane
-              .showMessageDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.error_when_translating_sequences_submit_bug_report"),
-                      MessageManager
-                              .getString("label.implementation_error")
-                              + MessageManager
-                                      .getString("translation_failed"),
-                      JOptionPane.ERROR_MESSAGE);
+      final String msg = MessageManager
+              .getString("label.error_when_translating_sequences_submit_bug_report");
+      final String errorTitle = MessageManager
+              .getString("label.implementation_error")
+              + MessageManager.getString("translation_failed");
+      JOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
+              JOptionPane.ERROR_MESSAGE);
       return;
     }
-    if (al == null)
+    if (al == null || al.getHeight() == 0)
     {
-      JOptionPane
-              .showMessageDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
-                      MessageManager.getString("label.translation_failed"),
-                      JOptionPane.WARNING_MESSAGE);
+      final String msg = MessageManager
+              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
+      final String errorTitle = MessageManager
+              .getString("label.translation_failed");
+      JOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
+              JOptionPane.WARNING_MESSAGE);
     }
     else
     {
       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-      Desktop.addInternalFrame(af, MessageManager.formatMessage(
-              "label.translation_of_params", new String[]
-              { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
+      af.setFileFormat(this.currentFileFormat);
+      final String newTitle = MessageManager.formatMessage(
+              "label.translation_of_params",
+              new Object[] { this.getTitle() });
+      af.setTitle(newTitle);
+      if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
+      {
+        final SequenceI[] seqs = viewport.getSelectionAsNewSequence();
+        viewport.openSplitFrame(af, new Alignment(seqs));
+      }
+      else
+      {
+        Desktop.addInternalFrame(af, newTitle, DEFAULT_WIDTH,
+                DEFAULT_HEIGHT);
+      }
     }
   }
 
   /**
+   * Set the file format
+   * 
+   * @param fileFormat
+   */
+  public void setFileFormat(String fileFormat)
+  {
+    this.currentFileFormat = fileFormat;
+  }
+
+  /**
    * Try to load a features file onto the alignment.
    * 
    * @param file
    *          contents or path to retrieve file
    * @param type
    *          access mode of file (see jalview.io.AlignFile)
-   * @return true if features file was parsed corectly.
+   * @return true if features file was parsed correctly.
    */
   public boolean parseFeaturesFile(String file, String type)
   {
-    boolean featuresFile = false;
-    try
-    {
-      featuresFile = new FeaturesFile(file, type).parse(viewport
-              .getAlignment().getDataset(), alignPanel.getSeqPanel().seqCanvas
-              .getFeatureRenderer().featureColours, false,
-              jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
-    }
+    return avc.parseFeaturesFile(file, type,
+            jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
 
-    if (featuresFile)
+  }
+
+  @Override
+  public void refreshFeatureUI(boolean enableIfNecessary)
+  {
+    // note - currently this is only still here rather than in the controller
+    // because of the featureSettings hard reference that is yet to be
+    // abstracted
+    if (enableIfNecessary)
     {
-      viewport.showSequenceFeatures = true;
+      viewport.setShowSequenceFeatures(true);
       showSeqFeatures.setSelected(true);
-      if (alignPanel.getSeqPanel().seqCanvas.fr != null)
-      {
-        // update the min/max ranges where necessary
-        alignPanel.getSeqPanel().seqCanvas.fr.findAllFeatures(true);
-      }
-      if (featureSettings != null)
-      {
-        featureSettings.setTableData();
-      }
-      alignPanel.paintAlignment(true);
     }
 
-    return featuresFile;
   }
 
   @Override
@@ -5040,8 +5097,7 @@ this,
               {
                 if (type.equalsIgnoreCase("PDB"))
                 {
-                  filesmatched.add(new Object[]
-                  { file, protocol, mtch });
+                  filesmatched.add(new Object[] { file, protocol, mtch });
                   continue;
                 }
               }
@@ -5060,10 +5116,10 @@ this,
                                   MessageManager
                                           .formatMessage(
                                                   "label.automatically_associate_pdb_files_with_sequences_same_name",
-                                                  new String[]
-                                                  { Integer.valueOf(
-                                                          filesmatched
-                                                                  .size())
+                                                  new Object[] { Integer
+                                                          .valueOf(
+                                                                  filesmatched
+                                                                          .size())
                                                           .toString() }),
                                   MessageManager
                                           .getString("label.automatically_associate_pdb_files_by_name"),
@@ -5100,14 +5156,16 @@ this,
                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
                           .showConfirmDialog(
                                   this,
-                                  "<html>"+MessageManager
-                                          .formatMessage(
-                                                  "label.ignore_unmatched_dropped_files_info",
-                                                  new String[]
-                                                  { Integer.valueOf(
-                                                          filesnotmatched
-                                                                  .size())
-                                                          .toString() })+"</html>",
+                                  "<html>"
+                                          + MessageManager
+                                                  .formatMessage(
+                                                          "label.ignore_unmatched_dropped_files_info",
+                                                          new Object[] { Integer
+                                                                  .valueOf(
+                                                                          filesnotmatched
+                                                                                  .size())
+                                                                  .toString() })
+                                          + "</html>",
                                   MessageManager
                                           .getString("label.ignore_unmatched_dropped_files"),
                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
@@ -5150,8 +5208,7 @@ this,
       // try to parse as annotation.
       boolean isAnnotation = (format == null || format
               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
-              .readAnnotationFile(viewport.getAlignment(), file, protocol)
-              : false;
+              .annotateAlignmentView(viewport, file, protocol) : false;
 
       if (!isAnnotation)
       {
@@ -5211,8 +5268,14 @@ this,
           {
             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
                     file, protocol);
-            new JnetAnnotationMaker().add_annotation(predictions,
+            new JnetAnnotationMaker();
+            JnetAnnotationMaker.add_annotation(predictions,
                     viewport.getAlignment(), 0, false);
+            SequenceI repseq = viewport.getAlignment().getSequenceAt(0);
+            viewport.getAlignment().setSeqrep(repseq);
+            ColumnSelection cs = new ColumnSelection();
+            cs.hideInsertionsFor(repseq);
+            viewport.setColumnSelection(cs);
             isAnnotation = true;
           }
           else
@@ -5283,31 +5346,55 @@ this,
     }
   }
 
+  /**
+   * Method invoked by the ChangeListener on the tabbed pane, in other words
+   * when a different tabbed pane is selected by the user or programmatically.
+   */
   @Override
   public void tabSelectionChanged(int index)
   {
     if (index > -1)
     {
-      alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
+      alignPanel = alignPanels.get(index);
       viewport = alignPanel.av;
       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
       setMenusFromViewport(viewport);
     }
+
+    /*
+     * If there is a frame linked to this one in a SplitPane, switch it to the
+     * same view tab index. No infinite recursion of calls should happen, since
+     * tabSelectionChanged() should not get invoked on setting the selected
+     * index to an unchanged value. Guard against setting an invalid index
+     * before the new view peer tab has been created.
+     */
+    final AlignViewportI peer = viewport.getCodingComplement();
+    if (peer != null)
+    {
+      AlignFrame linkedAlignFrame = ((AlignViewport) peer).getAlignPanel().alignFrame;
+      if (linkedAlignFrame.tabbedPane.getTabCount() > index)
+      {
+        linkedAlignFrame.tabbedPane.setSelectedIndex(index);
+      }
+    }
   }
 
+  /**
+   * On right mouse click on view tab, prompt for and set new view name.
+   */
   @Override
   public void tabbedPane_mousePressed(MouseEvent e)
   {
     if (SwingUtilities.isRightMouseButton(e))
     {
-      String reply = JOptionPane.showInternalInputDialog(this,
-              MessageManager.getString("label.enter_view_name"),
-              MessageManager.getString("label.enter_view_name"),
+      String msg = MessageManager.getString("label.enter_view_name");
+      String reply = JOptionPane.showInternalInputDialog(this, msg, msg,
               JOptionPane.QUESTION_MESSAGE);
 
       if (reply != null)
       {
         viewport.viewName = reply;
+        // TODO warn if reply is in getExistingViewNames()?
         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
       }
     }
@@ -5347,7 +5434,7 @@ this,
   @Override
   protected void showDbRefs_actionPerformed(ActionEvent e)
   {
-    viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
+    viewport.setShowDBRefs(showDbRefsMenuitem.isSelected());
   }
 
   /*
@@ -5359,7 +5446,7 @@ this,
   @Override
   protected void showNpFeats_actionPerformed(ActionEvent e)
   {
-    viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
+    viewport.setShowNPFeats(showNpFeatsMenuitem.isSelected());
   }
 
   /**
@@ -5368,7 +5455,7 @@ this,
    * 
    * @param av
    */
-  public boolean closeView(AlignViewport av)
+  public boolean closeView(AlignViewportI av)
   {
     if (viewport == av)
     {
@@ -5514,7 +5601,10 @@ this,
                   }
 
                 });
-                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{src.getDbName()})));
+                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true,
+                        MessageManager.formatMessage(
+                                "label.fetch_retrieve_from",
+                                new Object[] { src.getDbName() })));
                 dfetch.add(fetchr);
                 comp++;
               }
@@ -5525,8 +5615,8 @@ this,
                 // fetch all entry
                 DbSourceProxy src = otherdb.get(0);
                 fetchr = new JMenuItem(MessageManager.formatMessage(
-                        "label.fetch_all_param", new String[]
-                        { src.getDbSource() }));
+                        "label.fetch_all_param",
+                        new Object[] { src.getDbSource() }));
                 fetchr.addActionListener(new ActionListener()
                 {
                   @Override
@@ -5547,11 +5637,19 @@ this,
                   }
                 });
 
-                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new String[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
+                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true,
+                        MessageManager.formatMessage(
+                                "label.fetch_retrieve_from_all_sources",
+                                new Object[] {
+                                    Integer.valueOf(otherdb.size())
+                                            .toString(), src.getDbSource(),
+                                    src.getDbName() })));
                 dfetch.add(fetchr);
                 comp++;
                 // and then build the rest of the individual menus
-                ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new String[]{src.getDbSource()}));
+                ifetch = new JMenu(MessageManager.formatMessage(
+                        "label.source_from_db_source",
+                        new Object[] { src.getDbSource() }));
                 icomp = 0;
                 String imname = null;
                 int i = 0;
@@ -5564,11 +5662,11 @@ this,
                           0, 10) + "..." : dbname;
                   if (imname == null)
                   {
-                    imname = MessageManager.formatMessage("label.from_msname", new String[]{sname});
+                    imname = MessageManager.formatMessage(
+                            "label.from_msname", new Object[] { sname });
                   }
                   fetchr = new JMenuItem(msname);
-                  final DbSourceProxy[] dassrc =
-                  { sproxy };
+                  final DbSourceProxy[] dassrc = { sproxy };
                   fetchr.addActionListener(new ActionListener()
                   {
 
@@ -5591,7 +5689,9 @@ this,
 
                   });
                   fetchr.setToolTipText("<html>"
-                          + MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{dbname}));
+                          + MessageManager.formatMessage(
+                                  "label.fetch_retrieve_from", new Object[]
+                                  { dbname }));
                   ifetch.add(fetchr);
                   ++i;
                   if (++icomp >= mcomp || i == (otherdb.size()))
@@ -5648,8 +5748,8 @@ this,
 
   public void setShowSeqFeatures(boolean b)
   {
-    showSeqFeatures.setSelected(true);
-    viewport.setShowSequenceFeatures(true);
+    showSeqFeatures.setSelected(b);
+    viewport.setShowSequenceFeatures(b);
   }
 
   /*
@@ -5756,6 +5856,18 @@ this,
     }
   }
 
+  public void clearAlignmentSeqRep()
+  {
+    // TODO refactor alignmentseqrep to controller
+    if (viewport.getAlignment().hasSeqrep())
+    {
+      viewport.getAlignment().setSeqrep(null);
+      PaintRefresher.Refresh(this, viewport.getSequenceSetId());
+      alignPanel.updateAnnotation();
+      alignPanel.paintAlignment(true);
+    }
+  }
+
   @Override
   protected void createGroup_actionPerformed(ActionEvent e)
   {
@@ -5784,10 +5896,13 @@ this,
     if (!viewport.getSequenceSetId().equals(
             alignmentPanel.av.getSequenceSetId()))
     {
-      throw new Error(MessageManager.getString("error.implementation_error_cannot_show_view_alignment_frame"));
+      throw new Error(
+              MessageManager
+                      .getString("error.implementation_error_cannot_show_view_alignment_frame"));
     }
     if (tabbedPane != null
-            & alignPanels.indexOf(alignmentPanel) != tabbedPane
+            && tabbedPane.getTabCount() > 0
+            && alignPanels.indexOf(alignmentPanel) != tabbedPane
                     .getSelectedIndex())
     {
       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
@@ -5810,6 +5925,13 @@ this,
     for (AlignmentAnnotation aa : alignPanel.getAlignment()
             .getAlignmentAnnotation())
     {
+      /*
+       * don't display non-positional annotations on an alignment
+       */
+      if (aa.annotations == null)
+      {
+        continue;
+      }
       boolean apply = (aa.sequenceRef == null && forAlignment)
               || (aa.sequenceRef != null && forSequences);
       if (apply)
@@ -5817,7 +5939,7 @@ this,
         aa.visible = visible;
       }
     }
-    alignPanel.validateAnnotationDimensions(false);
+    alignPanel.validateAnnotationDimensions(true);
     alignPanel.alignmentChanged();
   }
 
@@ -5832,6 +5954,79 @@ this,
             .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
     alignPanel.paintAlignment(true);
   }
+
+  /**
+   * 
+   * @return alignment panels in this alignment frame
+   */
+  public List<? extends AlignmentViewPanel> getAlignPanels()
+  {
+    return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
+  }
+
+  /**
+   * Open a new alignment window, with the cDNA associated with this (protein)
+   * alignment, aligned as is the protein.
+   */
+  protected void viewAsCdna_actionPerformed()
+  {
+    // TODO no longer a menu action - refactor as required
+    final AlignmentI alignment = getViewport().getAlignment();
+    Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
+    if (mappings == null)
+    {
+      return;
+    }
+    List<SequenceI> cdnaSeqs = new ArrayList<SequenceI>();
+    for (SequenceI aaSeq : alignment.getSequences())
+    {
+      for (AlignedCodonFrame acf : mappings)
+      {
+        SequenceI dnaSeq = acf.getDnaForAaSeq(aaSeq.getDatasetSequence());
+        if (dnaSeq != null)
+        {
+          /*
+           * There is a cDNA mapping for this protein sequence - add to new
+           * alignment. It will share the same dataset sequence as other mapped
+           * cDNA (no new mappings need to be created).
+           */
+          final Sequence newSeq = new Sequence(dnaSeq);
+          newSeq.setDatasetSequence(dnaSeq);
+          cdnaSeqs.add(newSeq);
+        }
+      }
+    }
+    if (cdnaSeqs.size() == 0)
+    {
+      // show a warning dialog no mapped cDNA
+      return;
+    }
+    AlignmentI cdna = new Alignment(cdnaSeqs.toArray(new SequenceI[cdnaSeqs
+            .size()]));
+    AlignFrame alignFrame = new AlignFrame(cdna, AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
+    cdna.alignAs(alignment);
+    String newtitle = "cDNA " + MessageManager.getString("label.for") + " "
+            + this.title;
+    Desktop.addInternalFrame(alignFrame, newtitle,
+            AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+  }
+
+  /**
+   * Set visibility of dna/protein complement view (available when shown in a
+   * split frame).
+   * 
+   * @param show
+   */
+  @Override
+  protected void showComplement_actionPerformed(boolean show)
+  {
+    SplitContainerI sf = getSplitViewContainer();
+    if (sf != null)
+    {
+      sf.setComplementVisible(this, show);
+    }
+  }
 }
 
 class PrintThread extends Thread