Revert "Merge branch 'bug/JAL-3806_mappingCoversSequence' into releases/Release_2_11_...
[jalview.git] / src / jalview / gui / AlignFrame.java
index 6346870..9013416 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-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  */
 package jalview.gui;
 
-import jalview.analysis.AAFrequency;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.print.PageFormat;
+import java.awt.print.PrinterJob;
+import java.beans.PropertyChangeEvent;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+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.Vector;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JEditorPane;
+import javax.swing.JInternalFrame;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+
 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.GeneticCodeI;
 import jalview.analysis.ParseProperties;
 import jalview.analysis.SequenceIdMatcher;
+import jalview.api.AlignExportSettingI;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
-import jalview.api.analysis.ScoreModelI;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureSettingsControllerI;
+import jalview.api.FeatureSettingsModelI;
+import jalview.api.SplitContainerI;
+import jalview.api.ViewStyleI;
+import jalview.api.analysis.SimilarityParamsI;
 import jalview.bin.Cache;
+import jalview.bin.Jalview;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
+import jalview.commands.EditCommand.Action;
 import jalview.commands.OrderCommand;
 import jalview.commands.RemoveGapColCommand;
 import jalview.commands.RemoveGapsCommand;
@@ -42,98 +98,58 @@ 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.HiddenColumns;
+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.ColourMenuHelper.ColourChangeListener;
+import jalview.gui.ViewSelectionMenu.ViewSetProvider;
 import jalview.io.AlignmentProperties;
 import jalview.io.AnnotationFile;
-import jalview.io.FeaturesFile;
+import jalview.io.BackupFiles;
+import jalview.io.BioJsHTMLOutput;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
 import jalview.io.FileLoader;
+import jalview.io.FileParse;
 import jalview.io.FormatAdapter;
-import jalview.io.HTMLOutput;
+import jalview.io.HtmlSvgOutput;
 import jalview.io.IdentifyFile;
+import jalview.io.JPredFile;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
 import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
+import jalview.io.ScoreMatrixFile;
 import jalview.io.TCoffeeScoreFile;
+import jalview.io.vcf.VCFLoader;
 import jalview.jbgui.GAlignFrame;
-import jalview.schemes.Blosum62ColourScheme;
-import jalview.schemes.BuriedColourScheme;
-import jalview.schemes.ClustalxColourScheme;
 import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ColourSchemeProperty;
-import jalview.schemes.HelixColourScheme;
-import jalview.schemes.HydrophobicColourScheme;
-import jalview.schemes.NucleotideColourScheme;
-import jalview.schemes.PIDColourScheme;
-import jalview.schemes.PurinePyrimidineColourScheme;
-import jalview.schemes.RNAHelicesColourChooser;
-import jalview.schemes.ResidueProperties;
-import jalview.schemes.StrandColourScheme;
+import jalview.schemes.ColourSchemes;
+import jalview.schemes.ResidueColourScheme;
 import jalview.schemes.TCoffeeColourScheme;
-import jalview.schemes.TaylorColourScheme;
-import jalview.schemes.TurnColourScheme;
-import jalview.schemes.UserColourScheme;
-import jalview.schemes.ZappoColourScheme;
+import jalview.util.HttpUtils;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
+import jalview.ws.DBRefFetcher;
+import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 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;
-import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.StringSelection;
-import java.awt.datatransfer.Transferable;
-import java.awt.dnd.DnDConstants;
-import java.awt.dnd.DropTargetDragEvent;
-import java.awt.dnd.DropTargetDropEvent;
-import java.awt.dnd.DropTargetEvent;
-import java.awt.dnd.DropTargetListener;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.print.PageFormat;
-import java.awt.print.PrinterJob;
-import java.beans.PropertyChangeEvent;
-import java.io.File;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
-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;
-
 /**
  * DOCUMENT ME!
  * 
@@ -141,27 +157,28 @@ import javax.swing.SwingUtilities;
  * @version $Revision$
  */
 public class AlignFrame extends GAlignFrame implements DropTargetListener,
-        IProgressIndicator, AlignViewControllerGuiI
+        IProgressIndicator, AlignViewControllerGuiI, ColourChangeListener
 {
 
-  /** 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<>();
 
   /**
    * Last format used to load or save alignments in this window
    */
-  String currentFileFormat = null;
+  FileFormatI currentFileFormat = null;
 
   /**
    * Current filename for this alignment
@@ -223,8 +240,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param height
    *          height of frame.
    */
-  public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
-          int width, int height)
+  public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width,
+          int height)
   {
     this(al, hiddenColumns, width, height, null);
   }
@@ -240,8 +257,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param sequenceSetId
    *          (may be null)
    */
-  public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
-          int width, int height, String sequenceSetId)
+  public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width,
+          int height, String sequenceSetId)
   {
     this(al, hiddenColumns, width, height, sequenceSetId, null);
   }
@@ -259,8 +276,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param viewId
    *          (may be null)
    */
-  public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
-          int width, int height, String sequenceSetId, String viewId)
+  public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width,
+          int height, String sequenceSetId, String viewId)
   {
     setSize(width, height);
 
@@ -273,13 +290,33 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     alignPanel = new AlignmentPanel(this, viewport);
 
+    addAlignmentPanel(alignPanel, true);
+    init();
+  }
+
+  public AlignFrame(AlignmentI al, SequenceI[] hiddenSeqs,
+          HiddenColumns 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();
   }
 
   /**
-   * Make a new AlignFrame from exisiting alignmentPanels
+   * Make a new AlignFrame from existing alignmentPanels
    * 
    * @param ap
    *          AlignmentPanel
@@ -300,11 +337,16 @@ 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)
     {
-      BLOSUM62Colour.setEnabled(false);
+      // BLOSUM62Colour.setEnabled(false);
       conservationMenuItem.setEnabled(false);
       modifyConservation.setEnabled(false);
       // PIDColour.setEnabled(false);
@@ -324,17 +366,30 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       sortPairwiseMenuItem_actionPerformed(null);
     }
 
+    this.alignPanel.av
+            .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+
+    setMenusFromViewport(viewport);
+    buildSortByAnnotationScoresMenu();
+    calculateTree.addActionListener(new ActionListener()
+    {
+
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        openTreePcaDialog();
+      }
+    });
+    buildColourMenu();
+
     if (Desktop.desktop != null)
     {
       this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
       addServiceListeners();
-      setGUINucleotide(viewport.getAlignment().isNucleotide());
+      setGUINucleotide();
     }
 
-    setMenusFromViewport(viewport);
-    buildSortByAnnotationScoresMenu();
-    buildTreeMenu();
-    if (viewport.wrapAlignment)
+    if (viewport.getWrapAlignment())
     {
       wrapMenuItem_actionPerformed(null);
     }
@@ -346,6 +401,90 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     addKeyListener();
 
+    final List<AlignmentPanel> selviews = new ArrayList<>();
+    final List<AlignmentPanel> origview = new ArrayList<>();
+    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<>(
+                        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);
+    }
+    addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusGained(FocusEvent e)
+      {
+        Jalview.setCurrentAlignFrame(AlignFrame.this);
+      }
+    });
+
   }
 
   /**
@@ -357,13 +496,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param format
    *          format of file
    */
-  public void setFileName(String file, String format)
+  public void setFileName(String file, FileFormatI 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()
@@ -372,11 +515,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       public void keyPressed(KeyEvent evt)
       {
         if (viewport.cursorMode
-                && ((evt.getKeyCode() >= KeyEvent.VK_0 && evt.getKeyCode() <= KeyEvent.VK_9) || (evt
-                        .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
-                        .getKeyCode() <= KeyEvent.VK_NUMPAD9))
+                && ((evt.getKeyCode() >= KeyEvent.VK_0
+                        && evt.getKeyCode() <= KeyEvent.VK_9)
+                        || (evt.getKeyCode() >= KeyEvent.VK_NUMPAD0
+                                && evt.getKeyCode() <= KeyEvent.VK_NUMPAD9))
                 && Character.isDigit(evt.getKeyChar()))
-          alignPanel.seqPanel.numberPressed(evt.getKeyChar());
+        {
+          alignPanel.getSeqPanel().numberPressed(evt.getKeyChar());
+        }
 
         switch (evt.getKeyCode())
         {
@@ -388,38 +534,56 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
         case KeyEvent.VK_DOWN:
           if (evt.isAltDown() || !viewport.cursorMode)
+          {
             moveSelectedSequences(false);
+          }
           if (viewport.cursorMode)
-            alignPanel.seqPanel.moveCursor(0, 1);
+          {
+            alignPanel.getSeqPanel().moveCursor(0, 1,
+                    evt.isShiftDown() && !evt.isAltDown());
+          }
           break;
 
         case KeyEvent.VK_UP:
           if (evt.isAltDown() || !viewport.cursorMode)
+          {
             moveSelectedSequences(true);
+          }
           if (viewport.cursorMode)
-            alignPanel.seqPanel.moveCursor(0, -1);
-
+          {
+            alignPanel.getSeqPanel().moveCursor(0, -1,
+                    evt.isShiftDown() && !evt.isAltDown());
+          }
           break;
 
         case KeyEvent.VK_LEFT:
           if (evt.isAltDown() || !viewport.cursorMode)
-            slideSequences(false, alignPanel.seqPanel.getKeyboardNo1());
+          {
+            slideSequences(false,
+                    alignPanel.getSeqPanel().getKeyboardNo1());
+          }
           else
-            alignPanel.seqPanel.moveCursor(-1, 0);
+          {
+            alignPanel.getSeqPanel().moveCursor(-1, 0, evt.isShiftDown());
+          }
 
           break;
 
         case KeyEvent.VK_RIGHT:
           if (evt.isAltDown() || !viewport.cursorMode)
-            slideSequences(true, alignPanel.seqPanel.getKeyboardNo1());
+          {
+            slideSequences(true, alignPanel.getSeqPanel().getKeyboardNo1());
+          }
           else
-            alignPanel.seqPanel.moveCursor(1, 0);
+          {
+            alignPanel.getSeqPanel().moveCursor(1, 0, evt.isShiftDown());
+          }
           break;
 
         case KeyEvent.VK_SPACE:
           if (viewport.cursorMode)
           {
-            alignPanel.seqPanel.insertGapAtCursor(evt.isControlDown()
+            alignPanel.getSeqPanel().insertGapAtCursor(evt.isControlDown()
                     || evt.isShiftDown() || evt.isAltDown());
           }
           break;
@@ -443,7 +607,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.seqPanel.deleteGapAtCursor(evt.isControlDown()
+            alignPanel.getSeqPanel().deleteGapAtCursor(evt.isControlDown()
                     || evt.isShiftDown() || evt.isAltDown());
           }
 
@@ -452,19 +616,19 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         case KeyEvent.VK_S:
           if (viewport.cursorMode)
           {
-            alignPanel.seqPanel.setCursorRow();
+            alignPanel.getSeqPanel().setCursorRow();
           }
           break;
         case KeyEvent.VK_C:
           if (viewport.cursorMode && !evt.isControlDown())
           {
-            alignPanel.seqPanel.setCursorColumn();
+            alignPanel.getSeqPanel().setCursorColumn();
           }
           break;
         case KeyEvent.VK_P:
           if (viewport.cursorMode)
           {
-            alignPanel.seqPanel.setCursorPosition();
+            alignPanel.getSeqPanel().setCursorPosition();
           }
           break;
 
@@ -472,47 +636,43 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         case KeyEvent.VK_COMMA:
           if (viewport.cursorMode)
           {
-            alignPanel.seqPanel.setCursorRowAndColumn();
+            alignPanel.getSeqPanel().setCursorRowAndColumn();
           }
           break;
 
         case KeyEvent.VK_Q:
           if (viewport.cursorMode)
           {
-            alignPanel.seqPanel.setSelectionAreaAtCursor(true);
+            alignPanel.getSeqPanel().setSelectionAreaAtCursor(true);
           }
           break;
         case KeyEvent.VK_M:
           if (viewport.cursorMode)
           {
-            alignPanel.seqPanel.setSelectionAreaAtCursor(false);
+            alignPanel.getSeqPanel().setSelectionAreaAtCursor(false);
           }
           break;
 
         case KeyEvent.VK_F2:
           viewport.cursorMode = !viewport.cursorMode;
-          statusBar.setText(MessageManager.formatMessage(
-                  "label.keyboard_editing_mode", new String[]
+          statusBar.setText(MessageManager
+                  .formatMessage("label.keyboard_editing_mode", new String[]
                   { (viewport.cursorMode ? "on" : "off") }));
           if (viewport.cursorMode)
           {
-            alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;
-            alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;
+            ViewportRanges ranges = viewport.getRanges();
+            alignPanel.getSeqPanel().seqCanvas.cursorX = ranges
+                    .getStartRes();
+            alignPanel.getSeqPanel().seqCanvas.cursorY = ranges
+                    .getStartSeq();
           }
-          alignPanel.seqPanel.seqCanvas.repaint();
+          alignPanel.getSeqPanel().seqCanvas.repaint();
           break;
 
         case KeyEvent.VK_F1:
           try
           {
-            ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
-            java.net.URL url = javax.help.HelpSet.findHelpSet(cl,
-                    "help/help");
-            javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
-
-            javax.help.HelpBroker hb = hs.createHelpBroker();
-            hb.setCurrentID("home");
-            hb.setDisplayed(true);
+            Help.showHelpWindow();
           } catch (Exception ex)
           {
             ex.printStackTrace();
@@ -525,27 +685,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           toggleHiddenRegions(toggleSeqs, toggleCols);
           break;
         }
+        case KeyEvent.VK_B:
+        {
+          boolean toggleSel = evt.isControlDown() || evt.isMetaDown();
+          boolean modifyExisting = true; // always modify, don't clear
+                                         // evt.isShiftDown();
+          boolean invertHighlighted = evt.isAltDown();
+          avc.markHighlightedColumns(invertHighlighted, modifyExisting,
+                  toggleSel);
+          break;
+        }
         case KeyEvent.VK_PAGE_UP:
-          if (viewport.wrapAlignment)
-          {
-            alignPanel.scrollUp(true);
-          }
-          else
-          {
-            alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                    - viewport.endSeq + viewport.startSeq);
-          }
+          viewport.getRanges().pageUp();
           break;
         case KeyEvent.VK_PAGE_DOWN:
-          if (viewport.wrapAlignment)
-          {
-            alignPanel.scrollUp(false);
-          }
-          else
-          {
-            alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                    + viewport.endSeq - viewport.startSeq);
-          }
+          viewport.getRanges().pageDown();
           break;
         }
       }
@@ -557,14 +711,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         {
         case KeyEvent.VK_LEFT:
           if (evt.isAltDown() || !viewport.cursorMode)
-            viewport.firePropertyChange("alignment", null, viewport
-                    .getAlignment().getSequences());
+          {
+            viewport.firePropertyChange("alignment", null,
+                    viewport.getAlignment().getSequences());
+          }
           break;
 
         case KeyEvent.VK_RIGHT:
           if (evt.isAltDown() || !viewport.cursorMode)
-            viewport.firePropertyChange("alignment", null, viewport
-                    .getAlignment().getSequences());
+          {
+            viewport.firePropertyChange("alignment", null,
+                    viewport.getAlignment().getSequences());
+          }
           break;
         }
       }
@@ -577,15 +735,15 @@ 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());
 
     int aSize = alignPanels.size();
 
-    tabbedPane.setVisible(aSize > 1 || ap.av.viewName != null);
+    tabbedPane.setVisible(aSize > 1 || ap.av.getViewName() != null);
 
-    if (aSize == 1 && ap.av.viewName == null)
+    if (aSize == 1 && ap.av.getViewName() == null)
     {
       this.getContentPane().add(ap, BorderLayout.CENTER);
     }
@@ -598,7 +756,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       expandViews.setEnabled(true);
       gatherViews.setEnabled(true);
-      tabbedPane.addTab(ap.av.viewName, ap);
+      tabbedPane.addTab(ap.av.getViewName(), ap);
 
       ap.setVisible(false);
     }
@@ -620,8 +778,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     expandViews.setEnabled(true);
     gatherViews.setEnabled(true);
     tabbedPane.setVisible(true);
-    AlignmentPanel first = (AlignmentPanel) alignPanels.firstElement();
-    tabbedPane.addTab(first.av.viewName, first);
+    AlignmentPanel first = alignPanels.get(0);
+    tabbedPane.addTab(first.av.getViewName(), first);
     this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
   }
 
@@ -649,8 +807,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                     @Override
                     public void run()
                     {
-                      System.err
-                              .println("Rebuild WS Menu for service change");
+                      System.err.println(
+                              "Rebuild WS Menu for service change");
                       BuildWebServiceMenu();
                     }
 
@@ -664,7 +822,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);
@@ -681,27 +839,35 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }).start();
   }
 
-  public void setGUINucleotide(boolean nucleotide)
+  /**
+   * Configure menu items that vary according to whether the alignment is
+   * nucleotide or protein
+   */
+  public void setGUINucleotide()
   {
+    AlignmentI al = getViewport().getAlignment();
+    boolean nucleotide = al.isNucleotide();
+
+    loadVcf.setVisible(nucleotide);
     showTranslation.setVisible(nucleotide);
+    showReverse.setVisible(nucleotide);
+    showReverseComplement.setVisible(nucleotide);
     conservationMenuItem.setEnabled(!nucleotide);
-    modifyConservation.setEnabled(!nucleotide);
+    modifyConservation
+            .setEnabled(!nucleotide && conservationMenuItem.isSelected());
     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(nucleotide ? MessageManager.getString("label.protein")
+                    : MessageManager.getString("label.nucleotide"));
   }
 
   /**
-   * 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.
    */
