JAL-1830 dropped reference counts for mappings, instead remove mapping
[jalview.git] / src / jalview / gui / AlignFrame.java
index c7792a5..afa6847 100644 (file)
  */
 package jalview.gui;
 
-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.ItemEvent;
-import java.awt.event.ItemListener;
-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.Arrays;
-import java.util.Deque;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Set;
-import java.util.Vector;
-
-import javax.swing.JButton;
-import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JEditorPane;
-import javax.swing.JInternalFrame;
-import javax.swing.JLabel;
-import javax.swing.JLayeredPane;
-import javax.swing.JMenu;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JProgressBar;
-import javax.swing.JRadioButtonMenuItem;
-import javax.swing.JScrollPane;
-import javax.swing.SwingUtilities;
-
 import jalview.analysis.AAFrequency;
 import jalview.analysis.AlignmentSorter;
 import jalview.analysis.AlignmentUtils;
@@ -83,10 +32,12 @@ import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureSettingsControllerI;
 import jalview.api.SplitContainerI;
 import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
 import jalview.bin.Cache;
+import jalview.bin.Jalview;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
@@ -98,10 +49,12 @@ import jalview.commands.TrimRegionCommand;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentExportData;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SeqCigar;
 import jalview.datamodel.Sequence;
@@ -111,7 +64,6 @@ import jalview.gui.ViewSelectionMenu.ViewSetProvider;
 import jalview.io.AlignmentProperties;
 import jalview.io.AnnotationFile;
 import jalview.io.BioJsHTMLOutput;
-import jalview.io.FeaturesFile;
 import jalview.io.FileLoader;
 import jalview.io.FormatAdapter;
 import jalview.io.HtmlSvgOutput;
@@ -140,6 +92,7 @@ import jalview.schemes.TaylorColourScheme;
 import jalview.schemes.TurnColourScheme;
 import jalview.schemes.UserColourScheme;
 import jalview.schemes.ZappoColourScheme;