+  @Override
   public void setMenusForViewport()
   {
     setMenusFromViewport(viewport);
@@ -714,23 +880,32 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param av
    *          AlignViewport
    */
-  void setMenusFromViewport(AlignViewport av)
+  public void setMenusFromViewport(AlignViewport av)
   {
     padGapsMenuitem.setSelected(av.isPadGaps());
-    colourTextMenuItem.setSelected(av.showColourText);
+    colourTextMenuItem.setSelected(av.isShowColourText());
     abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
+    modifyPID.setEnabled(abovePIDThreshold.isSelected());
     conservationMenuItem.setSelected(av.getConservationSelected());
+    modifyConservation.setEnabled(conservationMenuItem.isSelected());
     seqLimits.setSelected(av.getShowJVSuffix());
-    idRightAlign.setSelected(av.rightAlignIds);
-    centreColumnLabelsMenuItem.setState(av.centreColumnLabels);
-    renderGapsMenuItem.setSelected(av.renderGaps);
-    wrapMenuItem.setSelected(av.wrapAlignment);
-    scaleAbove.setVisible(av.wrapAlignment);
-    scaleLeft.setVisible(av.wrapAlignment);
-    scaleRight.setVisible(av.wrapAlignment);
-    annotationPanelMenuItem.setState(av.showAnnotation);
-    viewBoxesMenuItem.setSelected(av.showBoxes);
-    viewTextMenuItem.setSelected(av.showText);
+    idRightAlign.setSelected(av.isRightAlignIds());
+    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
+     */
+    showAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    viewBoxesMenuItem.setSelected(av.getShowBoxes());
+    viewTextMenuItem.setSelected(av.getShowText());
     showNonconservedMenuItem.setSelected(av.getShowUnconserved());
     showGroupConsensus.setSelected(av.isShowGroupConsensus());
     showGroupConservation.setSelected(av.isShowGroupConservation());
@@ -738,27 +913,35 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     showSequenceLogo.setSelected(av.isShowSequenceLogo());
     normaliseSequenceLogo.setSelected(av.isNormaliseSequenceLogo());
 
-    setColourSelected(ColourSchemeProperty.getColourName(av
-            .getGlobalColourScheme()));
+    ColourMenuHelper.setColourSelected(colourMenu,
+            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);
-    rnahelicesColour.setEnabled(av.getAlignment().hasRNAStructure());
-    rnahelicesColour
-            .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
-    setShowProductsEnabled();
+
+    showProducts.setEnabled(canShowProducts());
+    setGroovyEnabled(Desktop.getGroovyConsole() != null);
+
     updateEditMenuBar();
   }
 
-  // methods for implementing IProgressIndicator
-  // need to refactor to a reusable stub class
-  Hashtable progressBars, progressBarHandlers;
+  /**
+   * Set the enabled state of the 'Run Groovy' option in the Calculate menu
+   * 
+   * @param b
+   */
+  public void setGroovyEnabled(boolean b)
+  {
+    runGroovy.setEnabled(b);
+  }
+
+  private IProgressIndicator progressBar;
 
   /*
    * (non-Javadoc)
@@ -768,78 +951,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);
   }
 
   /**
@@ -849,18 +968,19 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public boolean operationInProgress()
   {
-    if (progressBars != null && progressBars.size() > 0)
-    {
-      return true;
-    }
-    return false;
+    return progressBar.operationInProgress();
   }
 
+  /**
+   * Sets the text of the status bar. Note that setting a null or empty value
+   * will cause the status bar to be hidden, with possibly undesirable flicker
+   * of the screen layout.
+   */
   @Override
   public void setStatus(String text)
   {
-    statusBar.setText(text);
-  };
+    statusBar.setText(text == null || text.isEmpty() ? " " : text);
+  }
 
   /*
    * Added so Castor Mapping file can obtain Jalview Version
@@ -872,13 +992,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   public FeatureRenderer getFeatureRenderer()
   {
-    return alignPanel.seqPanel.seqCanvas.getFeatureRenderer();
+    return alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer();
   }
 
   @Override
   public void fetchSequence_actionPerformed(ActionEvent e)
   {
-    new SequenceFetcher(this);
+    new jalview.gui.SequenceFetcher(this);
   }
 
   @Override
@@ -896,7 +1016,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // originating file's format
       // TODO: work out how to recover feature settings for correct view(s) when
       // file is reloaded.
-      if (currentFileFormat.equals("Jalview"))
+      if (FileFormat.Jalview.equals(currentFileFormat))
       {
         JInternalFrame[] frames = Desktop.desktop.getAllFrames();
         for (int i = 0; i < frames.length; i++)
@@ -918,7 +1038,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         Desktop.instance.closeAssociatedWindows();
 
         FileLoader loader = new FileLoader();
-        String protocol = fileName.startsWith("http:") ? "URL" : "File";
+        DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(fileName)
+                ? DataSourceType.URL
+                : DataSourceType.FILE;
         loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
       }
       else
@@ -926,7 +1048,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         Rectangle bounds = this.getBounds();
 
         FileLoader loader = new FileLoader();
-        String protocol = fileName.startsWith("http:") ? "URL" : "File";
+        DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(fileName)
+                ? DataSourceType.URL
+                : DataSourceType.FILE;
         AlignFrame newframe = loader.LoadFileWaitTillLoaded(fileName,
                 protocol, currentFileFormat);
 
@@ -957,7 +1081,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
@@ -969,10 +1094,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void save_actionPerformed(ActionEvent e)
   {
-    if (fileName == null
-            || (currentFileFormat == null || !jalview.io.FormatAdapter
-                    .isValidIOFormat(currentFileFormat, true))
-            || fileName.startsWith("http"))
+    if (fileName == null || (currentFileFormat == null)
+            || HttpUtils.startsWithHttpOrHttps(fileName))
     {
       saveAs_actionPerformed(null);
     }
@@ -991,14 +1114,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void saveAs_actionPerformed(ActionEvent e)
   {
-    JalviewFileChooser chooser = new JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
-            jalview.io.AppletFormatAdapter.WRITABLE_EXTENSIONS,
-            jalview.io.AppletFormatAdapter.WRITABLE_FNAMES,
-            currentFileFormat, false);
+    String format = currentFileFormat == null ? null
+            : currentFileFormat.getName();
+    JalviewFileChooser chooser = JalviewFileChooser
+            .forWrite(Cache.getProperty("LAST_DIRECTORY"), format);
 
     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);
@@ -1006,92 +1129,70 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
       currentFileFormat = chooser.getSelectedFormat();
-      if (currentFileFormat == null)
+      while (currentFileFormat == null)
       {
-        JOptionPane
-                .showInternalMessageDialog(
-                        Desktop.desktop,
-                        MessageManager
-                                .getString("label.select_file_format_before_saving"),
-                        MessageManager
-                                .getString("label.file_format_not_specified"),
-                        JOptionPane.WARNING_MESSAGE);
+        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+                MessageManager.getString(
+                        "label.select_file_format_before_saving"),
+                MessageManager.getString("label.file_format_not_specified"),
+                JvOptionPane.WARNING_MESSAGE);
+        currentFileFormat = chooser.getSelectedFormat();
         value = chooser.showSaveDialog(this);
-        return;
+        if (value != JalviewFileChooser.APPROVE_OPTION)
+        {
+          return;
+        }
       }
 
       fileName = chooser.getSelectedFile().getPath();
 
-      jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT",
-              currentFileFormat);
+      Cache.setProperty("DEFAULT_FILE_FORMAT", currentFileFormat.getName());
 
-      jalview.bin.Cache.setProperty("LAST_DIRECTORY", fileName);
-      if (currentFileFormat.indexOf(" ") > -1)
-      {
-        currentFileFormat = currentFileFormat.substring(0,
-                currentFileFormat.indexOf(" "));
-      }
+      Cache.setProperty("LAST_DIRECTORY", fileName);
       saveAlignment(fileName, currentFileFormat);
     }
   }
 
-  public boolean saveAlignment(String file, String format)
+  public boolean saveAlignment(String file, FileFormatI format)
   {
     boolean success = true;
 
-    if (format.equalsIgnoreCase("Jalview"))
+    if (FileFormat.Jalview.equals(format))
     {
       String shortName = title;
 
       if (shortName.indexOf(java.io.File.separatorChar) > -1)
       {
-        shortName = shortName.substring(shortName
-                .lastIndexOf(java.io.File.separatorChar) + 1);
+        shortName = shortName.substring(
+                shortName.lastIndexOf(java.io.File.separatorChar) + 1);
       }
 
-      success = new Jalview2XML().SaveAlignment(this, file, shortName);
+      success = new jalview.project.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[]
+              { file, format }));
 
     }
     else
     {
-      if (!jalview.io.AppletFormatAdapter.isValidFormat(format, true))
+      AlignmentExportData exportData = getAlignmentForExport(format,
+              viewport, null);
+      if (exportData.getSettings().isCancelled())
       {
-        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
         return false;
       }
-
-      String[] omitHidden = null;
-
-      if (viewport.hasHiddenColumns())
-      {
-        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);
-        }
-      }
-      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),
-              viewport.getColumnSelection());
+              exportData.getOmitHidden(), exportData.getStartEndPostions(),
+              f.getCacheSuffixDefault(format),
+              viewport.getAlignment().getHiddenColumns());
 
       if (output == null)
       {
@@ -1099,33 +1200,67 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       }
       else
       {
+        // create backupfiles object and get new temp filename destination
+        Cache.log.trace("ALIGNFRAME making backupfiles object for " + file);
+        BackupFiles backupfiles = new BackupFiles(file);
+
         try
         {
-          java.io.PrintWriter out = new java.io.PrintWriter(
-                  new java.io.FileWriter(file));
+          String tempFilePath = backupfiles.getTempFilePath();
+          Cache.log.trace(
+                  "ALIGNFRAME setting PrintWriter to " + tempFilePath);
+          PrintWriter out = new PrintWriter(new FileWriter(tempFilePath));
+
+          Cache.log.trace(
+                  "ALIGNFRAME about to write to temp file " + tempFilePath);
 
           out.print(output);
+          Cache.log.trace("ALIGNFRAME about to close file");
           out.close();
+          Cache.log.trace("ALIGNFRAME closed file");
           this.setTitle(file);
           statusBar.setText(MessageManager.formatMessage(
-                  "label.successfully_saved_to_file_in_format",
-                  new String[]
-                  { fileName, format }));
+                  "label.successfully_saved_to_file_in_format", new Object[]
+                  { file, format.getName() }));
+        } catch (IOException e)
+        {
+          success = false;
+          Cache.log.error(
+                  "ALIGNFRAME Something happened writing the temp file");
+          Cache.log.error(e.getMessage());
+          Cache.log.debug(Cache.getStackTraceString(e));
+
         } catch (Exception ex)
         {
           success = false;
-          ex.printStackTrace();
+          Cache.log.error(
+                  "ALIGNFRAME Something unexpected happened writing the temp file");
+          Cache.log.error(ex.getMessage());
+          Cache.log.debug(Cache.getStackTraceString(ex));
         }
+
+        backupfiles.setWriteSuccess(success);
+        Cache.log.debug("ALIGNFRAME writing temp file was "
+                + (success ? "" : "NOT ") + "successful");
+        // do the backup file roll and rename the temp file to actual file
+        Cache.log.trace("ALIGNFRAME about to rollBackupsAndRenameTempFile");
+        success = backupfiles.rollBackupsAndRenameTempFile();
+        Cache.log.debug("ALIGNFRAME performed rollBackupsAndRenameTempFile "
+                + (success ? "" : "un") + "successfully");
+
       }
     }
 
     if (!success)
     {
-      JOptionPane.showInternalMessageDialog(this, MessageManager
-              .formatMessage("label.couldnt_save_file", new String[]
-              { fileName }), MessageManager
-              .getString("label.error_saving_file"),
-              JOptionPane.WARNING_MESSAGE);
+      if (!Platform.isHeadless())
+      {
+        JvOptionPane.showInternalMessageDialog(this, MessageManager
+                .formatMessage("label.couldnt_save_file", new Object[]
+                { file }),
+                MessageManager.getString("label.error_saving_file"),
+                JvOptionPane.WARNING_MESSAGE);
+      }
     }
 
     return success;
@@ -1140,8 +1275,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     else
     {
-      JOptionPane.showInternalMessageDialog(this, warning, title,
-              JOptionPane.WARNING_MESSAGE);
+      JvOptionPane.showInternalMessageDialog(this, warning, title,
+              JvOptionPane.WARNING_MESSAGE);
     }
     return;
   }
@@ -1155,47 +1290,81 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void outputText_actionPerformed(ActionEvent e)
   {
-    String[] omitHidden = null;
-
-    if (viewport.hasHiddenColumns())
+    FileFormatI fileFormat = FileFormats.getInstance()
+            .forName(e.getActionCommand());
+    AlignmentExportData exportData = getAlignmentForExport(fileFormat,
+            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()));
-      Desktop.addInternalFrame(cap, MessageManager.formatMessage(
-              "label.alignment_output_command", new String[]
+      FileFormatI format = fileFormat;
+      cap.setText(new FormatAdapter(alignPanel, exportData.getSettings())
+              .formatSequences(format, exportData.getAlignment(),
+                      exportData.getOmitHidden(),
+                      exportData.getStartEndPostions(),
+                      viewport.getAlignment().getHiddenColumns()));
+      Desktop.addInternalFrame(cap, MessageManager
+              .formatMessage("label.alignment_output_command", new Object[]
               { e.getActionCommand() }), 600, 500);
     } catch (OutOfMemoryError oom)
     {
-      new OOMWarning("Outputting alignment as " + e.getActionCommand(), oom);
+      new OOMWarning("Outputting alignment as " + e.getActionCommand(),
+              oom);
       cap.dispose();
     }
 
   }
 
-  /**
-   * DOCUMENT ME!
+  public static AlignmentExportData getAlignmentForExport(
+          FileFormatI format, AlignViewportI viewport,
+          AlignExportSettingI exportSettings)
+  {
+    AlignmentI alignmentToExport = null;
+    AlignExportSettingI settings = exportSettings;
+    String[] omitHidden = null;
+
+    HiddenSequences hiddenSeqs = viewport.getAlignment()
+            .getHiddenSequences();
+
+    alignmentToExport = viewport.getAlignment();
+
+    boolean hasHiddenSeqs = hiddenSeqs.getSize() > 0;
+    if (settings == null)
+    {
+      settings = new AlignExportSettings(hasHiddenSeqs,
+              viewport.hasHiddenColumns(), format);
+    }
+    // settings.isExportAnnotations();
+
+    if (viewport.hasHiddenColumns() && !settings.isExportHiddenColumns())
+    {
+      omitHidden = viewport.getViewAsString(false,
+              settings.isExportHiddenSequences());
+    }
+
+    int[] alignmentStartEnd = new int[2];
+    if (hasHiddenSeqs && settings.isExportHiddenSequences())
+    {
+      alignmentToExport = hiddenSeqs.getFullAlignment();
+    }
+    else
+    {
+      alignmentToExport = viewport.getAlignment();
+    }
+    alignmentStartEnd = viewport.getAlignment().getHiddenColumns()
+            .getVisibleStartAndEndIndex(alignmentToExport.getWidth());
+    AlignmentExportData ed = new AlignmentExportData(alignmentToExport,
+            omitHidden, alignmentStartEnd, settings);
+    return ed;
+  }
+
+  /**
+   * DOCUMENT ME!
    * 
    * @param e
    *          DOCUMENT ME!
@@ -1203,9 +1372,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void htmlMenuItem_actionPerformed(ActionEvent e)
   {
-    new HTMLOutput(alignPanel,
-            alignPanel.seqPanel.seqCanvas.getSequenceRenderer(),
-            alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
+    HtmlSvgOutput htmlSVG = new HtmlSvgOutput(alignPanel);
+    htmlSVG.exportHTML(null);
+  }
+
+  @Override
+  public void bioJSMenuItem_actionPerformed(ActionEvent e)
+  {
+    BioJsHTMLOutput bjs = new BioJsHTMLOutput(alignPanel);
+    bjs.exportHTML(null);
   }
 
   public void createImageMap(File file, String image)
@@ -1238,6 +1413,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   @Override
+  public void createSVG(File f)
+  {
+    alignPanel.makeSVG(f);
+  }
+
+  @Override
   public void pageSetup_actionPerformed(ActionEvent e)
   {
     PrinterJob printJob = PrinterJob.getPrinterJob();
@@ -1261,17 +1442,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void exportFeatures_actionPerformed(ActionEvent e)
   {
-    new AnnotationExporter().exportFeatures(alignPanel);
+    new AnnotationExporter(alignPanel).exportFeatures();
   }
 
   @Override
   public void exportAnnotations_actionPerformed(ActionEvent e)
   {
-    new AnnotationExporter().exportAnnotations(alignPanel,
-            viewport.showAnnotation ? viewport.getAlignment()
-                    .getAlignmentAnnotation() : null, viewport
-                    .getAlignment().getGroups(), ((Alignment) viewport
-                    .getAlignment()).alignmentProperties);
+    new AnnotationExporter(alignPanel).exportAnnotations();
   }
 
   @Override
@@ -1281,10 +1458,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     JalviewFileChooser chooser = new JalviewFileChooser(
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
     chooser.setFileView(new JalviewFileView());
-    chooser.setDialogTitle(MessageManager
-            .getString("label.load_jalview_annotations"));
-    chooser.setToolTipText(MessageManager
-            .getString("label.load_jalview_annotations"));
+    chooser.setDialogTitle(
+            MessageManager.getString("label.load_jalview_annotations"));
+    chooser.setToolTipText(
+            MessageManager.getString("label.load_jalview_annotations"));
 
     int value = chooser.showOpenDialog(null);
 
@@ -1323,7 +1500,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();
             }
           }
@@ -1333,9 +1510,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           closeView(alignPanel);
         }
       }
-
       if (closeAllTabs)
       {
+        if (featureSettings != null && featureSettings.isOpen())
+        {
+          featureSettings.close();
+          featureSettings = null;
+        }
+        /*
+         * 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)
@@ -1345,22 +1530,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();
@@ -1380,12 +1560,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   void updateEditMenuBar()
   {
 
-    if (viewport.historyList.size() > 0)
+    if (viewport.getHistoryList().size() > 0)
     {
       undoMenuItem.setEnabled(true);
-      CommandI command = (CommandI) viewport.historyList.peek();
-      undoMenuItem.setText(MessageManager.formatMessage(
-              "label.undo_command", new String[]
+      CommandI command = viewport.getHistoryList().peek();
+      undoMenuItem.setText(MessageManager
+              .formatMessage("label.undo_command", new Object[]
               { command.getDescription() }));
     }
     else
@@ -1394,13 +1574,13 @@ 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 = (CommandI) viewport.redoList.peek();
-      redoMenuItem.setText(MessageManager.formatMessage(
-              "label.redo_command", new String[]
+      CommandI command = viewport.getRedoList().peek();
+      redoMenuItem.setText(MessageManager
+              .formatMessage("label.redo_command", new Object[]
               { command.getDescription() }));
     }
     else
@@ -1410,12 +1590,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
+  @Override
   public void addHistoryItem(CommandI command)
   {
     if (command.getSize() > 0)
     {
-      viewport.historyList.push(command);
-      viewport.redoList.clear();
+      viewport.addToHistoryList(command);
+      viewport.clearRedoList();
       updateEditMenuBar();
       viewport.updateHiddenColumns();
       // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
@@ -1433,18 +1614,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;
   }
@@ -1458,21 +1638,23 @@ 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 = (CommandI) 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)
     {
       if (originalSource != viewport)
       {
-        Cache.log
-                .warn("Implementation worry: mismatch of viewport origin for undo");
+        Cache.log.warn(
+                "Implementation worry: mismatch of viewport origin for undo");
       }
       originalSource.updateHiddenColumns();
       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
@@ -1480,8 +1662,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // && viewport.getColumnSelection().getHiddenColumns() != null &&
       // viewport.getColumnSelection()
       // .getHiddenColumns().size() > 0);
-      originalSource.firePropertyChange("alignment", null, originalSource
-              .getAlignment().getSequences());
+      originalSource.firePropertyChange("alignment", null,
+              originalSource.getAlignment().getSequences());
     }
   }
 
@@ -1494,16 +1676,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 = (CommandI) 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)
@@ -1511,8 +1693,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       if (originalSource != viewport)
       {
-        Cache.log
-                .warn("Implementation worry: mismatch of viewport origin for redo");
+        Cache.log.warn(
+                "Implementation worry: mismatch of viewport origin for redo");
       }
       originalSource.updateHiddenColumns();
       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
@@ -1520,14 +1702,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // && viewport.getColumnSelection().getHiddenColumns() != null &&
       // viewport.getColumnSelection()
       // .getHiddenColumns().size() > 0);
-      originalSource.firePropertyChange("alignment", null, originalSource
-              .getAlignment().getSequences());
+      originalSource.firePropertyChange("alignment", null,
+              originalSource.getAlignment().getSequences());
     }
   }
 
-  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
@@ -1536,16 +1718,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       EditCommand editCommand = (EditCommand) command;
       al = editCommand.getAlignment();
-      Vector comps = (Vector) PaintRefresher.components.get(viewport
-              .getSequenceSetId());
+      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;
           }
         }
@@ -1568,10 +1750,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * DOCUMENT ME!
+   * Calls AlignmentI.moveSelectedSequencesByOne with current sequence selection
+   * or the sequence under cursor in keyboard mode
    * 
    * @param up
-   *          DOCUMENT ME!
+   *          or down (if !up)
    */
   public void moveSelectedSequences(boolean up)
   {
@@ -1579,27 +1762,44 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (sg == null)
     {
+      if (viewport.cursorMode)
+      {
+        sg = new SequenceGroup();
+        sg.addSequence(viewport.getAlignment().getSequenceAt(
+                alignPanel.getSeqPanel().seqCanvas.cursorY), false);
+      }
+      else
+      {
+        return;
+      }
+    }
+
+    if (sg.getSize() < 1)
+    {
       return;
     }
+
+    // TODO: JAL-3733 - add an event to the undo buffer for this !
+
     viewport.getAlignment().moveSelectedSequencesByOne(sg,
             viewport.getHiddenRepSequences(), up);
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
   }
 
   synchronized void slideSequences(boolean right, int size)
   {
-    List<SequenceI> sg = new Vector();
-    if (viewport.cursorMode)
+    List<SequenceI> sg = new ArrayList<>();
+    if (viewport.getSelectionGroup() != null && viewport.getSelectionGroup()
+            .getSize() != viewport.getAlignment().getHeight())
     {
-      sg.add(viewport.getAlignment().getSequenceAt(
-              alignPanel.seqPanel.seqCanvas.cursorY));
+      sg = viewport.getSelectionGroup()
+              .getSequences(viewport.getHiddenRepSequences());
     }
-    else if (viewport.getSelectionGroup() != null
-            && viewport.getSelectionGroup().getSize() != viewport
-                    .getAlignment().getHeight())
+
+    if (sg.size() == 0 && viewport.cursorMode)
     {
-      sg = viewport.getSelectionGroup().getSequences(
-              viewport.getHiddenRepSequences());
+      sg.add(viewport.getAlignment()
+              .getSequenceAt(alignPanel.getSeqPanel().seqCanvas.cursorY));
     }
 
     if (sg.size() < 1)
@@ -1607,42 +1807,58 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    Vector invertGroup = new Vector();
+    List<SequenceI> invertGroup = new ArrayList<>();
 
-    for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
+    for (SequenceI seq : viewport.getAlignment().getSequences())
     {
-      if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
-        invertGroup.add(viewport.getAlignment().getSequenceAt(i));
+      if (!sg.contains(seq))
+      {
+        invertGroup.add(seq);
+      }
     }
 
     SequenceI[] seqs1 = sg.toArray(new SequenceI[0]);
 
     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;
     if (right)
-      ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
-              size, viewport.getGapCharacter());
+    {
+      ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1, size,
+              viewport.getGapCharacter());
+    }
     else
-      ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
-              size, viewport.getGapCharacter());
+    {
+      ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2, size,
+              viewport.getGapCharacter());
+    }
 
     int groupAdjustment = 0;
     if (ssc.getGapsInsertedBegin() && right)
     {
       if (viewport.cursorMode)
-        alignPanel.seqPanel.moveCursor(size, 0);
+      {
+        alignPanel.getSeqPanel().moveCursor(size, 0);
+      }
       else
+      {
         groupAdjustment = size;
+      }
     }
     else if (!ssc.getGapsInsertedBegin() && !right)
     {
       if (viewport.cursorMode)
-        alignPanel.seqPanel.moveCursor(-size, 0);
+      {
+        alignPanel.getSeqPanel().moveCursor(-size, 0);
+      }
       else
+      {
         groupAdjustment = -size;
+      }
     }
 
     if (groupAdjustment != 0)
@@ -1653,17 +1869,24 @@ 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
-                      .peek());
+      appendHistoryItem = ssc.appendSlideCommand(
+              (SlideSequencesCommand) historyList.peek());
     }
 
     if (!appendHistoryItem)
+    {
       addHistoryItem(ssc);
+    }
 
     repaint();
   }
@@ -1677,7 +1900,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void copy_actionPerformed(ActionEvent e)
   {
-    System.gc();
     if (viewport.getSelectionGroup() == null)
     {
       return;
@@ -1692,8 +1914,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       omitHidden = viewport.getViewAsString(true);
     }
 
-    String output = new FormatAdapter().formatSequences("Fasta", seqs,
-            omitHidden);
+    String output = new FormatAdapter().formatSequences(FileFormat.Fasta,
+            seqs, omitHidden, null);
 
     StringSelection ss = new StringSelection(output);
 
@@ -1705,37 +1927,31 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       Toolkit.getDefaultToolkit().getSystemClipboard()
               .setContents(new StringSelection(""), null);
 
-      Toolkit.getDefaultToolkit().getSystemClipboard()
-              .setContents(ss, Desktop.instance);
+      Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss,
+              Desktop.instance);
     } catch (OutOfMemoryError er)
     {
       new OOMWarning("copying region", er);
       return;
     }
 
-    Vector hiddenColumns = null;
+    HiddenColumns hiddenColumns = null;
     if (viewport.hasHiddenColumns())
     {
-      hiddenColumns = new Vector();
-      int hiddenOffset = viewport.getSelectionGroup().getStartRes(), hiddenCutoff = viewport
-              .getSelectionGroup().getEndRes();
-      for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns()
-              .size(); i++)
-      {
-        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 });
-        }
-      }
+      int hiddenOffset = viewport.getSelectionGroup().getStartRes();
+      int hiddenCutoff = viewport.getSelectionGroup().getEndRes();
+
+      // create new HiddenColumns object with copy of hidden regions
+      // between startRes and endRes, offset by startRes
+      hiddenColumns = new HiddenColumns(
+              viewport.getAlignment().getHiddenColumns(), hiddenOffset,
+              hiddenCutoff, 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[]
+            "label.copied_sequences_to_clipboard", new Object[]
             { Integer.valueOf(seqs.length).toString() }));
   }
 
@@ -1782,7 +1998,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         return;
       }
 
-      String str, format;
+      String str;
+      FileFormatI format;
       try
       {
         str = (String) contents.getTransferData(DataFlavor.stringFlavor);
@@ -1791,7 +2008,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           return;
         }
 
-        format = new IdentifyFile().Identify(str, "Paste");
+        format = new IdentifyFile().identify(str, DataSourceType.PASTE);
 
       } catch (OutOfMemoryError er)
       {
@@ -1821,12 +2038,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       else
       {
         // parse the clipboard as an alignment.
-        alignment = new FormatAdapter().readFile(str, "Paste", format);
+        alignment = new FormatAdapter().readFile(str, DataSourceType.PASTE,
+                format);
         sequences = alignment.getSequencesArray();
       }
 
       int alwidth = 0;
-      ArrayList<Integer> newGraphGroups = new ArrayList<Integer>();
+      ArrayList<Integer> newGraphGroups = new ArrayList<>();
       int fgroup = -1;
 
       if (newAlignment)
@@ -1884,8 +2102,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           {
             // copy and derive new dataset sequence
             sequences[i] = sequences[i].deriveSequence();
-            alignment.getDataset().addSequence(
-                    sequences[i].getDatasetSequence());
+            alignment.getDataset()
+                    .addSequence(sequences[i].getDatasetSequence());
             // TODO: avoid creation of duplicate dataset sequences with a
             // 'contains' method using SequenceI.equals()/SequenceI.contains()
           }
@@ -1915,18 +2133,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             annotationAdded = true;
             if (alann[i].sequenceRef == null && !alann[i].autoCalculated)
             {
-              AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
+              AlignmentAnnotation newann = new AlignmentAnnotation(
+                      alann[i]);
               if (newann.graphGroup > -1)
               {
                 if (newGraphGroups.size() <= newann.graphGroup
                         || newGraphGroups.get(newann.graphGroup) == null)
                 {
-                  for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
+                  for (int q = newGraphGroups
+                          .size(); q <= newann.graphGroup; q++)
                   {
                     newGraphGroups.add(q, null);
                   }
-                  newGraphGroups.set(newann.graphGroup, new Integer(
-                          ++fgroup));
+                  newGraphGroups.set(newann.graphGroup,
+                          Integer.valueOf(++fgroup));
                 }
                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
                         .intValue();
@@ -1943,8 +2163,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         // /////
         // ADD HISTORY ITEM
         //
-        addHistoryItem(new EditCommand(MessageManager.getString("label.add_sequences"), EditCommand.PASTE,
-                sequences, 0, alignment.getWidth(), alignment));
+        addHistoryItem(new EditCommand(
+                MessageManager.getString("label.add_sequences"),
+                Action.PASTE, sequences, 0, alignment.getWidth(),
+                alignment));
       }
       // Add any annotations attached to sequences
       for (int i = 0; i < sequences.length; i++)
@@ -1965,12 +2187,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 if (newGraphGroups.size() <= newann.graphGroup
                         || newGraphGroups.get(newann.graphGroup) == null)
                 {
-                  for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
+                  for (int q = newGraphGroups
+                          .size(); q <= newann.graphGroup; q++)
                   {
                     newGraphGroups.add(q, null);
                   }
-                  newGraphGroups.set(newann.graphGroup, new Integer(
-                          ++fgroup));
+                  newGraphGroups.set(newann.graphGroup,
+                          Integer.valueOf(++fgroup));
                 }
                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
                         .intValue();
@@ -1980,8 +2203,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             // was
             // duplicated
             // earlier
-            alignment
-                    .setAnnotationIndex(sequences[i].getAnnotation()[a], a);
+            alignment.setAnnotationIndex(sequences[i].getAnnotation()[a],
+                    a);
           }
         }
       }
@@ -1989,7 +2212,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
 
         // propagate alignment changed.
-        viewport.setEndSeq(alignment.getHeight());
+        viewport.getRanges().setEndSeq(alignment.getHeight());
         if (annotationAdded)
         {
           // Duplicate sequence annotation in all views.
@@ -1998,7 +2221,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           {
             AlignmentAnnotation sann[] = sequences[i].getAnnotation();
             if (sann == null)
+            {
               continue;
+            }
             for (int avnum = 0; avnum < alview.length; avnum++)
             {
               if (alview[avnum] != alignment)
@@ -2029,7 +2254,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);
           }
@@ -2049,19 +2274,15 @@ 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++)
-          {
-            int[] region = (int[]) hc.elementAt(i);
-            af.viewport.hideColumns(region[0], region[1]);
-          }
+          HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
+          af.viewport.setHiddenColumns(hc);
         }
 
         // >>>This is a fix for the moment, until a better solution is
         // found!!<<<
-        af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
-                .transferSettings(
-                        alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
+        af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+                .transferSettings(alignPanel.getSeqPanel().seqCanvas
+                        .getFeatureRenderer());
 
         // TODO: maintain provenance of an alignment, rather than just make the
         // title a concatenation of operations.
@@ -2100,8 +2321,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     try
     {
-      AlignmentI alignment = AlignmentUtils.expandContext(getViewport()
-              .getAlignment(), -1);
+      AlignmentI alignment = AlignmentUtils
+              .expandContext(getViewport().getAlignment(), -1);
       AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
               DEFAULT_HEIGHT);
       String newtitle = new String("Flanking alignment");
@@ -2109,19 +2330,15 @@ 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++)
-        {
-          int[] region = (int[]) hc.elementAt(i);
-          af.viewport.hideColumns(region[0], region[1]);
-        }
+        HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
+        af.viewport.setHiddenColumns(hc);
       }
 
       // >>>This is a fix for the moment, until a better solution is
       // found!!<<<
-      af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
-              .transferSettings(
-                      alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
+      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+              .transferSettings(alignPanel.getSeqPanel().seqCanvas
+                      .getFeatureRenderer());
 
       // TODO: maintain provenance of an alignment, rather than just make the
       // title a concatenation of operations.
@@ -2178,40 +2395,43 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    Vector seqs = new Vector();
-    SequenceI seq;
-    for (int i = 0; i < sg.getSize(); i++)
-    {
-      seq = sg.getSequenceAt(i);
-      seqs.addElement(seq);
-    }
-
-    // If the cut affects all sequences, remove highlighted columns
+    /*
+     * If the cut affects all sequences, warn, remove highlighted columns
+     */
     if (sg.getSize() == viewport.getAlignment().getHeight())
     {
+      boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes())
+              + 1) == viewport.getAlignment().getWidth()) ? true : false;
+      if (isEntireAlignWidth)
+      {
+        int confirm = JvOptionPane.showConfirmDialog(this,
+                MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
+                MessageManager.getString("label.delete_all"), // $NON-NLS-1$
+                JvOptionPane.OK_CANCEL_OPTION);
+
+        if (confirm == JvOptionPane.CANCEL_OPTION
+                || confirm == JvOptionPane.CLOSED_OPTION)
+        {
+          return;
+        }
+      }
       viewport.getColumnSelection().removeElements(sg.getStartRes(),
               sg.getEndRes() + 1);
     }
+    SequenceI[] cut = sg.getSequences()
+            .toArray(new SequenceI[sg.getSize()]);
 
-    SequenceI[] cut = new SequenceI[seqs.size()];
-    for (int i = 0; i < seqs.size(); i++)
-    {
-      cut[i] = (SequenceI) seqs.elementAt(i);
-    }
-
-    /*
-     * //ADD HISTORY ITEM
-     */
-    addHistoryItem(new EditCommand(MessageManager.getString("label.cut_sequences"), EditCommand.CUT, cut,
-            sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
+    addHistoryItem(new EditCommand(
+            MessageManager.getString("label.cut_sequences"), Action.CUT,
+            cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
             viewport.getAlignment()));
 
     viewport.setSelectionGroup(null);
     viewport.sendSelection();
     viewport.getAlignment().deleteGroup(sg);
 
-    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
-            .getSequences());
+    viewport.firePropertyChange("alignment", null,
+            viewport.getAlignment().getSequences());
     if (viewport.getAlignment().getHeight() < 1)
     {
       try
@@ -2236,7 +2456,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
       alignPanel.updateAnnotation();
-      alignPanel.paintAlignment(true);
+      alignPanel.paintAlignment(true, true);
     }
   }
 
@@ -2249,17 +2469,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
   {
-    SequenceGroup sg = new SequenceGroup();
-
-    for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
-    {
-      sg.addSequence(viewport.getAlignment().getSequenceAt(i), false);
-    }
+    SequenceGroup sg = new SequenceGroup(
+            viewport.getAlignment().getSequences());
 
     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
     viewport.setSelectionGroup(sg);
+    viewport.isSelectionGroupChanged(true);
     viewport.sendSelection();
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false, false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
   }
 
@@ -2274,15 +2494,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (viewport.cursorMode)
     {
-      alignPanel.seqPanel.keyboardNo1 = null;
-      alignPanel.seqPanel.keyboardNo2 = null;
+      alignPanel.getSeqPanel().keyboardNo1 = null;
+      alignPanel.getSeqPanel().keyboardNo2 = null;
     }
     viewport.setSelectionGroup(null);
     viewport.getColumnSelection().clear();