+import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.jws1.Discoverer;
@@ -147,6 +100,52 @@ 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.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.ItemEvent;
+import java.awt.event.ItemListener;
+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.Arrays;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JEditorPane;
+import javax.swing.JInternalFrame;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+
 /**
  * DOCUMENT ME!
  * 
@@ -243,6 +242,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     this(al, hiddenColumns, width, height, null);
   }
 
+
   /**
    * Create alignment frame for al with hiddenColumns, a specific width and
    * height, and specific sequenceId
@@ -292,6 +292,28 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     init();
   }
 
+  public AlignFrame(AlignmentI al, SequenceI[] hiddenSeqs,
+          ColumnSelection hiddenColumns, int width, int height)
+  {
+    setSize(width, height);
+
+    if (al.getDataset() == null)
+    {
+      al.setDataset(null);
+    }
+
+    viewport = new AlignViewport(al, hiddenColumns);
+
+    if (hiddenSeqs != null && hiddenSeqs.length > 0)
+    {
+      viewport.hideSequence(hiddenSeqs);
+    }
+    alignPanel = new AlignmentPanel(this, viewport);
+    addAlignmentPanel(alignPanel, true);
+    init();
+  }
+
+
   /**
    * Make a new AlignFrame from existing alignmentPanels
    * 
@@ -314,6 +336,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   void init()
   {
+    if (!Jalview.isHeadlessMode())
+    {
+      progressBar = new ProgressBar(this.statusPanel, this.statusBar);
+    }
+
     avc = new jalview.controller.AlignViewController(this, viewport,
             alignPanel);
     if (viewport.getAlignmentConservationAnnotation() == null)
@@ -363,7 +390,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     final List<AlignmentPanel> selviews = new ArrayList<AlignmentPanel>();
     final List<AlignmentPanel> origview = new ArrayList<AlignmentPanel>();
-    ViewSelectionMenu vsel = new ViewSelectionMenu("Transfer colours from",
+    final String menuLabel = MessageManager
+            .getString("label.copy_format_from");
+    ViewSelectionMenu vsel = new ViewSelectionMenu(menuLabel,
             new ViewSetProvider()
             {
 
@@ -372,7 +401,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               {
                 origview.clear();
                 origview.add(alignPanel);
-                return Desktop.getAlignmentPanels(null);
+                // make an array of all alignment panels except for this one
+                List<AlignmentPanel> aps = new ArrayList<AlignmentPanel>(
+                        Arrays.asList(Desktop.getAlignmentPanels(null)));
+                aps.remove(AlignFrame.this.alignPanel);
+                return aps.toArray(new AlignmentPanel[aps.size()]);
               }
             }, selviews, new ItemListener()
             {
@@ -382,32 +415,43 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               {
                 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();
-                  origview.get(0).getAlignViewport().setViewStyle(vs);
-                  AlignViewportI complement = origview.get(0)
-                          .getAlignViewport().getCodingComplement();
-                  if (complement != null)
+                  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);
-                    if (complement.isNucleotide())
-                    {
-                      complement.setViewStyle(vs);
-                      vs.setCharWidth(vs.getCharWidth() / 3);
-                    }
-                    else
-                    {
-                      int rw = vs.getCharWidth();
-                      vs.setCharWidth(rw * 3);
-                      complement.setViewStyle(vs);
-                      vs.setCharWidth(rw);
-                    }
-                    af.alignPanel.updateLayout();
+                    ((SplitFrame) af.getSplitViewContainer())
+                            .adjustLayout();
                     af.setMenusForViewport();
                   }
-                  origview.get(0).updateLayout();
-                  origview.get(0).setSelected(true);
-                  origview.get(0).alignFrame.setMenusForViewport();
+
+                  ap.updateLayout();
+                  ap.setSelected(true);
+                  ap.alignFrame.setMenusForViewport();
 
                 }
               }
@@ -855,9 +899,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     updateEditMenuBar();
   }
 
-  // methods for implementing IProgressIndicator
-  // need to refactor to a reusable stub class
-  Hashtable progressBars, progressBarHandlers;
+  private IProgressIndicator progressBar;
 
   /*
    * (non-Javadoc)
@@ -867,78 +909,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 Object[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
-        }
-      });
-      progressPanel.add(cancel, BorderLayout.EAST);
-    }
+    progressBar.registerHandler(id, handler);
   }
 
   /**
@@ -948,18 +926,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public boolean operationInProgress()
   {
-    if (progressBars != null && progressBars.size() > 0)
-    {
-      return true;
-    }
-    return false;
+    return progressBar.operationInProgress();
   }
 
   @Override
   public void setStatus(String text)
   {
     statusBar.setText(text);
-  };
+  }
 
   /*
    * Added so Castor Mapping file can obtain Jalview Version
@@ -1152,11 +1126,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 .lastIndexOf(java.io.File.separatorChar) + 1);
       }
 
-      /*
-       * First save any linked Chimera session.
-       */
-      Desktop.instance.saveChimeraSessions(file);
-
       success = new Jalview2XML().saveAlignment(this, file, shortName);
 
       statusBar.setText(MessageManager.formatMessage(
@@ -1170,35 +1139,25 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         warningMessage("Cannot save file " + fileName + " using format "
                 + format, "Alignment output format not supported");
-        saveAs_actionPerformed(null);
-        // JBPNote need to have a raise_gui flag here
+        if (!Jalview.isHeadlessMode())
+        {
+          saveAs_actionPerformed(null);
+        }
         return false;
       }
 
-      String[] omitHidden = null;
-
-      if (viewport.hasHiddenColumns())
+      AlignmentExportData exportData = getAlignmentForExport(format, viewport);
+      if (exportData.getSettings().isCancelled())
       {
-        int reply = JOptionPane
-                .showInternalConfirmDialog(
-                        Desktop.desktop,
-                        MessageManager
-                                .getString("label.alignment_contains_hidden_columns"),
-                        MessageManager
-                                .getString("action.save_omit_hidden_columns"),
-                        JOptionPane.YES_NO_OPTION,
-                        JOptionPane.QUESTION_MESSAGE);
-
-        if (reply == JOptionPane.YES_OPTION)
-        {
-          omitHidden = viewport.getViewAsString(false);
-        }
+        return false;
       }
-      FormatAdapter f = new FormatAdapter();
+      FormatAdapter f = new FormatAdapter(alignPanel,
+              exportData.getSettings());
       String output = f.formatSequences(format,
-              viewport.getAlignment(), // class cast exceptions will
+              exportData.getAlignment(), // class cast exceptions will
               // occur in the distant future
-              omitHidden, f.getCacheSuffixDefault(format),
+              exportData.getOmitHidden(), exportData.getStartEndPostions(),
+              f.getCacheSuffixDefault(format),
               viewport.getColumnSelection());
 
       if (output == null)
@@ -1239,6 +1198,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return success;
   }
 