-    viewport.setSelectionGroup(null);
-    alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
-    alignPanel.idPanel.idCanvas.searchResults = null;
-    alignPanel.paintAlignment(true);
+    viewport.setSearchResults(null);
+    alignPanel.getIdPanel().getIdCanvas().searchResults = null;
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false, false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2309,8 +2531,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
     }
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
 
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2319,7 +2544,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void invertColSel_actionPerformed(ActionEvent e)
   {
     viewport.invertColumnSelection();
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
     viewport.sendSelection();
   }
 
@@ -2352,7 +2577,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     ColumnSelection colSel = viewport.getColumnSelection();
     int column;
 
-    if (colSel.size() > 0)
+    if (!colSel.isEmpty())
     {
       if (trimLeft)
       {
@@ -2366,8 +2591,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       SequenceI[] seqs;
       if (viewport.getSelectionGroup() != null)
       {
-        seqs = viewport.getSelectionGroup().getSequencesAsArray(
-                viewport.getHiddenRepSequences());
+        seqs = viewport.getSelectionGroup()
+                .getSequencesAsArray(viewport.getHiddenRepSequences());
       }
       else
       {
@@ -2377,22 +2602,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       TrimRegionCommand trimRegion;
       if (trimLeft)
       {
-        trimRegion = new TrimRegionCommand("Remove Left",
-                TrimRegionCommand.TRIM_LEFT, seqs, column,
-                viewport.getAlignment(), viewport.getColumnSelection(),
-                viewport.getSelectionGroup());
-        viewport.setStartRes(0);
+        trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
+                column, viewport.getAlignment());
+        viewport.getRanges().setStartRes(0);
       }
       else
       {
-        trimRegion = new TrimRegionCommand("Remove Right",
-                TrimRegionCommand.TRIM_RIGHT, seqs, column,
-                viewport.getAlignment(), viewport.getColumnSelection(),
-                viewport.getSelectionGroup());
+        trimRegion = new TrimRegionCommand("Remove Right", false, seqs,
+                column, viewport.getAlignment());
       }
 
-      statusBar.setText(MessageManager.formatMessage(
-              "label.removed_columns", new String[]
+      statusBar.setText(MessageManager
+              .formatMessage("label.removed_columns", new String[]
               { Integer.valueOf(trimRegion.getSize()).toString() }));
 
       addHistoryItem(trimRegion);
@@ -2406,8 +2627,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         }
       }
 
-      viewport.firePropertyChange("alignment", null, viewport
-              .getAlignment().getSequences());
+      viewport.firePropertyChange("alignment", null,
+              viewport.getAlignment().getSequences());
     }
   }
 
@@ -2425,8 +2646,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     SequenceI[] seqs;
     if (viewport.getSelectionGroup() != null)
     {
-      seqs = viewport.getSelectionGroup().getSequencesAsArray(
-              viewport.getHiddenRepSequences());
+      seqs = viewport.getSelectionGroup()
+              .getSequencesAsArray(viewport.getHiddenRepSequences());
       start = viewport.getSelectionGroup().getStartRes();
       end = viewport.getSelectionGroup().getEndRes();
     }
@@ -2441,22 +2662,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     addHistoryItem(removeGapCols);
 
-    statusBar.setText(MessageManager.formatMessage(
-            "label.removed_empty_columns", new String[]
+    statusBar.setText(MessageManager
+            .formatMessage("label.removed_empty_columns", new Object[]
             { Integer.valueOf(removeGapCols.getSize()).toString() }));
 
     // This is to maintain viewport position on first residue
     // of first sequence
     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-    int startRes = seq.findPosition(viewport.startRes);
+    ViewportRanges ranges = viewport.getRanges();
+    int startRes = seq.findPosition(ranges.getStartRes());
     // ShiftList shifts;
     // viewport.getAlignment().removeGaps(shifts=new ShiftList());
     // edit.alColumnChanges=shifts.getInverse();
     // if (viewport.hasHiddenColumns)
     // viewport.getColumnSelection().compensateForEdits(shifts);
-    viewport.setStartRes(seq.findIndex(startRes) - 1);
-    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
-            .getSequences());
+    ranges.setStartRes(seq.findIndex(startRes) - 1);
+    viewport.firePropertyChange("alignment", null,
+            viewport.getAlignment().getSequences());
 
   }
 
@@ -2474,8 +2696,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     SequenceI[] seqs;
     if (viewport.getSelectionGroup() != null)
     {
-      seqs = viewport.getSelectionGroup().getSequencesAsArray(
-              viewport.getHiddenRepSequences());
+      seqs = viewport.getSelectionGroup()
+              .getSequencesAsArray(viewport.getHiddenRepSequences());
       start = viewport.getSelectionGroup().getStartRes();
       end = viewport.getSelectionGroup().getEndRes();
     }
@@ -2487,15 +2709,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // This is to maintain viewport position on first residue
     // of first sequence
     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-    int startRes = seq.findPosition(viewport.startRes);
+    int startRes = seq.findPosition(viewport.getRanges().getStartRes());
 
     addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
             viewport.getAlignment()));
 
-    viewport.setStartRes(seq.findIndex(startRes) - 1);
+    viewport.getRanges().setStartRes(seq.findIndex(startRes) - 1);
 
-    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
-            .getSequences());
+    viewport.firePropertyChange("alignment", null,
+            viewport.getAlignment().getSequences());
 
   }
 
@@ -2509,100 +2731,114 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void padGapsMenuitem_actionPerformed(ActionEvent e)
   {
     viewport.setPadGaps(padGapsMenuitem.isSelected());
-    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
-            .getSequences());
-  }
-
-  // else
-  {
-    // if (justifySeqs>0)
-    {
-      // alignment.justify(justifySeqs!=RIGHT_JUSTIFY);
-    }
+    viewport.firePropertyChange("alignment", null,
+            viewport.getAlignment().getSequences());
   }
 
-  // }
-
   /**
-   * DOCUMENT ME!
+   * Opens a Finder dialog
    * 
    * @param e
-   *          DOCUMENT ME!
    */
   @Override
   public void findMenuItem_actionPerformed(ActionEvent e)
   {
-    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);
+    new Finder(alignPanel);
   }
 
   /**
-   * 
-   * @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)
   {
-    AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
-            true);
+    /*
+     * Create a new AlignmentPanel (with its own, new Viewport)
+     */
+    AlignmentPanel newap = new jalview.project.Jalview2XML()
+            .copyAlignPanel(alignPanel);
     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.setGatherViewsHere(false);
+
+    if (viewport.getViewName() == null)
+    {
+      viewport.setViewName(
+              MessageManager.getString("label.view_name_original"));
     }
 
-    newap.av.gatherViewsHere = false;
+    /*
+     * Views share the same edits undo and redo stacks
+     */
+    newap.av.setHistoryList(viewport.getHistoryList());
+    newap.av.setRedoList(viewport.getRedoList());
+
+    /*
+     * copy any visualisation settings that are not saved in the project
+     */
+    newap.av.setColourAppliesToAllGroups(
+            viewport.getColourAppliesToAllGroups());
+
+    /*
+     * 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());
 
-    if (viewport.viewName == null)
+    /*
+     * start up cDNA consensus (if applicable) now mappings are in place
+     */
+    if (newap.av.initComplementConsensus())
     {
-      viewport.viewName = "Original";
+      newap.refresh(true); // adjust layout of annotations
     }
 
-    newap.av.historyList = viewport.historyList;
-    newap.av.redoList = viewport.redoList;
+    newap.av.setViewName(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)
     {
@@ -2614,45 +2850,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
-            .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<Component> comps = PaintRefresher.components
+            .get(viewport.getSequenceSetId());
+
+    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<>();
+    for (Component comp : comps)
     {
-      viewport.gatherViewsHere = true;
+      if (comp instanceof AlignmentPanel)
+      {
+        AlignmentPanel ap = (AlignmentPanel) comp;
+        if (!existingNames.contains(ap.av.getViewName()))
+        {
+          existingNames.add(ap.av.getViewName());
+        }
+      }
     }
-    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);
+    Desktop.explodeViews(this);
   }
 
+  /**
+   * Gather views in separate windows back into a tabbed presentation.
+   */
   @Override
   public void gatherViews_actionPerformed(ActionEvent e)
   {
@@ -2682,23 +2928,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.setShowJVSuffix(seqLimits.isSelected());
 
-    alignPanel.idPanel.idCanvas.setPreferredSize(alignPanel
-            .calculateIdWidth());
-    alignPanel.paintAlignment(true);
+    alignPanel.getIdPanel().getIdCanvas()
+            .setPreferredSize(alignPanel.calculateIdWidth());
+    alignPanel.paintAlignment(true, false);
   }
 
   @Override
   public void idRightAlign_actionPerformed(ActionEvent e)
   {
-    viewport.rightAlignIds = idRightAlign.isSelected();
-    alignPanel.paintAlignment(true);
+    viewport.setRightAlignIds(idRightAlign.isSelected());
+    alignPanel.paintAlignment(false, false);
   }
 
   @Override
   public void centreColumnLabels_actionPerformed(ActionEvent e)
   {
-    viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
-    alignPanel.paintAlignment(true);
+    viewport.setCentreColumnLabels(centreColumnLabelsMenuItem.getState());
+    alignPanel.paintAlignment(false, false);
   }
 
   /*
@@ -2709,10 +2955,15 @@ 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.seqPanel.seqCanvas.searchResults, false);
+      alignPanel.scrollToPosition(viewport.getSearchResults());
     }
   }
 
@@ -2726,7 +2977,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
   {
     viewport.setColourText(colourTextMenuItem.isSelected());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false, false);
   }
 
   /**
@@ -2742,7 +2993,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
@@ -2755,14 +3006,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void showAllColumns_actionPerformed(ActionEvent e)
   {
     viewport.showAllHiddenColumns();
-    repaint();
+    alignPanel.paintAlignment(true, true);
+    viewport.sendSelection();
   }
 
   @Override
   public void hideSelSequences_actionPerformed(ActionEvent e)
   {
     viewport.hideAllSelectedSeqs();
-    alignPanel.paintAlignment(true);
   }
 
   /**
@@ -2781,11 +3032,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // Hide everything by the current selection - this is a hack - we do the
       // invert and then hide
       // first check that there will be visible columns after the invert.
-      if ((viewport.getColumnSelection() != null
-              && viewport.getColumnSelection().getSelected() != null && viewport
-              .getColumnSelection().getSelected().size() > 0)
-              || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg
-                      .getEndRes()))
+      if (viewport.hasSelectedColumns() || (sg != null && sg.getSize() > 0
+              && sg.getStartRes() <= sg.getEndRes()))
       {
         // now invert the sequence set, if required - empty selection implies
         // that no hiding is required.
@@ -2811,8 +3059,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         hideSelSequences_actionPerformed(null);
         hide = true;
       }
-      else if (!(toggleCols && viewport.getColumnSelection().getSelected()
-              .size() > 0))
+      else if (!(toggleCols && viewport.hasSelectedColumns()))
       {
         showAllSeqs_actionPerformed(null);
       }
@@ -2820,7 +3067,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (toggleCols)
     {
-      if (viewport.getColumnSelection().getSelected().size() > 0)
+      if (viewport.hasSelectedColumns())
       {
         hideSelColumns_actionPerformed(null);
         if (!toggleSeqs)
@@ -2846,6 +3093,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void hideAllButSelection_actionPerformed(ActionEvent e)
   {
     toggleHiddenRegions(false, false);
+    viewport.sendSelection();
   }
 
   /*
@@ -2862,7 +3110,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.expandColSelection(sg, false);
     viewport.hideAllSelectedSeqs();
     viewport.hideSelectedColumns();
-    alignPanel.paintAlignment(true);
+    alignPanel.updateLayout();
+    alignPanel.paintAlignment(true, true);
+    viewport.sendSelection();
   }
 
   /*
@@ -2877,14 +3127,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.showAllHiddenColumns();
     viewport.showAllHiddenSeqs();
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, true);
+    viewport.sendSelection();
   }
 
   @Override
   public void hideSelColumns_actionPerformed(ActionEvent e)
   {
     viewport.hideSelectedColumns();
-    alignPanel.paintAlignment(true);
+    alignPanel.updateLayout();
+    alignPanel.paintAlignment(true, true);
+    viewport.sendSelection();
   }
 
   @Override
@@ -2904,7 +3157,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void scaleAbove_actionPerformed(ActionEvent e)
   {
     viewport.setScaleAboveWrapped(scaleAbove.isSelected());
-    alignPanel.paintAlignment(true);
+    alignPanel.updateLayout();
+    alignPanel.paintAlignment(true, false);
   }
 
   /**
@@ -2917,7 +3171,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void scaleLeft_actionPerformed(ActionEvent e)
   {
     viewport.setScaleLeftWrapped(scaleLeft.isSelected());
-    alignPanel.paintAlignment(true);
+    alignPanel.updateLayout();
+    alignPanel.paintAlignment(true, false);
   }
 
   /**
@@ -2930,7 +3185,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void scaleRight_actionPerformed(ActionEvent e)
   {
     viewport.setScaleRightWrapped(scaleRight.isSelected());
-    alignPanel.paintAlignment(true);
+    alignPanel.updateLayout();
+    alignPanel.paintAlignment(true, false);
   }
 
   /**
@@ -2943,7 +3199,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
   {
     viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false, false);
   }
 
   /**
@@ -2956,7 +3212,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void viewTextMenuItem_actionPerformed(ActionEvent e)
   {
     viewport.setShowText(viewTextMenuItem.isSelected());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false, false);
   }
 
   /**
@@ -2969,17 +3225,29 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
   {
     viewport.setRenderGaps(renderGapsMenuItem.isSelected());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false, false);
   }
 
   public FeatureSettings featureSettings;
 
   @Override
+  public FeatureSettingsControllerI getFeatureSettingsUI()
+  {
+    return featureSettings;
+  }
+
+  @Override
   public void featureSettings_actionPerformed(ActionEvent e)
   {
+    showFeatureSettingsUI();
+  }
+
+  @Override
+  public FeatureSettingsControllerI showFeatureSettingsUI()
+  {
     if (featureSettings != null)
     {
-      featureSettings.close();
+      featureSettings.closeOldSettings();
       featureSettings = null;
     }
     if (!showSeqFeatures.isSelected())
@@ -2989,6 +3257,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       showSeqFeatures_actionPerformed(null);
     }
     featureSettings = new FeatureSettings(this);
+    return featureSettings;
   }
 
   /**
@@ -3001,48 +3270,28 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void showSeqFeatures_actionPerformed(ActionEvent evt)
   {
     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
-    alignPanel.paintAlignment(true);
-    if (alignPanel.getOverviewPanel() != null)
-    {
-      alignPanel.getOverviewPanel().updateOverviewImage();
-    }
+    alignPanel.paintAlignment(true, true);
   }
 
   /**
-   * Set or clear 'Show Sequence Features'
+   * Action on toggle of the 'Show annotations' menu item. This shows or hides
+   * the annotations panel as a whole.
    * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
-  {
-    viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
-            .isSelected());
-    if (viewport.getShowSequenceFeaturesHeight())
-    {
-      // ensure we're actually displaying features
-      viewport.setShowSequenceFeatures(true);
-      showSeqFeatures.setSelected(true);
-    }
-    alignPanel.paintAlignment(true);
-    if (alignPanel.getOverviewPanel() != null)
-    {
-      alignPanel.getOverviewPanel().updateOverviewImage();
-    }
-  }
-
-  /**
-   * DOCUMENT ME!
+   * The options to show/hide all annotations should be enabled when the panel
+   * is shown, and disabled when the panel is hidden.
    * 
    * @param e
-   *          DOCUMENT ME!
    */
   @Override
   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
   {
-    viewport.setShowAnnotation(annotationPanelMenuItem.isSelected());
-    alignPanel.setAnnotationVisible(annotationPanelMenuItem.isSelected());
+    final boolean setVisible = annotationPanelMenuItem.isSelected();
+    viewport.setShowAnnotation(setVisible);
+    this.showAllSeqAnnotations.setEnabled(setVisible);
+    this.hideAllSeqAnnotations.setEnabled(setVisible);
+    this.showAllAlAnnotations.setEnabled(setVisible);
+    this.hideAllAlAnnotations.setEnabled(setVisible);
+    alignPanel.updateLayout();
   }
 
   @Override
@@ -3052,14 +3301,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     editPane.setEditable(false);
     StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
             .formatAsHtml();
-    editPane.setText(MessageManager.formatMessage("label.html_content",
-            new String[]
+    editPane.setText(
+            MessageManager.formatMessage("label.html_content", 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[]
+    Desktop.addInternalFrame(frame, MessageManager
+            .formatMessage("label.alignment_properties", new Object[]
             { getTitle() }), 500, 400);
   }
 
@@ -3078,504 +3327,180 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     JInternalFrame frame = new JInternalFrame();
-    OverviewPanel overview = new OverviewPanel(alignPanel);
+    final OverviewPanel overview = new OverviewPanel(alignPanel);
     frame.setContentPane(overview);
-    Desktop.addInternalFrame(frame, MessageManager.formatMessage(
-            "label.overview_params", new String[]
-            { this.getTitle() }), frame.getWidth(), frame.getHeight());
+    Desktop.addInternalFrame(frame, MessageManager
+            .formatMessage("label.overview_params", new Object[]
+            { this.getTitle() }), true, frame.getWidth(), frame.getHeight(),
+            true, true);
     frame.pack();
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
-    frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+    frame.addInternalFrameListener(
+            new javax.swing.event.InternalFrameAdapter()
+            {
+              @Override
+              public void internalFrameClosed(
+                      javax.swing.event.InternalFrameEvent evt)
+              {
+                overview.dispose();
+                alignPanel.setOverviewPanel(null);
+              };
+            });
+    if (getKeyListeners().length > 0)
     {
-      @Override
-      public void internalFrameClosed(
-              javax.swing.event.InternalFrameEvent evt)
-      {
-        alignPanel.setOverviewPanel(null);
-      };
-    });
+      frame.addKeyListener(getKeyListeners()[0]);
+    }
 
     alignPanel.setOverviewPanel(overview);
   }
 
   @Override