+
   private void warningMessage(String warning, String title)
   {
     if (new jalview.util.Platform().isHeadless())
@@ -1263,33 +1223,22 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void outputText_actionPerformed(ActionEvent e)
   {
-    String[] omitHidden = null;
 
-    if (viewport.hasHiddenColumns())
+    AlignmentExportData exportData = getAlignmentForExport(
+            e.getActionCommand(), viewport);
+    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,
+      cap.setText(new FormatAdapter(alignPanel, exportData.getSettings())
+              .formatSequences(
+              e.getActionCommand(),
+ exportData.getAlignment(),
+              exportData.getOmitHidden(), exportData.getStartEndPostions(),
               viewport.getColumnSelection()));
       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
               "label.alignment_output_command", new Object[]
@@ -1302,6 +1251,106 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   }
 
+  public static AlignmentExportData getAlignmentForExport(String exportFormat,
+          AlignViewportI viewport)
+  {
+    AlignmentI alignmentToExport = null;
+    String[] omitHidden = null;
+    int[] alignmentStartEnd = new int[2];
+
+    HiddenSequences hiddenSeqs = viewport.getAlignment()
+            .getHiddenSequences();
+
+
+    alignmentToExport = viewport.getAlignment();
+    alignmentStartEnd = new int[]
+    { 0, alignmentToExport.getWidth() - 1 };
+
+    boolean hasHiddenSeqs = hiddenSeqs.getSize() > 0;
+    AlignExportSettings settings = new AlignExportSettings(hasHiddenSeqs,
+            viewport.hasHiddenColumns(), exportFormat);
+    settings.isExportAnnotations();
+
+    if (viewport.hasHiddenColumns() && !settings.isExportHiddenColumns())
+    {
+      omitHidden = viewport.getViewAsString(false);
+    }
+
+    if (hasHiddenSeqs && settings.isExportHiddenSequences())
+    {
+      alignmentToExport = hiddenSeqs.getFullAlignment();
+    }
+    else
+    {
+      alignmentToExport = viewport.getAlignment();
+      alignmentStartEnd = getStartEnd(alignmentStartEnd, viewport
+              .getColumnSelection().getHiddenColumns());
+    }
+    AlignmentExportData ed = new AlignmentExportData(alignmentToExport, omitHidden, alignmentStartEnd,
+            settings);
+    return ed;
+  }
+
+  public static int[] getStartEnd(int[] aligmentStartEnd,
+          List<int[]> hiddenCols)
+  {
+    int startPos = aligmentStartEnd[0];
+    int endPos = aligmentStartEnd[1];
+
+    int[] lowestRange = new int[2];
+    int[] higestRange = new int[2];
+
+    for (int[] hiddenCol : hiddenCols)
+    {
+      // System.out.println("comparing : " + hiddenCol[0] + "-" + hiddenCol[1]);
+      lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
+      higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
+    }
+    // System.out.println("min : " + lowestRange[0] + "-" + lowestRange[1]);
+    // System.out.println("max : " + higestRange[0] + "-" + higestRange[1]);
+
+    if (lowestRange[0] == 0 && lowestRange[1] == 0)
+    {
+      startPos = aligmentStartEnd[0];
+    }
+    else
+    {
+      startPos = lowestRange[1] + 1;
+    }
+
+    if (higestRange[0] == 0 && higestRange[1] == 0)
+    {
+      endPos = aligmentStartEnd[1];
+    }
+    else
+    {
+      endPos = higestRange[0];
+    }
+
+    // System.out.println("Export range : " + minPos + " - " + maxPos);
+    return new int[]
+    { startPos, endPos };
+  }
+
+  public static void main(String[] args)
+  {
+    ArrayList<int[]> hiddenCols = new ArrayList<int[]>();
+    hiddenCols.add(new int[]
+    { 0, 4 });
+    hiddenCols.add(new int[]
+    { 6, 9 });
+    hiddenCols.add(new int[]
+    { 11, 12 });
+    hiddenCols.add(new int[]
+    { 33, 33 });
+    hiddenCols.add(new int[]
+    { 45, 50 });
+
+    int[] x = getStartEnd(new int[]
+    { 0, 50 }, hiddenCols);
+    // System.out.println("Export range : " + x[0] + " - " + x[1]);
+  }
+
   /**
    * DOCUMENT ME!
    * 
@@ -1311,17 +1360,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void htmlMenuItem_actionPerformed(ActionEvent e)
   {
-    // new HTMLOutput(alignPanel,
-    // alignPanel.getSeqPanel().seqCanvas.getSequenceRenderer(),
-    // alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer());
     new HtmlSvgOutput(null, alignPanel);
   }
 
   @Override
   public void bioJSMenuItem_actionPerformed(ActionEvent e)
   {
-    new BioJsHTMLOutput(alignPanel,
-            alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer());
+    BioJsHTMLOutput bjs = new BioJsHTMLOutput(alignPanel);
+    bjs.exportJalviewAlignmentAsBioJsHtmlFile();
   }
   public void createImageMap(File file, String image)
   {
@@ -1451,6 +1497,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       if (closeAllTabs)
       {
+        /*
+         * this will raise an INTERNAL_FRAME_CLOSED event and this method will
+         * be called recursively, with the frame now in 'closed' state
+         */
         this.setClosed(true);
       }
     } catch (Exception ex)