-  public void textColour_actionPerformed(ActionEvent e)
+  public void textColour_actionPerformed()
   {
     new TextColourChooser().chooseColour(alignPanel, null);
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+  /*
+   * public void covariationColour_actionPerformed() {
+   * changeColour(new
+   * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
+   * ()[0])); }
    */
   @Override
-  protected void noColourmenuItem_actionPerformed(ActionEvent e)
+  public void annotationColour_actionPerformed()
   {
-    changeColour(null);
+    new AnnotationColourChooser(viewport, alignPanel);
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
   @Override
-  public void clustalColour_actionPerformed(ActionEvent e)
+  public void annotationColumn_actionPerformed(ActionEvent e)
   {
-    changeColour(new ClustalxColourScheme(viewport.getAlignment(),
-            viewport.getHiddenRepSequences()));
+    new AnnotationColumnChooser(viewport, alignPanel);
   }
 
   /**
-   * DOCUMENT ME!
+   * Action on the user checking or unchecking the option to apply the selected
+   * colour scheme to all groups. If unchecked, groups may have their own
+   * independent colour schemes.
    * 
-   * @param e
-   *          DOCUMENT ME!
+   * @param selected
    */
   @Override
-  public void zappoColour_actionPerformed(ActionEvent e)
+  public void applyToAllGroups_actionPerformed(boolean selected)
   {
-    changeColour(new ZappoColourScheme());
+    viewport.setColourAppliesToAllGroups(selected);
   }
 
   /**
-   * DOCUMENT ME!
+   * Action on user selecting a colour from the colour menu
    * 
-   * @param e
-   *          DOCUMENT ME!
+   * @param name
+   *          the name (not the menu item label!) of the colour scheme
    */
   @Override
-  public void taylorColour_actionPerformed(ActionEvent e)
+  public void changeColour_actionPerformed(String name)
   {
-    changeColour(new TaylorColourScheme());
-  }
+    /*
+     * 'User Defined' opens a panel to configure or load a
+     * user-defined colour scheme
+     */
+    if (ResidueColourScheme.USER_DEFINED_MENU.equals(name))
+    {
+      new UserDefinedColours(alignPanel);
+      return;
+    }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void hydrophobicityColour_actionPerformed(ActionEvent e)
-  {
-    changeColour(new HydrophobicColourScheme());
+    /*
+     * otherwise set the chosen colour scheme (or null for 'None')
+     */
+    ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(name,
+            viewport, viewport.getAlignment(),
+            viewport.getHiddenRepSequences());
+    changeColour(cs);
   }
 
   /**
-   * DOCUMENT ME!
+   * Actions on setting or changing the alignment colour scheme
    * 
-   * @param e
-   *          DOCUMENT ME!
+   * @param cs
    */
   @Override
-  public void helixColour_actionPerformed(ActionEvent e)
+  public void changeColour(ColourSchemeI cs)
   {
-    changeColour(new HelixColourScheme());
-  }
+    // TODO: pull up to controller method
+    ColourMenuHelper.setColourSelected(colourMenu, cs);
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void strandColour_actionPerformed(ActionEvent e)
-  {
-    changeColour(new StrandColourScheme());
-  }
+    viewport.setGlobalColourScheme(cs);
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void turnColour_actionPerformed(ActionEvent e)
-  {
-    changeColour(new TurnColourScheme());
+    alignPanel.paintAlignment(true, true);
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Show the PID threshold slider panel
    */
   @Override
-  public void buriedColour_actionPerformed(ActionEvent e)
+  protected void modifyPID_actionPerformed()
   {
-    changeColour(new BuriedColourScheme());
+    SliderPanel.setPIDSliderSource(alignPanel, viewport.getResidueShading(),
+            alignPanel.getViewName());
+    SliderPanel.showPIDSlider();
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void nucleotideColour_actionPerformed(ActionEvent e)
-  {
-    changeColour(new NucleotideColourScheme());
-  }
-
-  @Override
-  public void purinePyrimidineColour_actionPerformed(ActionEvent e)
-  {
-    changeColour(new PurinePyrimidineColourScheme());
-  }
-
-  /*
-   * public void covariationColour_actionPerformed(ActionEvent e) {
-   * changeColour(new
-   * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
-   * ()[0])); }
+   * Show the Conservation slider panel
    */
   @Override
-  public void annotationColour_actionPerformed(ActionEvent e)
-  {
-    new AnnotationColourChooser(viewport, alignPanel);
-  }
-
-  @Override
-  public void rnahelicesColour_actionPerformed(ActionEvent e)
+  protected void modifyConservation_actionPerformed()
   {
-    new RNAHelicesColourChooser(viewport, alignPanel);
+    SliderPanel.setConservationSlider(alignPanel,
+            viewport.getResidueShading(), alignPanel.getViewName());
+    SliderPanel.showConservationSlider();
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Action on selecting or deselecting (Colour) By Conservation
    */
   @Override
-  protected void applyToAllGroups_actionPerformed(ActionEvent e)
-  {
-    viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param cs
-   *          DOCUMENT ME!
-   */
-  public void changeColour(ColourSchemeI cs)
+  public void conservationMenuItem_actionPerformed(boolean selected)
   {
-    // TODO: compare with applet and pull up to model method
-    int threshold = 0;
-
-    if (cs != null)
-    {
-      if (viewport.getAbovePIDThreshold())
-      {
-        threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
-                "Background");
-        cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
-      }
-      else
-      {
-        cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
-      }
-
-      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);
-        }
-      }
-    }
+    modifyConservation.setEnabled(selected);
+    viewport.setConservationSelected(selected);
+    viewport.getResidueShading().setConservationApplied(selected);
 
-    if (alignPanel.getOverviewPanel() != null)
+    changeColour(viewport.getGlobalColourScheme());
+    if (selected)
     {
-      alignPanel.getOverviewPanel().updateOverviewImage();
+      modifyConservation_actionPerformed();
     }
-
-    alignPanel.paintAlignment(true);
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  protected void modifyPID_actionPerformed(ActionEvent e)
-  {
-    if (viewport.getAbovePIDThreshold()
-            && viewport.getGlobalColourScheme() != null)
+    else
     {
-      SliderPanel.setPIDSliderSource(alignPanel,
-              viewport.getGlobalColourScheme(), "Background");
-      SliderPanel.showPIDSlider();
+      SliderPanel.hideConservationSlider();
     }
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Action on selecting or deselecting (Colour) Above PID Threshold
    */
   @Override
-  protected void modifyConservation_actionPerformed(ActionEvent e)
+  public void abovePIDThreshold_actionPerformed(boolean selected)
   {
-    if (viewport.getConservationSelected()
-            && viewport.getGlobalColourScheme() != null)
+    modifyPID.setEnabled(selected);
+    viewport.setAbovePIDThreshold(selected);
+    if (!selected)
     {
-      SliderPanel.setConservationSlider(alignPanel,
-              viewport.getGlobalColourScheme(), "Background");
-      SliderPanel.showConservationSlider();
+      viewport.getResidueShading().setThreshold(0,
+              viewport.isIgnoreGapsConsensus());
     }
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  protected void conservationMenuItem_actionPerformed(ActionEvent e)
-  {
-    viewport.setConservationSelected(conservationMenuItem.isSelected());
-
-    viewport.setAbovePIDThreshold(false);
-    abovePIDThreshold.setSelected(false);
-
-    changeColour(viewport.getGlobalColourScheme());
-
-    modifyConservation_actionPerformed(null);
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void abovePIDThreshold_actionPerformed(ActionEvent e)
-  {
-    viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
-
-    conservationMenuItem.setSelected(false);
-    viewport.setConservationSelected(false);
 
     changeColour(viewport.getGlobalColourScheme());
-
-    modifyPID_actionPerformed(null);
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void userDefinedColour_actionPerformed(ActionEvent e)
-  {
-    if (e.getActionCommand().equals(
-            MessageManager.getString("action.user_defined")))
+    if (selected)
     {
-      new UserDefinedColours(alignPanel, null);
+      modifyPID_actionPerformed();
     }
     else
     {
-      UserColourScheme udc = (UserColourScheme) UserDefinedColours
-              .getUserColourSchemes().get(e.getActionCommand());
-
-      changeColour(udc);
-    }
-  }
-
-  public void updateUserColourMenu()
-  {
-
-    Component[] menuItems = colourMenu.getMenuComponents();
-    int i, iSize = menuItems.length;
-    for (i = 0; i < iSize; i++)
-    {
-      if (menuItems[i].getName() != null
-              && menuItems[i].getName().equals("USER_DEFINED"))
-      {
-        colourMenu.remove(menuItems[i]);
-        iSize--;
-      }
+      SliderPanel.hidePIDSlider();
     }
-    if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
-    {
-      java.util.Enumeration userColours = jalview.gui.UserDefinedColours
-              .getUserColourSchemes().keys();
-
-      while (userColours.hasMoreElements())
-      {
-        final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
-                userColours.nextElement().toString());
-        radioItem.setName("USER_DEFINED");
-        radioItem.addMouseListener(new MouseAdapter()
-        {
-          @Override
-          public void mousePressed(MouseEvent evt)
-          {
-            if (evt.isControlDown()
-                    || SwingUtilities.isRightMouseButton(evt))
-            {
-              radioItem.removeActionListener(radioItem.getActionListeners()[0]);
-
-              int option = JOptionPane.showInternalConfirmDialog(
-                      jalview.gui.Desktop.desktop,
-                      MessageManager
-                              .getString("label.remove_from_default_list"),
-                      MessageManager
-                              .getString("label.remove_user_defined_colour"),
-                      JOptionPane.YES_NO_OPTION);
-              if (option == JOptionPane.YES_OPTION)
-              {
-                jalview.gui.UserDefinedColours
-                        .removeColourFromDefaults(radioItem.getText());
-                colourMenu.remove(radioItem);
-              }
-              else
-              {
-                radioItem.addActionListener(new ActionListener()
-                {
-                  @Override
-                  public void actionPerformed(ActionEvent evt)
-                  {
-                    userDefinedColour_actionPerformed(evt);
-                  }
-                });
-              }
-            }
-          }
-        });
-        radioItem.addActionListener(new ActionListener()
-        {
-          @Override
-          public void actionPerformed(ActionEvent evt)
-          {
-            userDefinedColour_actionPerformed(evt);
-          }
-        });
-
-        colourMenu.insert(radioItem, 15);
-        colours.add(radioItem);
-      }
-    }
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void PIDColour_actionPerformed(ActionEvent e)
-  {
-    changeColour(new PIDColourScheme());
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void BLOSUM62Colour_actionPerformed(ActionEvent e)
-  {
-    changeColour(new Blosum62ColourScheme());
   }
 
   /**
@@ -3588,11 +3513,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
   {
     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
-    AlignmentSorter.sortByPID(viewport.getAlignment(), viewport
-            .getAlignment().getSequenceAt(0), null);
+    AlignmentSorter.sortByPID(viewport.getAlignment(),
+            viewport.getAlignment().getSequenceAt(0));
     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
             viewport.getAlignment()));
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
   }
 
   /**
@@ -3606,9 +3531,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
     AlignmentSorter.sortByID(viewport.getAlignment());
-    addHistoryItem(new OrderCommand("ID Sort", oldOrder,
-            viewport.getAlignment()));
-    alignPanel.paintAlignment(true);
+    addHistoryItem(
+            new OrderCommand("ID Sort", oldOrder, viewport.getAlignment()));
+    alignPanel.paintAlignment(true, false);
   }
 
   /**
@@ -3624,7 +3549,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     AlignmentSorter.sortByLength(viewport.getAlignment());
     addHistoryItem(new OrderCommand("Length Sort", oldOrder,
             viewport.getAlignment()));
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
   }
 
   /**
@@ -3641,7 +3566,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     addHistoryItem(new OrderCommand("Group Sort", oldOrder,
             viewport.getAlignment()));
 
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
   }
 
   /**
@@ -3668,10 +3593,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if ((viewport.getSelectionGroup() == null)
             || (viewport.getSelectionGroup().getSize() < 2))
     {
-      JOptionPane.showInternalMessageDialog(this, MessageManager
-              .getString("label.you_must_select_least_two_sequences"),
+      JvOptionPane.showInternalMessageDialog(this,
+              MessageManager.getString(
+                      "label.you_must_select_least_two_sequences"),
               MessageManager.getString("label.invalid_selection"),
-              JOptionPane.WARNING_MESSAGE);
+              JvOptionPane.WARNING_MESSAGE);
     }
     else
     {
@@ -3683,43 +3609,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void PCAMenuItem_actionPerformed(ActionEvent e)
-  {
-    if (((viewport.getSelectionGroup() != null)
-            && (viewport.getSelectionGroup().getSize() < 4) && (viewport
-            .getSelectionGroup().getSize() > 0))
-            || (viewport.getAlignment().getHeight() < 4))
-    {
-      JOptionPane
-              .showInternalMessageDialog(
-                      this,
-                      MessageManager
-                              .getString("label.principal_component_analysis_must_take_least_four_input_sequences"),
-                      MessageManager
-                              .getString("label.sequence_selection_insufficient"),
-                      JOptionPane.WARNING_MESSAGE);
-
-      return;
-    }
-
-    new PCAPanel(alignPanel);
-  }
-
   @Override
   public void autoCalculate_actionPerformed(ActionEvent e)
   {
     viewport.autoCalculateConsensus = autoCalculate.isSelected();
     if (viewport.autoCalculateConsensus)
     {
-      viewport.firePropertyChange("alignment", null, viewport
-              .getAlignment().getSequences());
+      viewport.firePropertyChange("alignment", null,
+              viewport.getAlignment().getSequences());
     }
   }
 
@@ -3736,83 +3633,25 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
-  {
-    NewTreePanel("AV", "PID", "Average distance tree using PID");
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
-  {
-    NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
-  {
-    NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
-  {
-    NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
-  }
-
-  /**
-   * DOCUMENT ME!
+   * Constructs a tree panel and adds it to the desktop
    * 
    * @param type
-   *          DOCUMENT ME!
-   * @param pwType
-   *          DOCUMENT ME!
-   * @param title
-   *          DOCUMENT ME!
+   *          tree type (NJ or AV)
+   * @param modelName
+   *          name of score model used to compute the tree
+   * @param options
+   *          parameters for the distance or similarity calculation
    */
-  void NewTreePanel(String type, String pwType, String title)
+  void newTreePanel(String type, String modelName,
+          SimilarityParamsI options)
   {
+    String frameTitle = "";
     TreePanel tp;
 
+    boolean onSelection = false;
     if (viewport.getSelectionGroup() != null
             && viewport.getSelectionGroup().getSize() > 0)
     {
-      if (viewport.getSelectionGroup().getSize() < 3)
-      {
-        JOptionPane
-                .showMessageDialog(
-                        Desktop.desktop,
-                        MessageManager
-                                .getString("label.you_need_more_two_sequences_selected_build_tree"),
-                        MessageManager
-                                .getString("label.not_enough_sequences"),
-                        JOptionPane.WARNING_MESSAGE);
-        return;
-      }
-
       SequenceGroup sg = viewport.getSelectionGroup();
 
       /* Decide if the selection is a column region */
@@ -3820,57 +3659,39 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         if (_s.getLength() < sg.getEndRes())
         {
-          JOptionPane
-                  .showMessageDialog(
-                          Desktop.desktop,
-                          MessageManager
-                                  .getString("label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
-                          MessageManager
-                                  .getString("label.sequences_selection_not_aligned"),
-                          JOptionPane.WARNING_MESSAGE);
+          JvOptionPane.showMessageDialog(Desktop.desktop,
+                  MessageManager.getString(
+                          "label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
+                  MessageManager.getString(
+                          "label.sequences_selection_not_aligned"),
+                  JvOptionPane.WARNING_MESSAGE);
 
           return;
         }
       }
-
-      title = title + " on region";
-      tp = new TreePanel(alignPanel, type, pwType);
+      onSelection = true;
     }
     else
     {
-      // are the visible sequences aligned?
-      if (!viewport.getAlignment().isAligned(false))
-      {
-        JOptionPane
-                .showMessageDialog(
-                        Desktop.desktop,
-                        MessageManager
-                                .getString("label.sequences_must_be_aligned_before_creating_tree"),
-                        MessageManager
-                                .getString("label.sequences_not_aligned"),
-                        JOptionPane.WARNING_MESSAGE);
-
-        return;
-      }
-
       if (viewport.getAlignment().getHeight() < 2)
       {
         return;
       }
-
-      tp = new TreePanel(alignPanel, type, pwType);
     }
 
-    title += " from ";
+    tp = new TreePanel(alignPanel, type, modelName, options);
+    frameTitle = tp.getPanelTitle() + (onSelection ? " on region" : "");
+
+    frameTitle += " from ";
 
-    if (viewport.viewName != null)
+    if (viewport.getViewName() != null)
     {
-      title += viewport.viewName + " of ";
+      frameTitle += viewport.getViewName() + " of ";
     }
 
-    title += this.title;
+    frameTitle += this.title;
 
-    Desktop.addInternalFrame(tp, title, 600, 500);
+    Desktop.addInternalFrame(tp, frameTitle, 600, 500);
   }
 
   /**
@@ -3884,7 +3705,9 @@ 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()
     {
@@ -3897,10 +3720,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         // pointers
         AlignmentSorter.sortBy(viewport.getAlignment(), order);
 
-        addHistoryItem(new OrderCommand(order.getName(), oldOrder, viewport
-                .getAlignment()));
+        addHistoryItem(new OrderCommand(order.getName(), oldOrder,
+                viewport.getAlignment()));
 
-        alignPanel.paintAlignment(true);
+        alignPanel.paintAlignment(true, false);
       }
     });
   }
@@ -3929,7 +3752,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 viewport.getAlignment());// ,viewport.getSelectionGroup());
         addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
                 viewport.getAlignment()));
-        alignPanel.paintAlignment(true);
+        alignPanel.paintAlignment(true, false);
       }
     });
   }
@@ -3954,7 +3777,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    if (viewport.getAlignment().getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
+    if (viewport.getAlignment().getAlignmentAnnotation()
+            .hashCode() != _annotationScoreVectorHash)
     {
       sortByAnnotScore.removeAll();
       // almost certainly a quicker way to do this - but we keep it simple
@@ -3991,63 +3815,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * call. Listeners are added to remove the menu item when the treePanel is
    * closed, and adjust the tree leaf to sequence mapping when the alignment is
    * modified.
-   * 
-   * @param treePanel
-   *          Displayed tree window.
-   * @param title
-   *          SortBy menu item title.
    */
   @Override
-  public void buildTreeMenu()
+  public void buildTreeSortMenu()
   {
-    calculateTree.removeAll();
-    // build the calculate menu
-
-    for (final String type : new String[]
-    { "NJ", "AV" })
-    {
-      String treecalcnm = MessageManager.getString("label.tree_calc_"
-              + type.toLowerCase());
-      for (final Object pwtype : ResidueProperties.scoreMatrices.keySet())
-      {
-        JMenuItem tm = new JMenuItem();
-        ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
-        if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
-        {
-          String smn = MessageManager.getStringOrReturn(
-                  "label.score_model_", sm.getName());
-          final String title = MessageManager.formatMessage(
-                  "label.treecalc_title", treecalcnm, smn);
-          tm.setText(title);//
-          tm.addActionListener(new java.awt.event.ActionListener()
-          {
-            public void actionPerformed(ActionEvent e)
-            {
-              NewTreePanel(type, (String) pwtype, title);
-            }
-          });
-          calculateTree.add(tm);
-        }
-
-      }
-    }
     sortByTreeMenu.removeAll();
 
-    Vector comps = (Vector) PaintRefresher.components.get(viewport
-            .getSequenceSetId());
-    Vector treePanels = new Vector();
-    int i, iSize = comps.size();
-    for (i = 0; i < iSize; i++)
+    List<Component> comps = PaintRefresher.components
+            .get(viewport.getSequenceSetId());
+    List<TreePanel> treePanels = new ArrayList<>();
+    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;
@@ -4055,17 +3840,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));
 
         }
@@ -4084,7 +3867,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       addHistoryItem(new OrderCommand(undoname, oldOrder,
               viewport.getAlignment()));
     }
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
     return true;
   }
 
@@ -4112,16 +3895,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
        */
       msa = viewport.getAlignmentView(true);
     }
+    else if (viewport.getSelectionGroup() != null
+            && viewport.getSelectionGroup().getSize() == 1)
+    {
+      int option = JvOptionPane.showConfirmDialog(this,
+              MessageManager.getString("warn.oneseq_msainput_selection"),
+              MessageManager.getString("label.invalid_selection"),
+              JvOptionPane.OK_CANCEL_OPTION);
+      if (option == JvOptionPane.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;
@@ -4153,8 +3940,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // 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!
 
@@ -4169,73 +3955,59 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    *          DOCUMENT ME!
    */
   @Override
-  protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
+  protected void loadTreeMenuItem_actionPerformed(ActionEvent e)
   {
     // Pick the tree file
     JalviewFileChooser chooser = new JalviewFileChooser(
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
     chooser.setFileView(new JalviewFileView());
-    chooser.setDialogTitle(MessageManager
-            .getString("label.select_newick_like_tree_file"));
-    chooser.setToolTipText(MessageManager.getString("label.load_tree_file"));
+    chooser.setDialogTitle(
+            MessageManager.getString("label.select_newick_like_tree_file"));
+    chooser.setToolTipText(
+            MessageManager.getString("label.load_tree_file"));
 
     int value = chooser.showOpenDialog(null);
 
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
-      String choice = chooser.getSelectedFile().getPath();
-      jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
-      jalview.io.NewickFile fin = null;
+      String filePath = chooser.getSelectedFile().getPath();
+      Cache.setProperty("LAST_DIRECTORY", filePath);
+      NewickFile fin = null;
       try
       {
-        fin = new jalview.io.NewickFile(choice, "File");
-        viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
+        fin = new NewickFile(filePath, DataSourceType.FILE);
+        viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
       } catch (Exception ex)
       {
-        JOptionPane
-                .showMessageDialog(
-                        Desktop.desktop,
-                        ex.getMessage(),
-                        MessageManager
-                                .getString("label.problem_reading_tree_file"),
-                        JOptionPane.WARNING_MESSAGE);
+        JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
+                MessageManager.getString("label.problem_reading_tree_file"),
+                JvOptionPane.WARNING_MESSAGE);
         ex.printStackTrace();
       }
       if (fin != null && fin.hasWarningMessage())
       {
-        JOptionPane.showMessageDialog(Desktop.desktop, fin
-                .getWarningMessage(), MessageManager
-                .getString("label.possible_problem_with_tree_file"),
-                JOptionPane.WARNING_MESSAGE);
+        JvOptionPane.showMessageDialog(Desktop.desktop,
+                fin.getWarningMessage(),
+                MessageManager
+                        .getString("label.possible_problem_with_tree_file"),
+                JvOptionPane.WARNING_MESSAGE);
       }
     }
   }
 
-  @Override
-  protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
+  public TreePanel showNewickTree(NewickFile nf, String treeTitle)
   {
-    changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
+    return showNewickTree(nf, treeTitle, 600, 500, 4, 5);
   }
 
-  public TreePanel ShowNewickTree(NewickFile nf, String title)
-  {
-    return ShowNewickTree(nf, title, 600, 500, 4, 5);
-  }
-
-  public TreePanel ShowNewickTree(NewickFile nf, String title,
-          AlignmentView input)
-  {
-    return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
-  }
-
-  public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
+  public TreePanel showNewickTree(NewickFile nf, String treeTitle, int w,
           int h, int x, int y)
   {
-    return ShowNewickTree(nf, title, null, w, h, x, y);
+    return showNewickTree(nf, treeTitle, null, w, h, x, y);
   }
 
   /**
-   * Add a treeviewer for the tree extracted from a newick file object to the
+   * Add a treeviewer for the tree extracted from a Newick file object to the
    * current alignment view
    * 
    * @param nf
@@ -4254,7 +4026,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    *          position
    * @return TreePanel handle
    */
-  public TreePanel ShowNewickTree(NewickFile nf, String title,
+  public TreePanel showNewickTree(NewickFile nf, String treeTitle,
           AlignmentView input, int w, int h, int x, int y)
   {
     TreePanel tp = null;
@@ -4265,7 +4037,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       if (nf.getTree() != null)
       {
-        tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
+        tp = new TreePanel(alignPanel, nf, treeTitle, input);
 
         tp.setSize(w, h);
 
@@ -4274,7 +4046,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           tp.setLocation(x, y);
         }
 
-        Desktop.addInternalFrame(tp, title, w, h);
+        Desktop.addInternalFrame(tp, treeTitle, w, h);
       }
     } catch (Exception ex)
     {
@@ -4301,7 +4073,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       } catch (Exception e)
       {
       }
-      ;
     }
     final AlignFrame me = this;
     buildingMenu = true;
@@ -4310,11 +4081,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       @Override
       public void run()
       {
-        final List<JMenuItem> legacyItems = new ArrayList<JMenuItem>();
+        final List<JMenuItem> legacyItems = new ArrayList<>();
         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
@@ -4325,13 +4096,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           // TODO: group services by location as well as function and/or
           // introduce
           // object broker mechanism.
-          final Vector<JMenu> wsmenu = new Vector<JMenu>();
+          final Vector<JMenu> wsmenu = new Vector<>();
           final IProgressIndicator af = me;
-          final JMenu msawsmenu = new JMenu(MessageManager.getString("label.alignment"));
-          final JMenu secstrmenu = new JMenu(MessageManager.getString("label.secondary_structure_prediction"));
-          final JMenu seqsrchmenu = new JMenu(MessageManager.getString("label.sequence_database_search"));
-          final JMenu analymenu = new JMenu(MessageManager.getString("label.analysis"));
-          final JMenu dismenu = new JMenu(MessageManager.getString("label.protein_disorder"));
+
+          /*
+           * do not i18n these strings - they are hard-coded in class
+           * compbio.data.msa.Category, Jws2Discoverer.isRecalculable() and
+           * SequenceAnnotationWSClient.initSequenceAnnotationWSClient()
+           */
+          final JMenu msawsmenu = new JMenu("Alignment");
+          final JMenu secstrmenu = new JMenu(
+                  "Secondary Structure Prediction");
+          final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
+          final JMenu analymenu = new JMenu("Analysis");
+          final JMenu dismenu = new JMenu("Protein Disorder");
           // JAL-940 - only show secondary structure prediction services from
           // the legacy server
           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
@@ -4396,9 +4174,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                   webService.add(me.webServiceNoServices);
                 }
                 // TODO: move into separate menu builder class.
-                boolean new_sspred = false;
-                if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
                 {
+                  // logic for 2.11.1.4 is
+                  // always look to see if there is a discover. if there isn't
+                  // we can't show any Jws2 services
+                  // if there are services available, show them - regardless of
+                  // the 'show JWS2 preference'
+                  // if the discoverer is running then say so
+                  // otherwise offer to trigger discovery if 'show JWS2' is not
+                  // enabled
                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
                   if (jws2servs != null)
                   {
@@ -4415,8 +4199,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                           }
                         }
                       }
-
                     }
+
                     if (jws2servs.isRunning())
                     {
                       JMenuItem tm = new JMenuItem(
@@ -4424,6 +4208,26 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       tm.setEnabled(false);
                       webService.add(tm);
                     }
+                    else if (!Cache.getDefault("SHOW_JWS2_SERVICES", true))
+                    {
+                      JMenuItem enableJws2 = new JMenuItem(
+                              "Discover Web Services");
+                      enableJws2.setToolTipText(
+                              "Select to start JABA Web Service discovery (or enable option in Web Service preferences)");
+                      enableJws2.setEnabled(true);
+                      enableJws2.addActionListener(new ActionListener()
+                      {
+
+                        @Override
+                        public void actionPerformed(ActionEvent e)
+                        {
+                          // start service discoverer, but ignore preference
+                          Desktop.instance.startServiceDiscovery(false,
+                                  true);
+                        }
+                      });
+                      webService.add(enableJws2);
+                    }
                   }
                 }
                 build_urlServiceMenu(me.webService);
@@ -4441,18 +4245,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 }
               } catch (Exception e)
               {
-                Cache.log
-                        .debug("Exception during web service menu building process.",
-                                e);
+                Cache.log.debug(
+                        "Exception during web service menu building process.",
+                        e);
               }
-              ;
             }
           });
         } catch (Exception e)
         {
         }
-        ;
-
         buildingMenu = false;
       }
     }).start();
@@ -4493,81 +4294,47 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  /*
-   * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
-   * chooser = new JalviewFileChooser(jalview.bin.Cache.
-   * getProperty("LAST_DIRECTORY"));
-   * 
-   * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
-   * to Vamsas file"); chooser.setToolTipText("Export");
-   * 
-   * int value = chooser.showSaveDialog(this);
-   * 
-   * if (value == JalviewFileChooser.APPROVE_OPTION) {
-   * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
-   * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
-   * chooser.getSelectedFile().getAbsolutePath(), this); } }
-   */
   /**
-   * prototype of an automatically enabled/disabled analysis function
+   * Searches the alignment sequences for xRefs and builds the Show
+   * Cross-References menu (formerly called Show Products), with database
+   * sources for which cross-references are found (protein sources for a
+   * nucleotide alignment and vice versa)
    * 
+   * @return true if Show Cross-references menu should be enabled
    */
-  protected void setShowProductsEnabled()
+  public boolean canShowProducts()
   {
-    SequenceI[] selection = viewport.getSequenceSelection();
-    if (canShowProducts(selection, viewport.getSelectionGroup() != null,
-            viewport.getAlignment().getDataset()))
-    {
-      showProducts.setEnabled(true);
+    SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
+    AlignmentI dataset = viewport.getAlignment().getDataset();
 
-    }
-    else
+    showProducts.removeAll();
+    final boolean dna = viewport.getAlignment().isNucleotide();
+
+    if (seqs == null || seqs.length == 0)
     {
-      showProducts.setEnabled(false);
+      // nothing to see here.
+      return false;
     }
-  }
 
-  /**
-   * search selection for sequence xRef products and build the show products
-   * menu.
-   * 
-   * @param selection
-   * @param dataset
-   * @return true if showProducts menu should be enabled.
-   */
-  public boolean canShowProducts(SequenceI[] selection,
-          boolean isRegionSelection, Alignment dataset)
-  {
     boolean showp = false;
     try
     {
-      showProducts.removeAll();
-      final boolean dna = viewport.getAlignment().isNucleotide();
-      final Alignment ds = dataset;
-      String[] ptypes = (selection == null || selection.length == 0) ? null
-              : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
-      // Object[] prods =
-      // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
-      // selection, dataset, true);
-      final SequenceI[] sel = selection;
-      for (int t = 0; ptypes != null && t < ptypes.length; t++)
+      List<String> ptypes = new CrossRef(seqs, dataset)
+              .findXrefSourcesForSequences(dna);
+
+      for (final String source : ptypes)
       {
         showp = true;
-        final boolean isRegSel = isRegionSelection;
         final AlignFrame af = this;
-        final String source = ptypes[t];
-        JMenuItem xtype = new JMenuItem(ptypes[t]);
+        JMenuItem xtype = new JMenuItem(source);
         xtype.addActionListener(new ActionListener()
         {
-
           @Override
           public void actionPerformed(ActionEvent e)
           {
-            // TODO: new thread for this call with vis-delay
-            af.showProductsFor(af.viewport.getSequenceSelection(), ds,
-                    isRegSel, dna, source);
+            showProductsFor(af.viewport.getSequenceSelection(), dna,
+                    source);
           }
-
         });
         showProducts.add(xtype);
       }
@@ -4575,238 +4342,126 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       showProducts.setEnabled(showp);
     } catch (Exception e)
     {
-      jalview.bin.Cache.log
-              .warn("canTranslate threw an exception - please report to help@jalview.org",
-                      e);
+      Cache.log.warn(
+              "canShowProducts threw an exception - please report to help@jalview.org",
+              e);
       return false;
     }
     return showp;
   }
 
-  protected void showProductsFor(SequenceI[] sel, Alignment ds,
-          boolean isRegSel, boolean dna, 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()
-    {
-
-      @Override
-      public void run()
-      {
-        final long sttime = System.currentTimeMillis();
-        ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new String[]{fsrc}), sttime);
-        try
-        {
-          Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
-          // our local
-          // dataset
-          // reference
-          Alignment prods = CrossRef
-                  .findXrefSequences(fsel, fdna, fsrc, ds);
-          if (prods != null)
-          {
-            SequenceI[] sprods = new SequenceI[prods.getHeight()];
-            for (int s = 0; s < sprods.length; s++)
-            {
-              sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
-              if (ds.getSequences() == null
-                      || !ds.getSequences().contains(
-                              sprods[s].getDatasetSequence()))
-                ds.addSequence(sprods[s].getDatasetSequence());
-              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++)
-            {
-              al.addCodonFrame(cf[s]);
-              cf[s] = null;
-            }
-            AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
-                    DEFAULT_HEIGHT);
-            String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
-                    + " for " + ((fisRegSel) ? "selected region of " : "")
-                    + getTitle();
-            Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
-                    DEFAULT_HEIGHT);
-          }
-          else
-          {
-            System.err.println("No Sequences generated for xRef type "
-                    + fsrc);
-          }
-        } catch (Exception e)
-        {
-          jalview.bin.Cache.log.error(
-                  "Exception when finding crossreferences", e);
-        } catch (OutOfMemoryError e)
-        {
-          new OOMWarning("whilst fetching crossreferences", e);
-        } catch (Error e)
-        {
-          jalview.bin.Cache.log.error("Error when finding crossreferences",
-                  e);
-        }
-        ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new String[]{fsrc}),
-                sttime);
-      }
-
-    };
-    Thread frunner = new Thread(foo);
-    frunner.start();
-  }
-
-  public boolean canShowTranslationProducts(SequenceI[] selection,
-          AlignmentI alignment)
+  /**
+   * Finds and displays cross-references for the selected sequences (protein
+   * products for nucleotide sequences, dna coding sequences for peptides).
+   * 
+   * @param sel
+   *          the sequences to show cross-references for
+   * @param dna
+   *          true if from a nucleotide alignment (so showing proteins)
+   * @param source
+   *          the database to show cross-references for
+   */
+  protected void showProductsFor(final SequenceI[] sel, final boolean _odna,
+          final String source)
   {
-    // old way
-    try
-    {
-      return (jalview.analysis.Dna.canTranslate(selection,
-              viewport.getViewAsVisibleContigs(true)));
-    } catch (Exception e)
-    {
-      jalview.bin.Cache.log
-              .warn("canTranslate threw an exception - please report to help@jalview.org",
-                      e);
-      return false;
-    }
+    new Thread(CrossRefAction.getHandlerFor(sel, _odna, source, this))
+            .start();
   }
 