@@ -1781,9 +1831,14 @@ 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;
     Deque<CommandI> historyList = viewport.getHistoryList();
-    if (historyList != null
+    boolean inSplitFrame = getSplitViewContainer() != null;
+    if (!inSplitFrame && historyList != null
             && historyList.size() > 0
             && historyList.peek() instanceof SlideSequencesCommand)
     {
@@ -1825,7 +1880,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     String output = new FormatAdapter().formatSequences("Fasta", seqs,
-            omitHidden);
+            omitHidden, null);
 
     StringSelection ss = new StringSelection(output);
 
@@ -2698,12 +2753,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     /*
-     * Views share the same edits, undo and redo stacks, mappings.
+     * Views share the same edits undo and redo stacks
      */
     newap.av.setHistoryList(viewport.getHistoryList());
     newap.av.setRedoList(viewport.getRedoList());
-    newap.av.getAlignment().setCodonFrames(
-            viewport.getAlignment().getCodonFrames());
+
+    /*
+     * Views share the same mappings; need to deregister any new mappings
+     * created by copyAlignPanel, and register the new reference to the shared
+     * mappings
+     */
+    newap.av.replaceMappings(viewport.getAlignment());
 
     newap.av.viewName = getNewViewName(viewTitle);
 
@@ -2849,7 +2909,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
      * Set the 'follow' flag on the Viewport (and scroll to position if now
      * true).
      */
-    if (viewport.followHighlight = this.followHighlightMenuItem.getState())
+    final boolean state = this.followHighlightMenuItem.getState();
+    viewport.setFollowHighlight(state);
+    if (state)
     {
       alignPanel.scrollToPosition(
               alignPanel.getSeqPanel().seqCanvas.searchResults, false);
@@ -3115,6 +3177,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public FeatureSettings featureSettings;
 
   @Override
+  public FeatureSettingsControllerI getFeatureSettingsUI()
+  {
+    return featureSettings;
+  }
+
+  @Override
   public void featureSettings_actionPerformed(ActionEvent e)
   {
     if (featureSettings != null)
@@ -4779,8 +4847,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               sprods[s].updatePDBIds();
             }
             Alignment al = new Alignment(sprods);
-            Set<AlignedCodonFrame> cf = prods.getCodonFrames();
             al.setDataset(ds);
+
+            /*
+             * Copy dna-to-protein mappings to new alignment
+             */
+            // TODO 1: no mappings are set up for EMBL product
+            // TODO 2: if they were, should add them to protein alignment, not
+            // dna
+            Set<AlignedCodonFrame> cf = prods.getCodonFrames();
             for (AlignedCodonFrame acf : cf)
             {
               al.addCodonFrame(acf);
@@ -4792,12 +4867,33 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                     + getTitle();
             naf.setTitle(newtitle);
 
-            // remove this flag once confirmed we want a split view
-            boolean asSplitFrame = true;
+            // temporary flag until SplitFrame is released
+            boolean asSplitFrame = Cache.getDefault(
+                    Preferences.ENABLE_SPLIT_FRAME, true);
             if (asSplitFrame)
             {
-              final Alignment copyAlignment = new Alignment(new Alignment(
-                      AlignFrame.this.viewport.getSequenceSelection()));
+              /*
+               * Make a copy of this alignment (sharing the same dataset
+               * sequences). If we are DNA, drop introns and update mappings
+               */
+              AlignmentI copyAlignment = null;
+              final SequenceI[] sequenceSelection = AlignFrame.this.viewport
+                      .getSequenceSelection();
+              if (dna)
+              {
+                copyAlignment = AlignmentUtils.makeExonAlignment(
+                        sequenceSelection, cf);
+                al.getCodonFrames().clear();
+                al.getCodonFrames().addAll(cf);
+                final StructureSelectionManager ssm = StructureSelectionManager
+                        .getStructureSelectionManager(Desktop.instance);
+                ssm.registerMappings(cf);
+              }
+              else
+              {
+                copyAlignment = new Alignment(new Alignment(
+                        sequenceSelection));
+              }
               AlignFrame copyThis = new AlignFrame(copyAlignment,
                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
               copyThis.setTitle(AlignFrame.this.getTitle());
@@ -4881,10 +4977,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               "Exception during translation. Please report this !", ex);
       final String msg = MessageManager
               .getString("label.error_when_translating_sequences_submit_bug_report");
-      final String title = MessageManager
+      final String errorTitle = MessageManager
               .getString("label.implementation_error")
               + MessageManager.getString("translation_failed");
-      JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
+      JOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
               JOptionPane.ERROR_MESSAGE);
       return;
     }
@@ -4892,9 +4988,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       final String msg = MessageManager
               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
-      final String title = MessageManager
+      final String errorTitle = MessageManager
               .getString("label.translation_failed");
-      JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
+      JOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
               JOptionPane.WARNING_MESSAGE);
     }
     else