+  /**
+   * 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 showProducts_actionPerformed(ActionEvent e)
+  public void showTranslation_actionPerformed(GeneticCodeI codeTable)
   {
-    // /////////////////////////////
-    // 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);
-    }
-  }
+      Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
 
-  @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());
+      al = dna.translateCdna(codeTable);
     } 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("label.translation_failed");
+      JvOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
+              JvOptionPane.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");
+      JvOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
+              JvOptionPane.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(), codeTable.getId() });
+      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 format
+   */
+  public void setFileFormat(FileFormatI format)
+  {
+    this.currentFileFormat = format;
+  }
+
+  /**
    * Try to load a features file onto the alignment.
    * 
    * @param file
    *          contents or path to retrieve file
-   * @param type
+   * @param sourceType
    *          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)
+  public boolean parseFeaturesFile(String file, DataSourceType sourceType)
   {
-    boolean featuresFile = false;
-    try
-    {
-      featuresFile = new FeaturesFile(file, type).parse(viewport
-              .getAlignment().getDataset(), alignPanel.seqPanel.seqCanvas
-              .getFeatureRenderer().featureColours, false,
-              jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
-    }
+    return avc.parseFeaturesFile(file, sourceType,
+            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.seqPanel.seqCanvas.fr != null)
-      {
-        // update the min/max ranges where necessary
-        alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
-      }
-      if (featureSettings != null)
-      {
-        featureSettings.setTableData();
-      }
-      alignPanel.paintAlignment(true);
     }
 
-    return featuresFile;
   }
 
   @Override
@@ -4832,224 +4487,210 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void drop(DropTargetDropEvent evt)
   {
+    // JAL-1552 - acceptDrop required before getTransferable call for
+    // Java's Transferable for native dnd
+    evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
-    java.util.List files = null;
+    final AlignFrame thisaf = this;
+    final List<String> files = new ArrayList<>();
+    List<DataSourceType> protocols = new ArrayList<>();
 
     try
     {
-      DataFlavor uriListFlavor = new DataFlavor(
-              "text/uri-list;class=java.lang.String");
-      if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
-      {
-        // Works on Windows and MacOSX
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        files = (java.util.List) t
-                .getTransferData(DataFlavor.javaFileListFlavor);
-      }
-      else if (t.isDataFlavorSupported(uriListFlavor))
-      {
-        // This is used by Unix drag system
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        String data = (String) t.getTransferData(uriListFlavor);
-        files = new java.util.ArrayList(1);
-        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
-                data, "\r\n"); st.hasMoreTokens();)
-        {
-          String s = st.nextToken();
-          if (s.startsWith("#"))
-          {
-            // the line is a comment (as per the RFC 2483)
-            continue;
-          }
-
-          java.net.URI uri = new java.net.URI(s);
-          // check to see if we can handle this kind of URI
-          if (uri.getScheme().toLowerCase().startsWith("http"))
-          {
-            files.add(uri.toString());
-          }
-          else
-          {
-            // otherwise preserve old behaviour: catch all for file objects
-            java.io.File file = new java.io.File(uri);
-            files.add(file.toString());
-          }
-        }
-      }
+      Desktop.transferFromDropTarget(files, protocols, evt, t);
     } catch (Exception e)
     {
       e.printStackTrace();
     }
     if (files != null)
     {
-      try
+      new Thread(new Runnable()
       {
-        // check to see if any of these files have names matching sequences in
-        // the alignment
-        SequenceIdMatcher idm = new SequenceIdMatcher(viewport
-                .getAlignment().getSequencesArray());
-        /**
-         * Object[] { String,SequenceI}
-         */
-        ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
-        ArrayList<String> filesnotmatched = new ArrayList<String>();
-        for (int i = 0; i < files.size(); i++)
+        @Override
+        public void run()
         {
-          String file = files.get(i).toString();
-          String pdbfn = "";
-          String protocol = FormatAdapter.checkProtocol(file);
-          if (protocol == jalview.io.FormatAdapter.FILE)
-          {
-            File fl = new File(file);
-            pdbfn = fl.getName();
-          }
-          else if (protocol == jalview.io.FormatAdapter.URL)
-          {
-            URL url = new URL(file);
-            pdbfn = url.getFile();
-          }
-          if (pdbfn.length() > 0)
+          try
           {
-            // attempt to find a match in the alignment
-            SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
-            int l = 0, c = pdbfn.indexOf(".");
-            while (mtch == null && c != -1)
+            // check to see if any of these files have names matching sequences
+            // in
+            // the alignment
+            SequenceIdMatcher idm = new SequenceIdMatcher(
+                    viewport.getAlignment().getSequencesArray());
+            /**
+             * Object[] { String,SequenceI}
+             */
+            ArrayList<Object[]> filesmatched = new ArrayList<>();
+            ArrayList<String> filesnotmatched = new ArrayList<>();
+            for (int i = 0; i < files.size(); i++)
             {
-              do
+              String file = files.get(i).toString();
+              String pdbfn = "";
+              DataSourceType protocol = FormatAdapter.checkProtocol(file);
+              if (protocol == DataSourceType.FILE)
+              {
+                File fl = new File(file);
+                pdbfn = fl.getName();
+              }
+              else if (protocol == DataSourceType.URL)
               {
-                l = c;
-              } while ((c = pdbfn.indexOf(".", l)) > l);
-              if (l > -1)
+                URL url = new URL(file);
+                pdbfn = url.getFile();
+              }
+              if (pdbfn.length() > 0)
               {
-                pdbfn = pdbfn.substring(0, l);
+                // attempt to find a match in the alignment
+                SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
+                int l = 0, c = pdbfn.indexOf(".");
+                while (mtch == null && c != -1)
+                {
+                  do
+                  {
+                    l = c;
+                  } while ((c = pdbfn.indexOf(".", l)) > l);
+                  if (l > -1)
+                  {
+                    pdbfn = pdbfn.substring(0, l);
+                  }
+                  mtch = idm.findAllIdMatches(pdbfn);
+                }
+                if (mtch != null)
+                {
+                  FileFormatI type = null;
+                  try
+                  {
+                    type = new IdentifyFile().identify(file, protocol);
+                  } catch (Exception ex)
+                  {
+                    type = null;
+                  }
+                  if (type != null && type.isStructureFile())
+                  {
+                    filesmatched.add(new Object[] { file, protocol, mtch });
+                    continue;
+                  }
+                }
+                // File wasn't named like one of the sequences or wasn't a PDB
+                // file.
+                filesnotmatched.add(file);
               }
-              mtch = idm.findAllIdMatches(pdbfn);
             }
-            if (mtch != null)
+            int assocfiles = 0;
+            if (filesmatched.size() > 0)
             {
-              String type = null;
-              try
+              boolean autoAssociate = Cache
+                      .getDefault("AUTOASSOCIATE_PDBANDSEQS", false);
+              if (!autoAssociate)
               {
-                type = new IdentifyFile().Identify(file, protocol);
-              } catch (Exception ex)
+                String msg = MessageManager.formatMessage(
+                        "label.automatically_associate_structure_files_with_sequences_same_name",
+                        new Object[]
+                        { Integer.valueOf(filesmatched.size())
+                                .toString() });
+                String ttl = MessageManager.getString(
+                        "label.automatically_associate_structure_files_by_name");
+                int choice = JvOptionPane.showConfirmDialog(thisaf, msg,
+                        ttl, JvOptionPane.YES_NO_OPTION);
+                autoAssociate = choice == JvOptionPane.YES_OPTION;
+              }
+              if (autoAssociate)
               {
-                type = null;
+                for (Object[] fm : filesmatched)
+                {
+                  // try and associate
+                  // TODO: may want to set a standard ID naming formalism for
+                  // associating PDB files which have no IDs.
+                  for (SequenceI toassoc : (SequenceI[]) fm[2])
+                  {
+                    PDBEntry pe = new AssociatePdbFileWithSeq()
+                            .associatePdbWithSeq((String) fm[0],
+                                    (DataSourceType) fm[1], toassoc, false,
+                                    Desktop.instance);
+                    if (pe != null)
+                    {
+                      System.err.println("Associated file : "
+                              + ((String) fm[0]) + " with "
+                              + toassoc.getDisplayId(true));
+                      assocfiles++;
+                    }
+                  }
+                  // TODO: do we need to update overview ? only if features are
+                  // shown I guess
+                  alignPanel.paintAlignment(true, false);
+                }
               }
-              if (type != null)
+              else
               {
-                if (type.equalsIgnoreCase("PDB"))
+                /*
+                 * add declined structures as sequences
+                 */
+                for (Object[] o : filesmatched)
                 {
-                  filesmatched.add(new Object[]
-                  { file, protocol, mtch });
-                  continue;
+                  filesnotmatched.add((String) o[0]);
                 }
               }
             }
-            // File wasn't named like one of the sequences or wasn't a PDB file.
-            filesnotmatched.add(file);
-          }
-        }
-        int assocfiles = 0;
-        if (filesmatched.size() > 0)
-        {
-          if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
-                  || JOptionPane
-                          .showConfirmDialog(
-                                  this,
-                                  MessageManager
-                                          .formatMessage(
-                                                  "label.automatically_associate_pdb_files_with_sequences_same_name",
-                                                  new String[]
-                                                  { Integer.valueOf(
-                                                          filesmatched
-                                                                  .size())
-                                                          .toString() }),
-                                  MessageManager
-                                          .getString("label.automatically_associate_pdb_files_by_name"),
-                                  JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
-
-          {
-            for (Object[] fm : filesmatched)
+            if (filesnotmatched.size() > 0)
             {
-              // try and associate
-              // TODO: may want to set a standard ID naming formalism for
-              // associating PDB files which have no IDs.
-              for (SequenceI toassoc : (SequenceI[]) fm[2])
+              if (assocfiles > 0 && (Cache.getDefault(
+                      "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
+                      || JvOptionPane.showConfirmDialog(thisaf,
+                              "<html>" + MessageManager.formatMessage(
+                                      "label.ignore_unmatched_dropped_files_info",
+                                      new Object[]
+                                      { Integer.valueOf(
+                                              filesnotmatched.size())
+                                              .toString() })
+                                      + "</html>",
+                              MessageManager.getString(
+                                      "label.ignore_unmatched_dropped_files"),
+                              JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION))
               {
-                PDBEntry pe = new AssociatePdbFileWithSeq()
-                        .associatePdbWithSeq((String) fm[0],
-                                (String) fm[1], toassoc, false);
-                if (pe != null)
-                {
-                  System.err.println("Associated file : "
-                          + ((String) fm[0]) + " with "
-                          + toassoc.getDisplayId(true));
-                  assocfiles++;
-                }
+                return;
+              }
+              for (String fn : filesnotmatched)
+              {
+                loadJalviewDataFile(fn, null, null, null);
               }
-              alignPanel.paintAlignment(true);
+
             }
-          }
-        }
-        if (filesnotmatched.size() > 0)
-        {
-          if (assocfiles > 0
-                  && (Cache.getDefault(
-                          "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
-                          .showConfirmDialog(
-                                  this,
-                                  MessageManager
-                                          .formatMessage(
-                                                  "label.ignore_unmatched_dropped_files_info",
-                                                  new String[]
-                                                  { Integer.valueOf(
-                                                          filesnotmatched
-                                                                  .size())
-                                                          .toString() }),
-                                  MessageManager
-                                          .getString("label.ignore_unmatched_dropped_files"),
-                                  JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
-          {
-            return;
-          }
-          for (String fn : filesnotmatched)
+          } catch (Exception ex)
           {
-            loadJalviewDataFile(fn, null, null, null);
+            ex.printStackTrace();
           }
-
         }
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
-      }
+      }).start();
     }
   }
 
   /**
-   * Attempt to load a "dropped" file or URL string: First by testing whether
-   * it's and Annotation file, then a JNet file, and finally a features file. If
-   * all are false then the user may have dropped an alignment file onto this
-   * AlignFrame.
+   * Attempt to load a "dropped" file or URL string, by testing in turn for
+   * <ul>
+   * <li>an Annotation file</li>
+   * <li>a JNet file</li>
+   * <li>a features file</li>
+   * <li>else try to interpret as an alignment file</li>
+   * </ul>
    * 
    * @param file
    *          either a filename or a URL string.
    */
-  public void loadJalviewDataFile(String file, String protocol,
-          String format, SequenceI assocSeq)
+  public void loadJalviewDataFile(String file, DataSourceType sourceType,
+          FileFormatI format, SequenceI assocSeq)
   {
     try
     {
-      if (protocol == null)
+      if (sourceType == null)
       {
-        protocol = jalview.io.FormatAdapter.checkProtocol(file);
+        sourceType = FormatAdapter.checkProtocol(file);
       }
       // if the file isn't identified, or not positively identified as some
       // other filetype (PFAM is default unidentified alignment file type) then
       // try to parse as annotation.
-      boolean isAnnotation = (format == null || format
-              .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
-              .readAnnotationFile(viewport.getAlignment(), file, protocol)
-              : false;
+      boolean isAnnotation = (format == null
+              || FileFormat.Pfam.equals(format))
+                      ? new AnnotationFile().annotateAlignmentView(viewport,
+                              file, sourceType)
+                      : false;
 
       if (!isAnnotation)
       {
@@ -5057,32 +4698,30 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         TCoffeeScoreFile tcf = null;
         try
         {
-          tcf = new TCoffeeScoreFile(file, protocol);
+          tcf = new TCoffeeScoreFile(file, sourceType);
           if (tcf.isValid())
           {
             if (tcf.annotateAlignment(viewport.getAlignment(), true))
             {
-              tcoffeeColour.setEnabled(true);
-              tcoffeeColour.setSelected(true);
-              changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
+              buildColourMenu();
+              changeColour(
+                      new TCoffeeColourScheme(viewport.getAlignment()));
               isAnnotation = true;
-              statusBar
-                      .setText(MessageManager
-                              .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
+              statusBar.setText(MessageManager.getString(
+                      "label.successfully_pasted_tcoffee_scores_to_alignment"));
             }
             else
             {
               // some problem - if no warning its probable that the ID matching
               // process didn't work
-              JOptionPane
-                      .showMessageDialog(
-                              Desktop.desktop,
-                              tcf.getWarningMessage() == null ? MessageManager
-                                      .getString("label.check_file_matches_sequence_ids_alignment")
-                                      : tcf.getWarningMessage(),
-                              MessageManager
-                                      .getString("label.problem_reading_tcoffee_score_file"),
-                              JOptionPane.WARNING_MESSAGE);
+              JvOptionPane.showMessageDialog(Desktop.desktop,
+                      tcf.getWarningMessage() == null
+                              ? MessageManager.getString(
+                                      "label.check_file_matches_sequence_ids_alignment")
+                              : tcf.getWarningMessage(),
+                      MessageManager.getString(
+                              "label.problem_reading_tcoffee_score_file"),
+                      JvOptionPane.WARNING_MESSAGE);
             }
           }
           else
@@ -5091,9 +4730,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
         } catch (Exception x)
         {
-          Cache.log
-                  .debug("Exception when processing data source as T-COFFEE score file",
-                          x);
+          Cache.log.debug(
+                  "Exception when processing data source as T-COFFEE score file",
+                  x);
           tcf = null;
         }
         if (tcf == null)
@@ -5103,52 +4742,47 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           // try to parse it as a features file
           if (format == null)
           {
-            format = new IdentifyFile().Identify(file, protocol);
+            format = new IdentifyFile().identify(file, sourceType);
+          }
+          if (FileFormat.ScoreMatrix == format)
+          {
+            ScoreMatrixFile sm = new ScoreMatrixFile(
+                    new FileParse(file, sourceType));
+            sm.parse();
+            // todo: i18n this message
+            statusBar.setText(MessageManager.formatMessage(
+                    "label.successfully_loaded_matrix",
+                    sm.getMatrixName()));
           }
-          if (format.equalsIgnoreCase("JnetFile"))
+          else if (FileFormat.Jnet.equals(format))
           {
-            jalview.io.JPredFile predictions = new jalview.io.JPredFile(
-                    file, protocol);
-            new JnetAnnotationMaker().add_annotation(predictions,
+            JPredFile predictions = new JPredFile(file, sourceType);
+            new JnetAnnotationMaker();
+            JnetAnnotationMaker.add_annotation(predictions,
                     viewport.getAlignment(), 0, false);
+            viewport.getAlignment().setupJPredAlignment();
             isAnnotation = true;
           }
-          else
+          // else if (IdentifyFile.FeaturesFile.equals(format))
+          else if (FileFormat.Features.equals(format))
           {
-            /*
-             * if (format.equalsIgnoreCase("PDB")) {
-             * 
-             * String pdbfn = ""; // try to match up filename with sequence id
-             * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
-             * new File(file); pdbfn = fl.getName(); } else if (protocol ==
-             * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
-             * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
-             * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
-             * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
-             * // attempt to find a match in the alignment SequenceI mtch =
-             * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
-             * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
-             * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
-             * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
-             * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
-             * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
-             * { System.err.println("Associated file : " + file + " with " +
-             * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
-             * TODO: maybe need to load as normal otherwise return; } }
-             */
-            // try to parse it as a features file
-            boolean isGroupsFile = parseFeaturesFile(file, protocol);
-            // if it wasn't a features file then we just treat it as a general
-            // alignment file to load into the current view.
-            if (!isGroupsFile)
-            {
-              new FileLoader().LoadFile(viewport, file, protocol, format);
-            }
-            else
+            if (parseFeaturesFile(file, sourceType))
             {
-              alignPanel.paintAlignment(true);
+              SplitFrame splitFrame = (SplitFrame) getSplitViewContainer();
+              if (splitFrame != null)
+              {
+                splitFrame.repaint();
+              }
+              else
+              {
+                alignPanel.paintAlignment(true, true);
+              }
             }
           }
+          else
+          {
+            new FileLoader().LoadFile(viewport, file, sourceType, format);
+          }
         }
       }
       if (isAnnotation)
@@ -5157,7 +4791,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         alignPanel.adjustAnnotationHeight();
         viewport.updateSequenceIdColours();
         buildSortByAnnotationScoresMenu();
-        alignPanel.paintAlignment(true);
+        alignPanel.paintAlignment(true, true);
       }
     } catch (Exception ex)
     {
@@ -5170,42 +4804,108 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       } catch (Exception x)
       {
       }
-      ;
       new OOMWarning(
               "loading data "
-                      + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
-                              : "using " + protocol + " from " + file)
+                      + (sourceType != null
+                              ? (sourceType == DataSourceType.PASTE
+                                      ? "from clipboard."
+                                      : "using " + sourceType + " from "
+                                              + file)
                               : ".")
-                      + (format != null ? "(parsing as '" + format
-                              + "' file)" : ""), oom, Desktop.desktop);
+                      + (format != null
+                              ? "(parsing as '" + format + "' file)"
+                              : ""),
+              oom, Desktop.desktop);
     }
   }
 