@@ -4905,9 +5001,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               "label.translation_of_params", new Object[]
               { this.getTitle() });
       af.setTitle(newTitle);
-      final SequenceI[] seqs = viewport.getSelectionAsNewSequence();
-      viewport.openSplitFrame(af, new Alignment(seqs), al.getCodonFrames());
-      // Desktop.addInternalFrame(af, newTitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+      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);
+      }
     }
   }
 
@@ -4928,41 +5031,29 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    *          contents or path to retrieve file
    * @param type
    *          access mode of file (see jalview.io.AlignFile)
-   * @return true if features file was parsed corectly.
+   * @return true if features file was parsed correctly.
    */
   public boolean parseFeaturesFile(String file, String type)
   {
-    boolean featuresFile = false;
-    try
-    {
-      featuresFile = new FeaturesFile(file, type).parse(viewport
-              .getAlignment().getDataset(), alignPanel.getSeqPanel().seqCanvas
-              .getFeatureRenderer().getFeatureColours(), false,
-              jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
-    }
+    return avc.parseFeaturesFile(file, type,
+            jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
+    
+  }
 
-    if (featuresFile)
+  @Override
+  public void refreshFeatureUI(boolean enableIfNecessary)
+  {
+    // note - currently this is only still here rather than in the controller
+    // because of the featureSettings hard reference that is yet to be
+    // abstracted
+    if (enableIfNecessary)
     {
       viewport.setShowSequenceFeatures(true);
       showSeqFeatures.setSelected(true);
-      if (alignPanel.getSeqPanel().seqCanvas.fr != null)
-      {
-        // update the min/max ranges where necessary
-        alignPanel.getSeqPanel().seqCanvas.fr.findAllFeatures(true);
-      }
-      if (featureSettings != null)
-      {
-        featureSettings.setTableData();
-      }
-      alignPanel.paintAlignment(true);
     }
 
-    return featuresFile;
-  }
 
+  }
   @Override
   public void dragEnter(DropTargetDragEvent evt)
   {
@@ -5731,8 +5822,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   public void setShowSeqFeatures(boolean b)
   {
-    showSeqFeatures.setSelected(true);
-    viewport.setShowSequenceFeatures(true);
+    showSeqFeatures.setSelected(b);
+    viewport.setShowSequenceFeatures(b);
   }
 
   /*
@@ -5904,6 +5995,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     for (AlignmentAnnotation aa : alignPanel.getAlignment()
             .getAlignmentAnnotation())
     {
+      /*
+       * don't display non-positional annotations on an alignment
+       */
+      if (aa.annotations == null)
+      {
+        continue;
+      }
       boolean apply = (aa.sequenceRef == null && forAlignment)
               || (aa.sequenceRef != null && forSequences);
       if (apply)
@@ -5911,7 +6009,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         aa.visible = visible;
       }
     }
-    alignPanel.validateAnnotationDimensions(false);
+    alignPanel.validateAnnotationDimensions(true);
     alignPanel.alignmentChanged();
   }