+  /**
+   * 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 (featureSettings != null && featureSettings.isOpen()
+              && featureSettings.fr.getViewport() != viewport)
+      {
+        if (viewport.isShowSequenceFeatures())
+        {
+          // refresh the featureSettings to reflect UI change
+          showFeatureSettingsUI();
+        }
+        else
+        {
+          // close feature settings for this view.
+          featureSettings.close();
+        }
+      }
+
+    }
+
+    /*
+     * 'focus' any colour slider that is open to the selected viewport
+     */
+    if (viewport.getConservationSelected())
+    {
+      SliderPanel.setConservationSlider(alignPanel,
+              viewport.getResidueShading(), alignPanel.getViewName());
+    }
+    else
+    {
+      SliderPanel.hideConservationSlider();
+    }
+    if (viewport.getAbovePIDThreshold())
+    {
+      SliderPanel.setPIDSliderSource(alignPanel,
+              viewport.getResidueShading(), alignPanel.getViewName());
+    }
+    else
+    {
+      SliderPanel.hidePIDSlider();
+    }
+
+    /*
+     * 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))
+    if (e.isPopupTrigger())
     {
-      String reply = JOptionPane.showInternalInputDialog(this,
-              MessageManager.getString("label.enter_view_name"),
-              MessageManager.getString("label.enter_view_name"),
-              JOptionPane.QUESTION_MESSAGE);
+      String msg = MessageManager.getString("label.enter_view_name");
+      String reply = JvOptionPane.showInternalInputDialog(this, msg, msg,
+              JvOptionPane.QUESTION_MESSAGE);
 
       if (reply != null)
       {
-        viewport.viewName = reply;
+        viewport.setViewName(reply);
+        // TODO warn if reply is in getExistingViewNames()?
         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
       }
     }
@@ -5245,7 +4945,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void showDbRefs_actionPerformed(ActionEvent e)
   {
-    viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
+    viewport.setShowDBRefs(showDbRefsMenuitem.isSelected());
   }
 
   /*
@@ -5257,7 +4957,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void showNpFeats_actionPerformed(ActionEvent e)
   {
-    viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
+    viewport.setShowNPFeats(showNpFeatsMenuitem.isSelected());
   }
 
   /**
@@ -5266,7 +4966,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param av
    */
-  public boolean closeView(AlignViewport av)
+  public boolean closeView(AlignViewportI av)
   {
     if (viewport == av)
     {
@@ -5297,29 +4997,31 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // here
     final JMenu rfetch = new JMenu(
             MessageManager.getString("action.fetch_db_references"));
-    rfetch.setToolTipText(MessageManager
-            .getString("label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences"));
+    rfetch.setToolTipText(MessageManager.getString(
+            "label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences"));
     webService.add(rfetch);
 
     final JCheckBoxMenuItem trimrs = new JCheckBoxMenuItem(
             MessageManager.getString("option.trim_retrieved_seqs"));
-    trimrs.setToolTipText(MessageManager
-            .getString("label.trim_retrieved_sequences"));
-    trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
+    trimrs.setToolTipText(
+            MessageManager.getString("label.trim_retrieved_sequences"));
+    trimrs.setSelected(
+            Cache.getDefault(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES, true));
     trimrs.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         trimrs.setSelected(trimrs.isSelected());
-        Cache.setProperty("TRIM_FETCHED_DATASET_SEQS",
+        Cache.setProperty(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES,
                 Boolean.valueOf(trimrs.isSelected()).toString());
       };
     });
     rfetch.add(trimrs);
     JMenuItem fetchr = new JMenuItem(
             MessageManager.getString("label.standard_databases"));
-    fetchr.setToolTipText(MessageManager
-            .getString("label.fetch_embl_uniprot"));
+    fetchr.setToolTipText(
+            MessageManager.getString("label.fetch_embl_uniprot"));
     fetchr.addActionListener(new ActionListener()
     {
 
@@ -5328,13 +5030,31 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         new Thread(new Runnable()
         {
-
           @Override
           public void run()
           {
-            new jalview.ws.DBRefFetcher(alignPanel.av
-                    .getSequenceSelection(), alignPanel.alignFrame)
-                    .fetchDBRefs(false);
+            boolean isNucleotide = alignPanel.alignFrame.getViewport()
+                    .getAlignment().isNucleotide();
+            DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                    alignPanel.av.getSequenceSelection(),
+                    alignPanel.alignFrame, null,
+                    alignPanel.alignFrame.featureSettings, isNucleotide);
+            dbRefFetcher.addListener(new FetchFinishedListenerI()
+            {
+              @Override
+              public void finished()
+              {
+
+                for (FeatureSettingsModelI srcSettings : dbRefFetcher
+                        .getFeatureSettingsModels())
+                {
+
+                  alignPanel.av.mergeFeaturesStyle(srcSettings);
+                }
+                AlignFrame.this.setMenusForViewport();
+              }
+            });
+            dbRefFetcher.fetchDBRefs(false);
           }
         }).start();
 
@@ -5348,7 +5068,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       @Override
       public void run()
       {
-        final jalview.ws.SequenceFetcher sf = SequenceFetcher
+        final jalview.ws.SequenceFetcher sf = jalview.gui.SequenceFetcher
                 .getSequenceFetcherSingleton(me);
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
@@ -5402,17 +5122,37 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       @Override
                       public void run()
                       {
-                        new jalview.ws.DBRefFetcher(alignPanel.av
-                                .getSequenceSelection(),
-                                alignPanel.alignFrame, dassource)
-                                .fetchDBRefs(false);
+                        boolean isNucleotide = alignPanel.alignFrame
+                                .getViewport().getAlignment()
+                                .isNucleotide();
+                        DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                alignPanel.av.getSequenceSelection(),
+                                alignPanel.alignFrame, dassource,
+                                alignPanel.alignFrame.featureSettings,
+                                isNucleotide);
+                        dbRefFetcher
+                                .addListener(new FetchFinishedListenerI()
+                                {
+                                  @Override
+                                  public void finished()
+                                  {
+                                    FeatureSettingsModelI srcSettings = dassource[0]
+                                            .getFeatureColourScheme();
+                                    alignPanel.av.mergeFeaturesStyle(
+                                            srcSettings);
+                                    AlignFrame.this.setMenusForViewport();
+                                  }
+                                });
+                        dbRefFetcher.fetchDBRefs(false);
                       }
                     }).start();
                   }
 
                 });
-                fetchr.setToolTipText("<html>"
-                        + JvSwingUtils.wrapTooltip(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++;
               }
@@ -5422,8 +5162,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         .toArray(new DbSourceProxy[0]);
                 // fetch all entry
                 DbSourceProxy src = otherdb.get(0);
-                fetchr = new JMenuItem(MessageManager.formatMessage(
-                        "label.fetch_all_param", new String[]
+                fetchr = new JMenuItem(MessageManager
+                        .formatMessage("label.fetch_all_param", new Object[]
                         { src.getDbSource() }));
                 fetchr.addActionListener(new ActionListener()
                 {
@@ -5436,38 +5176,62 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       @Override
                       public void run()
                       {
-                        new jalview.ws.DBRefFetcher(alignPanel.av
-                                .getSequenceSelection(),
-                                alignPanel.alignFrame, dassource)
-                                .fetchDBRefs(false);
+                        boolean isNucleotide = alignPanel.alignFrame
+                                .getViewport().getAlignment()
+                                .isNucleotide();
+                        DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                alignPanel.av.getSequenceSelection(),
+                                alignPanel.alignFrame, dassource,
+                                alignPanel.alignFrame.featureSettings,
+                                isNucleotide);
+                        dbRefFetcher
+                                .addListener(new FetchFinishedListenerI()
+                                {
+                                  @Override
+                                  public void finished()
+                                  {
+                                    AlignFrame.this.setMenusForViewport();
+                                  }
+                                });
+                        dbRefFetcher.fetchDBRefs(false);
                       }
                     }).start();
                   }
                 });
 
-                fetchr.setToolTipText("<html>"
-                        + JvSwingUtils.wrapTooltip(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;
                 for (DbSourceProxy sproxy : otherdb)
                 {
                   String dbname = sproxy.getDbName();
-                  String sname = dbname.length() > 5 ? dbname.substring(0,
-                          5) + "..." : dbname;
-                  String msname = dbname.length() > 10 ? dbname.substring(
-                          0, 10) + "..." : dbname;
+                  String sname = dbname.length() > 5
+                          ? dbname.substring(0, 5) + "..."
+                          : dbname;
+                  String msname = dbname.length() > 10
+                          ? dbname.substring(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()
                   {
 
@@ -5480,17 +5244,33 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         @Override
                         public void run()
                         {
-                          new jalview.ws.DBRefFetcher(alignPanel.av
-                                  .getSequenceSelection(),
-                                  alignPanel.alignFrame, dassrc)
-                                  .fetchDBRefs(false);
+                          boolean isNucleotide = alignPanel.alignFrame
+                                  .getViewport().getAlignment()
+                                  .isNucleotide();
+                          DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                  alignPanel.av.getSequenceSelection(),
+                                  alignPanel.alignFrame, dassrc,
+                                  alignPanel.alignFrame.featureSettings,
+                                  isNucleotide);
+                          dbRefFetcher
+                                  .addListener(new FetchFinishedListenerI()
+                                  {
+                                    @Override
+                                    public void finished()
+                                    {
+                                      AlignFrame.this.setMenusForViewport();
+                                    }
+                                  });
+                          dbRefFetcher.fetchDBRefs(false);
                         }
                       }).start();
                     }
 
                   });
-                  fetchr.setToolTipText("<html>"
-                          + MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{dbname}));
+                  fetchr.setToolTipText(
+                          "<html>" + MessageManager.formatMessage(
+                                  "label.fetch_retrieve_from", new Object[]
+                                  { dbname }));
                   ifetch.add(fetchr);
                   ++i;
                   if (++icomp >= mcomp || i == (otherdb.size()))
@@ -5545,10 +5325,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.firePropertyChange("alignment", null, al);
   }
 
+  @Override
   public void setShowSeqFeatures(boolean b)
   {
-    showSeqFeatures.setSelected(true);
-    viewport.setShowSequenceFeatures(true);
+    showSeqFeatures.setSelected(b);
+    viewport.setShowSequenceFeatures(b);
   }
 
   /*
@@ -5562,7 +5343,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
   {
     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false, false);
   }
 
   /*
@@ -5651,7 +5432,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
       alignPanel.updateAnnotation();
-      alignPanel.paintAlignment(true);
+      alignPanel.paintAlignment(true,
+              viewport.needToUpdateStructureViews());
+    }
+  }
+
+  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, true);
     }
   }
 
@@ -5660,6 +5454,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (avc.createGroup())
     {
+      if (applyAutoAnnotationSettings.isSelected())
+      {
+        alignPanel.updateAnnotation(true, false);
+      }
       alignPanel.alignmentChanged();
     }
   }
@@ -5680,18 +5478,308 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   public void setDisplayedView(AlignmentPanel alignmentPanel)
   {
-    if (!viewport.getSequenceSetId().equals(
-            alignmentPanel.av.getSequenceSetId()))
+    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
-                    .getSelectedIndex())
+    if (tabbedPane != null && tabbedPane.getTabCount() > 0 && alignPanels
+            .indexOf(alignmentPanel) != tabbedPane.getSelectedIndex())
     {
       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
     }
   }
+
+  /**
+   * Action on selection of menu options to Show or Hide annotations.
+   * 
+   * @param visible
+   * @param forSequences
+   *          update sequence-related annotations
+   * @param forAlignment
+   *          update non-sequence-related annotations
+   */
+  @Override
+  protected void setAnnotationsVisibility(boolean visible,
+          boolean forSequences, boolean forAlignment)
+  {
+    AlignmentAnnotation[] anns = alignPanel.getAlignment()
+            .getAlignmentAnnotation();
+    if (anns == null)
+    {
+      return;
+    }
+    for (AlignmentAnnotation aa : anns)
+    {
+      /*
+       * 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)
+      {
+        aa.visible = visible;
+      }
+    }
+    alignPanel.validateAnnotationDimensions(true);
+    alignPanel.alignmentChanged();
+  }
+
+  /**
+   * Store selected annotation sort order for the view and repaint.
+   */
+  @Override
+  protected void sortAnnotations_actionPerformed()
+  {
+    this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
+    this.alignPanel.av
+            .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+    alignPanel.paintAlignment(false, false);
+  }
+
+  /**
+   * 
+   * @return alignment panels in this alignment frame
+   */
+  public List<? extends AlignmentViewPanel> getAlignPanels()
+  {
+    // alignPanels is never null
+    // return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
+    return 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();
+    List<AlignedCodonFrame> mappings = alignment.getCodonFrames();
+    if (mappings == null)
+    {
+      return;
+    }
+    List<SequenceI> cdnaSeqs = new ArrayList<>();
+    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()]));
+    GAlignFrame 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);
+    }
+  }
+
+  /**
+   * Generate the reverse (optionally complemented) of the selected sequences,
+   * and add them to the alignment
+   */
+  @Override
+  protected void showReverse_actionPerformed(boolean complement)
+  {
+    AlignmentI al = null;
+    try
+    {
+      Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
+      al = dna.reverseCdna(complement);
+      viewport.addAlignment(al, "");
+      addHistoryItem(new EditCommand(
+              MessageManager.getString("label.add_sequences"), Action.PASTE,
+              al.getSequencesArray(), 0, al.getWidth(),
+              viewport.getAlignment()));
+    } catch (Exception ex)
+    {
+      System.err.println(ex.getMessage());
+      return;
+    }
+  }
+
+  /**
+   * Try to run a script in the Groovy console, having first ensured that this
+   * AlignFrame is set as currentAlignFrame in Desktop, to allow the script to
+   * be targeted at this alignment.
+   */
+  @Override
+  protected void runGroovy_actionPerformed()
+  {
+    Jalview.setCurrentAlignFrame(this);
+    groovy.ui.Console console = Desktop.getGroovyConsole();
+    if (console != null)
+    {
+      try
+      {
+        console.runScript();
+      } catch (Exception ex)
+      {
+        System.err.println((ex.toString()));
+        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+                MessageManager.getString("label.couldnt_run_groovy_script"),
+                MessageManager.getString("label.groovy_support_failed"),
+                JvOptionPane.ERROR_MESSAGE);
+      }
+    }
+    else
+    {
+      System.err.println("Can't run Groovy script as console not found");
+    }
+  }
+
+  /**
+   * Hides columns containing (or not containing) a specified feature, provided
+   * that would not leave all columns hidden
+   * 
+   * @param featureType
+   * @param columnsContaining
+   * @return
+   */
+  public boolean hideFeatureColumns(String featureType,
+          boolean columnsContaining)
+  {
+    boolean notForHiding = avc.markColumnsContainingFeatures(
+            columnsContaining, false, false, featureType);
+    if (notForHiding)
+    {
+      if (avc.markColumnsContainingFeatures(!columnsContaining, false,
+              false, featureType))
+      {
+        getViewport().hideSelectedColumns();
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  protected void selectHighlightedColumns_actionPerformed(
+          ActionEvent actionEvent)
+  {
+    // include key modifier check in case user selects from menu
+    avc.markHighlightedColumns(
+            (actionEvent.getModifiers() & ActionEvent.ALT_MASK) != 0, true,
+            (actionEvent.getModifiers() & (ActionEvent.META_MASK
+                    | ActionEvent.CTRL_MASK)) != 0);
+  }
+
+  /**
+   * Rebuilds the Colour menu, including any user-defined colours which have
+   * been loaded either on startup or during the session
+   */
+  public void buildColourMenu()
+  {
+    colourMenu.removeAll();
+
+    colourMenu.add(applyToAllGroups);
+    colourMenu.add(textColour);
+    colourMenu.addSeparator();
+
+    ButtonGroup bg = ColourMenuHelper.addMenuItems(colourMenu, this,
+            viewport.getAlignment(), false);
+
+    colourMenu.add(annotationColour);
+    bg.add(annotationColour);
+    colourMenu.addSeparator();
+    colourMenu.add(conservationMenuItem);
+    colourMenu.add(modifyConservation);
+    colourMenu.add(abovePIDThreshold);
+    colourMenu.add(modifyPID);
+
+    ColourSchemeI colourScheme = viewport.getGlobalColourScheme();
+    ColourMenuHelper.setColourSelected(colourMenu, colourScheme);
+  }
+
+  /**
+   * Open a dialog (if not already open) that allows the user to select and
+   * calculate PCA or Tree analysis
+   */
+  protected void openTreePcaDialog()
+  {
+    if (alignPanel.getCalculationDialog() == null)
+    {
+      new CalculationChooser(AlignFrame.this);
+    }
+  }
+
+  @Override
+  protected void loadVcf_actionPerformed()
+  {
+    JalviewFileChooser chooser = new JalviewFileChooser(
+            Cache.getProperty("LAST_DIRECTORY"));
+    chooser.setFileView(new JalviewFileView());
+    chooser.setDialogTitle(MessageManager.getString("label.load_vcf_file"));
+    chooser.setToolTipText(MessageManager.getString("label.load_vcf_file"));
+
+    int value = chooser.showOpenDialog(null);
+
+    if (value == JalviewFileChooser.APPROVE_OPTION)
+    {
+      String choice = chooser.getSelectedFile().getPath();
+      Cache.setProperty("LAST_DIRECTORY", choice);
+      SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
+      new VCFLoader(choice).loadVCF(seqs, this);
+    }
+
+  }
+
+  private Rectangle lastFeatureSettingsBounds = null;
+
+  @Override
+  public void setFeatureSettingsGeometry(Rectangle bounds)
+  {
+    lastFeatureSettingsBounds = bounds;
+  }
+
+  @Override
+  public Rectangle getFeatureSettingsGeometry()
+  {
+    return lastFeatureSettingsBounds;
+  }
 }
 
 class PrintThread extends Thread