JAL-3811 document SHOW_JWS2_SERVICES user preference and provide new menu option...
[jalview.git] / src / jalview / gui / AlignFrame.java
index f4dd851..9013416 100644 (file)
  */
 package jalview.gui;
 
+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.CrossRef;
 import jalview.analysis.Dna;
+import jalview.analysis.GeneticCodeI;
 import jalview.analysis.ParseProperties;
 import jalview.analysis.SequenceIdMatcher;
 import jalview.api.AlignExportSettingI;
@@ -32,6 +81,7 @@ import jalview.api.AlignViewControllerI;
 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;
@@ -64,6 +114,7 @@ import jalview.gui.ColourMenuHelper.ColourChangeListener;
 import jalview.gui.ViewSelectionMenu.ViewSetProvider;
 import jalview.io.AlignmentProperties;
 import jalview.io.AnnotationFile;
+import jalview.io.BackupFiles;
 import jalview.io.BioJsHTMLOutput;
 import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
@@ -81,12 +132,15 @@ 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.ColourSchemeI;
 import jalview.schemes.ColourSchemes;
 import jalview.schemes.ResidueColourScheme;
 import jalview.schemes.TCoffeeColourScheme;
+import jalview.util.HttpUtils;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
 import jalview.ws.DBRefFetcher;
@@ -96,52 +150,6 @@ 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.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.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.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;
-
 /**
  * DOCUMENT ME!
  * 
@@ -163,8 +171,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   AlignViewport viewport;
 
-  ViewportRanges vpRanges;
-
   public AlignViewControllerI avc;
 
   List<AlignmentPanel> alignPanels = new ArrayList<>();
@@ -234,8 +240,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param height
    *          height of frame.
    */
-  public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns,
-          int width, int height)
+  public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width,
+          int height)
   {
     this(al, hiddenColumns, width, height, null);
   }
@@ -251,8 +257,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param sequenceSetId
    *          (may be null)
    */
-  public AlignFrame(AlignmentI al, HiddenColumns 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);
   }
@@ -270,8 +276,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param viewId
    *          (may be null)
    */
-  public AlignFrame(AlignmentI al, HiddenColumns 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);
 
@@ -336,7 +342,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       progressBar = new ProgressBar(this.statusPanel, this.statusBar);
     }
 
-    vpRanges = viewport.getRanges();
     avc = new jalview.controller.AlignViewController(this, viewport,
             alignPanel);
     if (viewport.getAlignmentConservationAnnotation() == null)
@@ -510,9 +515,10 @@ 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.getSeqPanel().numberPressed(evt.getKeyChar());
@@ -533,7 +539,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().moveCursor(0, 1);
+            alignPanel.getSeqPanel().moveCursor(0, 1,
+                    evt.isShiftDown() && !evt.isAltDown());
           }
           break;
 
@@ -544,19 +551,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().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.getSeqPanel().getKeyboardNo1());
+            slideSequences(false,
+                    alignPanel.getSeqPanel().getKeyboardNo1());
           }
           else
           {
-            alignPanel.getSeqPanel().moveCursor(-1, 0);
+            alignPanel.getSeqPanel().moveCursor(-1, 0, evt.isShiftDown());
           }
 
           break;
@@ -568,16 +576,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.getSeqPanel().moveCursor(1, 0);
+            alignPanel.getSeqPanel().moveCursor(1, 0, evt.isShiftDown());
           }
           break;
 
         case KeyEvent.VK_SPACE:
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().insertGapAtCursor(
-                    evt.isControlDown() || evt.isShiftDown()
-                            || evt.isAltDown());
+            alignPanel.getSeqPanel().insertGapAtCursor(evt.isControlDown()
+                    || evt.isShiftDown() || evt.isAltDown());
           }
           break;
 
@@ -600,9 +607,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.getSeqPanel().deleteGapAtCursor(
-                    evt.isControlDown() || evt.isShiftDown()
-                            || evt.isAltDown());
+            alignPanel.getSeqPanel().deleteGapAtCursor(evt.isControlDown()
+                    || evt.isShiftDown() || evt.isAltDown());
           }
 
           break;
@@ -649,14 +655,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
         case KeyEvent.VK_F2:
           viewport.cursorMode = !viewport.cursorMode;
-          statusBar.setText(MessageManager.formatMessage(
-                  "label.keyboard_editing_mode",
-                  new String[] { (viewport.cursorMode ? "on" : "off") }));
+          statusBar.setText(MessageManager
+                  .formatMessage("label.keyboard_editing_mode", new String[]
+                  { (viewport.cursorMode ? "on" : "off") }));
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().seqCanvas.cursorX = vpRanges
+            ViewportRanges ranges = viewport.getRanges();
+            alignPanel.getSeqPanel().seqCanvas.cursorX = ranges
                     .getStartRes();
-            alignPanel.getSeqPanel().seqCanvas.cursorY = vpRanges
+            alignPanel.getSeqPanel().seqCanvas.cursorY = ranges
                     .getStartSeq();
           }
           alignPanel.getSeqPanel().seqCanvas.repaint();
@@ -689,10 +696,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           break;
         }
         case KeyEvent.VK_PAGE_UP:
-          vpRanges.pageUp();
+          viewport.getRanges().pageUp();
           break;
         case KeyEvent.VK_PAGE_DOWN:
-          vpRanges.pageDown();
+          viewport.getRanges().pageDown();
           break;
         }
       }
@@ -705,16 +712,16 @@ 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;
         }
@@ -734,9 +741,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     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);
     }
@@ -749,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);
     }
@@ -772,7 +779,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     gatherViews.setEnabled(true);
     tabbedPane.setVisible(true);
     AlignmentPanel first = alignPanels.get(0);
-    tabbedPane.addTab(first.av.viewName, first);
+    tabbedPane.addTab(first.av.getViewName(), first);
     this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
   }
 
@@ -800,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();
                     }
 
@@ -841,17 +848,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     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
-            && conservationMenuItem.isSelected());
+    modifyConservation
+            .setEnabled(!nucleotide && conservationMenuItem.isSelected());
     showGroupConservation.setEnabled(!nucleotide);
 
-    showComplementMenuItem.setText(nucleotide ? MessageManager
-            .getString("label.protein") : MessageManager
-            .getString("label.nucleotide"));
+    showComplementMenuItem
+            .setText(nucleotide ? MessageManager.getString("label.protein")
+                    : MessageManager.getString("label.nucleotide"));
   }
 
   /**
@@ -872,7 +880,7 @@ 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.isShowColourText());
@@ -963,10 +971,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     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);
   }
 
   /*
@@ -1025,7 +1038,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         Desktop.instance.closeAssociatedWindows();
 
         FileLoader loader = new FileLoader();
-        DataSourceType protocol = fileName.startsWith("http:") ? DataSourceType.URL
+        DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(fileName)
+                ? DataSourceType.URL
                 : DataSourceType.FILE;
         loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
       }
@@ -1034,7 +1048,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         Rectangle bounds = this.getBounds();
 
         FileLoader loader = new FileLoader();
-        DataSourceType protocol = fileName.startsWith("http:") ? DataSourceType.URL
+        DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(fileName)
+                ? DataSourceType.URL
                 : DataSourceType.FILE;
         AlignFrame newframe = loader.LoadFileWaitTillLoaded(fileName,
                 protocol, currentFileFormat);
@@ -1066,8 +1081,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void addFromText_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.inputTextboxMenuItem_actionPerformed(viewport
-            .getAlignPanel());
+    Desktop.instance
+            .inputTextboxMenuItem_actionPerformed(viewport.getAlignPanel());
   }
 
   @Override
@@ -1080,7 +1095,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void save_actionPerformed(ActionEvent e)
   {
     if (fileName == null || (currentFileFormat == null)
-            || fileName.startsWith("http"))
+            || HttpUtils.startsWithHttpOrHttps(fileName))
     {
       saveAs_actionPerformed(null);
     }
@@ -1099,14 +1114,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void saveAs_actionPerformed(ActionEvent e)
   {
-    String format = currentFileFormat == null ? null : currentFileFormat
-            .getName();
-    JalviewFileChooser chooser = JalviewFileChooser.forWrite(
-            Cache.getProperty("LAST_DIRECTORY"), format);
+    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);
@@ -1116,14 +1131,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       currentFileFormat = chooser.getSelectedFormat();
       while (currentFileFormat == null)
       {
-        JvOptionPane
-                .showInternalMessageDialog(
-                        Desktop.desktop,
-                        MessageManager
-                                .getString("label.select_file_format_before_saving"),
-                        MessageManager
-                                .getString("label.file_format_not_specified"),
-                        JvOptionPane.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);
         if (value != JalviewFileChooser.APPROVE_OPTION)
@@ -1151,15 +1163,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       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 Object[] {
-                  fileName, format }));
+              "label.successfully_saved_to_file_in_format", new Object[]
+              { file, format }));
 
     }
     else
@@ -1172,13 +1185,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       }
       FormatAdapter f = new FormatAdapter(alignPanel,
               exportData.getSettings());
-      String output = f.formatSequences(
-              format,
-              exportData.getAlignment(), // class cast exceptions will
+      String output = f.formatSequences(format, exportData.getAlignment(), // class
+                                                                           // cast
+                                                                           // exceptions
+                                                                           // will
               // occur in the distant future
               exportData.getOmitHidden(), exportData.getStartEndPostions(),
-              f.getCacheSuffixDefault(format), viewport.getAlignment()
-                      .getHiddenColumns());
+              f.getCacheSuffixDefault(format),
+              viewport.getAlignment().getHiddenColumns());
 
       if (output == null)
       {
@@ -1186,31 +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
         {
-          PrintWriter out = new PrintWriter(new 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 Object[] { fileName, format.getName() }));
+                  "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)
     {
-      JvOptionPane.showInternalMessageDialog(this, MessageManager
-              .formatMessage("label.couldnt_save_file",
-                      new Object[] { fileName }), MessageManager
-              .getString("label.error_saving_file"),
-              JvOptionPane.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;
@@ -1240,8 +1290,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void outputText_actionPerformed(ActionEvent e)
   {
-    FileFormatI fileFormat = FileFormats.getInstance().forName(
-            e.getActionCommand());
+    FileFormatI fileFormat = FileFormats.getInstance()
+            .forName(e.getActionCommand());
     AlignmentExportData exportData = getAlignmentForExport(fileFormat,
             viewport, null);
     if (exportData.getSettings().isCancelled())
@@ -1256,15 +1306,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       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);
+                      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();
     }
 
@@ -1392,13 +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);
+    new AnnotationExporter(alignPanel).exportAnnotations();
   }
 
   @Override
@@ -1408,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);
 
@@ -1460,9 +1510,13 @@ 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
@@ -1510,9 +1564,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       undoMenuItem.setEnabled(true);
       CommandI command = viewport.getHistoryList().peek();
-      undoMenuItem.setText(MessageManager.formatMessage(
-              "label.undo_command",
-              new Object[] { command.getDescription() }));
+      undoMenuItem.setText(MessageManager
+              .formatMessage("label.undo_command", new Object[]
+              { command.getDescription() }));
     }
     else
     {
@@ -1525,9 +1579,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       redoMenuItem.setEnabled(true);
 
       CommandI command = viewport.getRedoList().peek();
-      redoMenuItem.setText(MessageManager.formatMessage(
-              "label.redo_command",
-              new Object[] { command.getDescription() }));
+      redoMenuItem.setText(MessageManager
+              .formatMessage("label.redo_command", new Object[]
+              { command.getDescription() }));
     }
     else
     {
@@ -1599,8 +1653,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       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() !=
@@ -1608,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());
     }
   }
 
@@ -1639,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() !=
@@ -1648,8 +1702,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());
     }
   }
 
@@ -1664,8 +1718,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       EditCommand editCommand = (EditCommand) command;
       al = editCommand.getAlignment();
-      List<Component> comps = PaintRefresher.components.get(viewport
-              .getSequenceSetId());
+      List<Component> comps = PaintRefresher.components
+              .get(viewport.getSequenceSetId());
 
       for (Component comp : comps)
       {
@@ -1696,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)
   {
@@ -1707,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 ArrayList<>();
-    if (viewport.cursorMode)
+    if (viewport.getSelectionGroup() != null && viewport.getSelectionGroup()
+            .getSize() != viewport.getAlignment().getHeight())
     {
-      sg.add(viewport.getAlignment().getSequenceAt(
-              alignPanel.getSeqPanel().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)
@@ -1756,13 +1828,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     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;
@@ -1807,9 +1879,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (!inSplitFrame && historyList != null && historyList.size() > 0
             && historyList.peek() instanceof SlideSequencesCommand)
     {
-      appendHistoryItem = ssc
-              .appendSlideCommand((SlideSequencesCommand) historyList
-                      .peek());
+      appendHistoryItem = ssc.appendSlideCommand(
+              (SlideSequencesCommand) historyList.peek());
     }
 
     if (!appendHistoryItem)
@@ -1829,7 +1900,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void copy_actionPerformed(ActionEvent e)
   {
-    System.gc();
     if (viewport.getSelectionGroup() == null)
     {
       return;
@@ -1857,37 +1927,32 @@ 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;
     }
 
-    ArrayList<int[]> hiddenColumns = null;
+    HiddenColumns hiddenColumns = null;
     if (viewport.hasHiddenColumns())
     {
-      hiddenColumns = new ArrayList<>();
       int hiddenOffset = viewport.getSelectionGroup().getStartRes();
       int hiddenCutoff = viewport.getSelectionGroup().getEndRes();
-      ArrayList<int[]> hiddenRegions = viewport.getAlignment()
-              .getHiddenColumns().getHiddenColumnsCopy();
-      for (int[] region : hiddenRegions)
-      {
-        if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
-        {
-          hiddenColumns.add(new int[] { region[0] - hiddenOffset,
-              region[1] - hiddenOffset });
-        }
-      }
+
+      // 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 };
     statusBar.setText(MessageManager.formatMessage(
-            "label.copied_sequences_to_clipboard", new Object[] { Integer
-                    .valueOf(seqs.length).toString() }));
+            "label.copied_sequences_to_clipboard", new Object[]
+            { Integer.valueOf(seqs.length).toString() }));
   }
 
   /**
@@ -2037,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()
           }
@@ -2068,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();
@@ -2098,7 +2165,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         //
         addHistoryItem(new EditCommand(
                 MessageManager.getString("label.add_sequences"),
-                Action.PASTE, sequences, 0, alignment.getWidth(), alignment));
+                Action.PASTE, sequences, 0, alignment.getWidth(),
+                alignment));
       }
       // Add any annotations attached to sequences
       for (int i = 0; i < sequences.length; i++)
@@ -2119,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();
@@ -2134,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);
           }
         }
       }
@@ -2143,7 +2212,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
 
         // propagate alignment changed.
-        vpRanges.setEndSeq(alignment.getHeight());
+        viewport.getRanges().setEndSeq(alignment.getHeight());
         if (annotationAdded)
         {
           // Duplicate sequence annotation in all views.
@@ -2205,19 +2274,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         if (Desktop.jalviewClipboard != null
                 && Desktop.jalviewClipboard[2] != null)
         {
-          List<int[]> hc = (List<int[]>) Desktop.jalviewClipboard[2];
-          for (int[] region : hc)
-          {
-            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.getSeqPanel().seqCanvas.getFeatureRenderer()
-                .transferSettings(
-                        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.
@@ -2256,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");
@@ -2265,19 +2330,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       if (Desktop.jalviewClipboard != null
               && Desktop.jalviewClipboard[2] != null)
       {
-        List<int[]> hc = (List<int[]>) Desktop.jalviewClipboard[2];
-        for (int region[] : hc)
-        {
-          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.getSeqPanel().seqCanvas.getFeatureRenderer()
-              .transferSettings(
-                      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.
@@ -2339,8 +2400,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
      */
     if (sg.getSize() == viewport.getAlignment().getHeight())
     {
-      boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes()) + 1) == viewport
-              .getAlignment().getWidth()) ? true : false;
+      boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes())
+              + 1) == viewport.getAlignment().getWidth()) ? true : false;
       if (isEntireAlignWidth)
       {
         int confirm = JvOptionPane.showConfirmDialog(this,
@@ -2369,8 +2430,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     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
@@ -2395,7 +2456,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
       alignPanel.updateAnnotation();
-      alignPanel.paintAlignment(true);
+      alignPanel.paintAlignment(true, true);
     }
   }
 
@@ -2408,20 +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();
     // JAL-2034 - should delegate to
     // alignPanel to decide if overview needs
     // updating.
-    alignPanel.paintAlignment(false);
+    alignPanel.paintAlignment(false, false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
   }
 
@@ -2441,13 +2499,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     viewport.setSelectionGroup(null);
     viewport.getColumnSelection().clear();
-    viewport.setSelectionGroup(null);
-    alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(null);
+    viewport.setSearchResults(null);
     alignPanel.getIdPanel().getIdCanvas().searchResults = null;
     // JAL-2034 - should delegate to
     // alignPanel to decide if overview needs
     // updating.
-    alignPanel.paintAlignment(false);
+    alignPanel.paintAlignment(false, false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2478,7 +2535,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // alignPanel to decide if overview needs
     // updating.
 
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2487,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();
   }
 
@@ -2534,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
       {
@@ -2547,7 +2604,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
                 column, viewport.getAlignment());
-        vpRanges.setStartRes(0);
+        viewport.getRanges().setStartRes(0);
       }
       else
       {
@@ -2555,10 +2612,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 column, viewport.getAlignment());
       }
 
-      statusBar.setText(MessageManager.formatMessage(
-              "label.removed_columns",
-              new String[] { Integer.valueOf(trimRegion.getSize())
-                      .toString() }));
+      statusBar.setText(MessageManager
+              .formatMessage("label.removed_columns", new String[]
+              { Integer.valueOf(trimRegion.getSize()).toString() }));
 
       addHistoryItem(trimRegion);
 
@@ -2571,8 +2627,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         }
       }
 
-      viewport.firePropertyChange("alignment", null, viewport
-              .getAlignment().getSequences());
+      viewport.firePropertyChange("alignment", null,
+              viewport.getAlignment().getSequences());
     }
   }
 
@@ -2590,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();
     }
@@ -2606,23 +2662,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     addHistoryItem(removeGapCols);
 
-    statusBar.setText(MessageManager.formatMessage(
-            "label.removed_empty_columns",
-            new Object[] { Integer.valueOf(removeGapCols.getSize())
-                    .toString() }));
+    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(vpRanges.getStartRes());
+    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);
-    vpRanges.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());
 
   }
 
@@ -2640,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();
     }
@@ -2653,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(vpRanges.getStartRes());
+    int startRes = seq.findPosition(viewport.getRanges().getStartRes());
 
     addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
             viewport.getAlignment()));
 
-    vpRanges.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());
 
   }
 
@@ -2675,20 +2731,19 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void padGapsMenuitem_actionPerformed(ActionEvent e)
   {
     viewport.setPadGaps(padGapsMenuitem.isSelected());
-    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
-            .getSequences());
+    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();
+    new Finder(alignPanel);
   }
 
   /**
@@ -2714,8 +2769,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     /*
      * Create a new AlignmentPanel (with its own, new Viewport)
      */
-    AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
-            true);
+    AlignmentPanel newap = new jalview.project.Jalview2XML()
+            .copyAlignPanel(alignPanel);
     if (!copyAnnotation)
     {
       /*
@@ -2727,10 +2782,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     newap.av.setGatherViewsHere(false);
 
-    if (viewport.viewName == null)
+    if (viewport.getViewName() == null)
     {
-      viewport.viewName = MessageManager
-              .getString("label.view_name_original");
+      viewport.setViewName(
+              MessageManager.getString("label.view_name_original"));
     }
 
     /*
@@ -2740,6 +2795,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     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
@@ -2754,7 +2815,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       newap.refresh(true); // adjust layout of annotations
     }
 
-    newap.av.viewName = getNewViewName(viewTitle);
+    newap.av.setViewName(getNewViewName(viewTitle));
 
     addAlignmentPanel(newap, true);
     newap.alignmentChanged();
@@ -2790,8 +2851,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
 
-    List<Component> comps = PaintRefresher.components.get(viewport
-            .getSequenceSetId());
+    List<Component> comps = PaintRefresher.components
+            .get(viewport.getSequenceSetId());
 
     List<String> existingNames = getExistingViewNames(comps);
 
@@ -2817,9 +2878,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       if (comp instanceof AlignmentPanel)
       {
         AlignmentPanel ap = (AlignmentPanel) comp;
-        if (!existingNames.contains(ap.av.viewName))
+        if (!existingNames.contains(ap.av.getViewName()))
         {
-          existingNames.add(ap.av.viewName);
+          existingNames.add(ap.av.getViewName());
         }
       }
     }
@@ -2869,21 +2930,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     alignPanel.getIdPanel().getIdCanvas()
             .setPreferredSize(alignPanel.calculateIdWidth());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
   }
 
   @Override
   public void idRightAlign_actionPerformed(ActionEvent e)
   {
     viewport.setRightAlignIds(idRightAlign.isSelected());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false, false);
   }
 
   @Override
   public void centreColumnLabels_actionPerformed(ActionEvent e)
   {
     viewport.setCentreColumnLabels(centreColumnLabelsMenuItem.getState());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false, false);
   }
 
   /*
@@ -2902,7 +2963,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.setFollowHighlight(state);
     if (state)
     {
-      alignPanel.scrollToPosition(viewport.getSearchResults(), false);
+      alignPanel.scrollToPosition(viewport.getSearchResults());
     }
   }
 
@@ -2916,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);
   }
 
   /**
@@ -2945,7 +3006,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void showAllColumns_actionPerformed(ActionEvent e)
   {
     viewport.showAllHiddenColumns();
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, true);
     viewport.sendSelection();
   }
 
@@ -2971,9 +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.hasSelectedColumns()
-              || (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.
@@ -3050,7 +3110,8 @@ 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();
   }
 
@@ -3066,7 +3127,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.showAllHiddenColumns();
     viewport.showAllHiddenSeqs();
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, true);
     viewport.sendSelection();
   }
 
@@ -3074,7 +3135,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void hideSelColumns_actionPerformed(ActionEvent e)
   {
     viewport.hideSelectedColumns();
-    alignPanel.paintAlignment(true);
+    alignPanel.updateLayout();
+    alignPanel.paintAlignment(true, true);
     viewport.sendSelection();
   }
 
@@ -3095,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);
   }
 
   /**
@@ -3108,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);
   }
 
   /**
@@ -3121,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);
   }
 
   /**
@@ -3134,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);
   }
 
   /**
@@ -3147,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);
   }
 
   /**
@@ -3160,7 +3225,7 @@ 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;
@@ -3174,9 +3239,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void featureSettings_actionPerformed(ActionEvent e)
   {
+    showFeatureSettingsUI();
+  }
+
+  @Override
+  public FeatureSettingsControllerI showFeatureSettingsUI()
+  {
     if (featureSettings != null)
     {
-      featureSettings.close();
+      featureSettings.closeOldSettings();
       featureSettings = null;
     }
     if (!showSeqFeatures.isSelected())
@@ -3186,6 +3257,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       showSeqFeatures_actionPerformed(null);
     }
     featureSettings = new FeatureSettings(this);
+    return featureSettings;
   }
 
   /**
@@ -3198,7 +3270,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void showSeqFeatures_actionPerformed(ActionEvent evt)
   {
     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, true);
   }
 
   /**
@@ -3229,14 +3301,15 @@ 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 Object[] { contents.toString() }));
+    editPane.setText(
+            MessageManager.formatMessage("label.html_content", new Object[]
+            { contents.toString() }));
     JInternalFrame frame = new JInternalFrame();
     frame.getContentPane().add(new JScrollPane(editPane));
 
-    Desktop.addInternalFrame(frame, MessageManager.formatMessage(
-            "label.alignment_properties", new Object[] { getTitle() }),
-            500, 400);
+    Desktop.addInternalFrame(frame, MessageManager
+            .formatMessage("label.alignment_properties", new Object[]
+            { getTitle() }), 500, 400);
   }
 
   /**
@@ -3256,21 +3329,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     JInternalFrame frame = new JInternalFrame();
     final OverviewPanel overview = new OverviewPanel(alignPanel);
     frame.setContentPane(overview);
-    Desktop.addInternalFrame(frame, MessageManager.formatMessage(
-            "label.overview_params", new Object[] { this.getTitle() }),
-            true, frame.getWidth(), frame.getHeight(), true, true);
+    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)
-      {
-        overview.dispose();
-        alignPanel.setOverviewPanel(null);
-      };
-    });
+      frame.addKeyListener(getKeyListeners()[0]);
+    }
 
     alignPanel.setOverviewPanel(overview);
   }
@@ -3335,7 +3414,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
      * otherwise set the chosen colour scheme (or null for 'None')
      */
     ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(name,
-            viewport.getAlignment(), viewport.getHiddenRepSequences());
+            viewport, viewport.getAlignment(),
+            viewport.getHiddenRepSequences());
     changeColour(cs);
   }
 
@@ -3352,7 +3432,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     viewport.setGlobalColourScheme(cs);
 
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, true);
   }
 
   /**
@@ -3361,8 +3441,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void modifyPID_actionPerformed()
   {
-    SliderPanel.setPIDSliderSource(alignPanel,
-            viewport.getResidueShading(), alignPanel.getViewName());
+    SliderPanel.setPIDSliderSource(alignPanel, viewport.getResidueShading(),
+            alignPanel.getViewName());
     SliderPanel.showPIDSlider();
   }
 
@@ -3433,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));
+    AlignmentSorter.sortByPID(viewport.getAlignment(),
+            viewport.getAlignment().getSequenceAt(0));
     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
             viewport.getAlignment()));
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(true, false);
   }
 
   /**
@@ -3451,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);
   }
 
   /**
@@ -3469,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);
   }
 
   /**
@@ -3486,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);
   }
 
   /**
@@ -3513,8 +3593,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if ((viewport.getSelectionGroup() == null)
             || (viewport.getSelectionGroup().getSize() < 2))
     {
-      JvOptionPane.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"),
               JvOptionPane.WARNING_MESSAGE);
     }
@@ -3534,8 +3615,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.autoCalculateConsensus = autoCalculate.isSelected();
     if (viewport.autoCalculateConsensus)
     {
-      viewport.firePropertyChange("alignment", null, viewport
-              .getAlignment().getSequences());
+      viewport.firePropertyChange("alignment", null,
+              viewport.getAlignment().getSequences());
     }
   }
 
@@ -3561,7 +3642,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param options
    *          parameters for the distance or similarity calculation
    */
-  void newTreePanel(String type, String modelName, SimilarityParamsI options)
+  void newTreePanel(String type, String modelName,
+          SimilarityParamsI options)
   {
     String frameTitle = "";
     TreePanel tp;
@@ -3577,14 +3659,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         if (_s.getLength() < sg.getEndRes())
         {
-          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);
+          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;
         }
@@ -3604,9 +3684,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     frameTitle += " from ";
 
-    if (viewport.viewName != null)
+    if (viewport.getViewName() != null)
     {
-      frameTitle += viewport.viewName + " of ";
+      frameTitle += viewport.getViewName() + " of ";
     }
 
     frameTitle += this.title;
@@ -3625,8 +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 Object[] { 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()
     {
@@ -3639,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);
       }
     });
   }
@@ -3671,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);
       }
     });
   }
@@ -3696,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
@@ -3739,8 +3821,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     sortByTreeMenu.removeAll();
 
-    List<Component> comps = PaintRefresher.components.get(viewport
-            .getSequenceSetId());
+    List<Component> comps = PaintRefresher.components
+            .get(viewport.getSequenceSetId());
     List<TreePanel> treePanels = new ArrayList<>();
     for (Component comp : comps)
     {
@@ -3785,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;
   }
 
@@ -3879,9 +3961,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.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);
 
@@ -3896,20 +3979,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
       } catch (Exception ex)
       {
-        JvOptionPane
-                .showMessageDialog(
-                        Desktop.desktop,
-                        ex.getMessage(),
-                        MessageManager
-                                .getString("label.problem_reading_tree_file"),
-                        JvOptionPane.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())
       {
-        JvOptionPane.showMessageDialog(Desktop.desktop, fin
-                .getWarningMessage(), MessageManager
-                .getString("label.possible_problem_with_tree_file"),
+        JvOptionPane.showMessageDialog(Desktop.desktop,
+                fin.getWarningMessage(),
+                MessageManager
+                        .getString("label.possible_problem_with_tree_file"),
                 JvOptionPane.WARNING_MESSAGE);
       }
     }
@@ -4094,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)
                   {
@@ -4113,8 +4199,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                           }
                         }
                       }
-
                     }
+
                     if (jws2servs.isRunning())
                     {
                       JMenuItem tm = new JMenuItem(
@@ -4122,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);
@@ -4139,9 +4245,9 @@ 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);
               }
             }
           });
@@ -4226,7 +4332,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           @Override
           public void actionPerformed(ActionEvent e)
           {
-            showProductsFor(af.viewport.getSequenceSelection(), dna, source);
+            showProductsFor(af.viewport.getSequenceSelection(), dna,
+                    source);
           }
         });
         showProducts.add(xtype);
@@ -4235,9 +4342,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       showProducts.setEnabled(showp);
     } catch (Exception e)
     {
-      Cache.log
-              .warn("canShowProducts 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;
@@ -4254,10 +4361,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param source
    *          the database to show cross-references for
    */
-  protected void showProductsFor(final SequenceI[] sel,
-          final boolean _odna, final String source)
+  protected void showProductsFor(final SequenceI[] sel, final boolean _odna,
+          final String source)
   {
-    new Thread(CrossRefAction.showProductsFor(sel, _odna, source, this))
+    new Thread(CrossRefAction.getHandlerFor(sel, _odna, source, this))
             .start();
   }
 
@@ -4266,20 +4373,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * frame's DNA sequences to their aligned protein (amino acid) equivalents.
    */
   @Override
-  public void showTranslation_actionPerformed(ActionEvent e)
+  public void showTranslation_actionPerformed(GeneticCodeI codeTable)
   {
     AlignmentI al = null;
     try
     {
       Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
 
-      al = dna.translateCdna();
+      al = dna.translateCdna(codeTable);
     } catch (Exception ex)
     {
       jalview.bin.Cache.log.error(
               "Exception during translation. Please report this !", ex);
-      final String msg = MessageManager
-              .getString("label.error_when_translating_sequences_submit_bug_report");
+      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");
@@ -4289,8 +4396,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     if (al == null || al.getHeight() == 0)
     {
-      final String msg = MessageManager
-              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
+      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,
@@ -4300,9 +4407,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
       af.setFileFormat(this.currentFileFormat);
-      final String newTitle = MessageManager.formatMessage(
-              "label.translation_of_params",
-              new Object[] { this.getTitle() });
+      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))
       {
@@ -4384,7 +4491,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // Java's Transferable for native dnd
     evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
-    List<String> files = new ArrayList<>();
+    final AlignFrame thisaf = this;
+    final List<String> files = new ArrayList<>();
     List<DataSourceType> protocols = new ArrayList<>();
 
     try
@@ -4396,145 +4504,161 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     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<>();
-        ArrayList<String> filesnotmatched = new ArrayList<>();
-        for (int i = 0; i < files.size(); i++)
+        @Override
+        public void run()
         {
-          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)
-          {
-            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)
               {
-                l = c;
-              } while ((c = pdbfn.indexOf(".", l)) > l);
-              if (l > -1)
+                File fl = new File(file);
+                pdbfn = fl.getName();
+              }
+              else if (protocol == DataSourceType.URL)
               {
-                pdbfn = pdbfn.substring(0, l);
+                URL url = new URL(file);
+                pdbfn = url.getFile();
+              }
+              if (pdbfn.length() > 0)
+              {
+                // 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)
             {
-              FileFormatI 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 && type.isStructureFile())
+              else
               {
-                filesmatched.add(new Object[] { file, protocol, mtch });
-                continue;
+                /*
+                 * add declined structures as sequences
+                 */
+                for (Object[] o : filesmatched)
+                {
+                  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)
-                  || JvOptionPane
-                          .showConfirmDialog(
-                                  this,
-                                  MessageManager
-                                          .formatMessage(
-                                                  "label.automatically_associate_structure_files_with_sequences_same_name",
-                                                  new Object[] { Integer
-                                                          .valueOf(
-                                                                  filesmatched
-                                                                          .size())
-                                                          .toString() }),
-                                  MessageManager
-                                          .getString("label.automatically_associate_structure_files_by_name"),
-                                  JvOptionPane.YES_NO_OPTION) == JvOptionPane.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],
-                                (DataSourceType) fm[1], toassoc, false,
-                                Desktop.instance);
-                if (pe != null)
-                {
-                  System.err.println("Associated file : "
-                          + ((String) fm[0]) + " with "
-                          + toassoc.getDisplayId(true));
-                  assocfiles++;
-                }
+                return;
               }
-              alignPanel.paintAlignment(true);
+              for (String fn : filesnotmatched)
+              {
+                loadJalviewDataFile(fn, null, null, null);
+              }
+
             }
-          }
-        }
-        if (filesnotmatched.size() > 0)
-        {
-          if (assocfiles > 0
-                  && (Cache.getDefault(
-                          "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JvOptionPane
-                          .showConfirmDialog(
-                                  this,
-                                  "<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))
-          {
-            return;
-          }
-          for (String fn : filesnotmatched)
+          } catch (Exception ex)
           {
-            loadJalviewDataFile(fn, null, null, null);
+            ex.printStackTrace();
           }
-
         }
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
-      }
+      }).start();
     }
   }
 
@@ -4562,9 +4686,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // 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 || FileFormat.Pfam
-              .equals(format)) ? new AnnotationFile()
-              .annotateAlignmentView(viewport, file, sourceType) : false;
+      boolean isAnnotation = (format == null
+              || FileFormat.Pfam.equals(format))
+                      ? new AnnotationFile().annotateAlignmentView(viewport,
+                              file, sourceType)
+                      : false;
 
       if (!isAnnotation)
       {
@@ -4578,25 +4704,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             if (tcf.annotateAlignment(viewport.getAlignment(), true))
             {
               buildColourMenu();
-              changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
+              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
-              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);
+              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
@@ -4605,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)
@@ -4621,14 +4746,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           if (FileFormat.ScoreMatrix == format)
           {
-            ScoreMatrixFile sm = new ScoreMatrixFile(new FileParse(file,
-                    sourceType));
+            ScoreMatrixFile sm = new ScoreMatrixFile(
+                    new FileParse(file, sourceType));
             sm.parse();
             // todo: i18n this message
-            statusBar
-                    .setText(MessageManager.formatMessage(
-                            "label.successfully_loaded_matrix",
-                            sm.getMatrixName()));
+            statusBar.setText(MessageManager.formatMessage(
+                    "label.successfully_loaded_matrix",
+                    sm.getMatrixName()));
           }
           else if (FileFormat.Jnet.equals(format))
           {
@@ -4636,11 +4760,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             new JnetAnnotationMaker();
             JnetAnnotationMaker.add_annotation(predictions,
                     viewport.getAlignment(), 0, false);
-            SequenceI repseq = viewport.getAlignment().getSequenceAt(0);
-            viewport.getAlignment().setSeqrep(repseq);
-            HiddenColumns cs = new HiddenColumns();
-            cs.hideInsertionsFor(repseq);
-            viewport.getAlignment().setHiddenColumns(cs);
+            viewport.getAlignment().setupJPredAlignment();
             isAnnotation = true;
           }
           // else if (IdentifyFile.FeaturesFile.equals(format))
@@ -4648,7 +4768,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           {
             if (parseFeaturesFile(file, sourceType))
             {
-              alignPanel.paintAlignment(true);
+              SplitFrame splitFrame = (SplitFrame) getSplitViewContainer();
+              if (splitFrame != null)
+              {
+                splitFrame.repaint();
+              }
+              else
+              {
+                alignPanel.paintAlignment(true, true);
+              }
             }
           }
           else
@@ -4663,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)
     {
@@ -4678,11 +4806,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       }
       new OOMWarning(
               "loading data "
-                      + (sourceType != null ? (sourceType == DataSourceType.PASTE ? "from clipboard."
-                              : "using " + sourceType + " 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);
     }
   }
 
@@ -4699,6 +4832,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       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();
+        }
+      }
+
     }
 
     /*
@@ -4733,7 +4881,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     final AlignViewportI peer = viewport.getCodingComplement();
     if (peer != null)
     {
-      AlignFrame linkedAlignFrame = ((AlignViewport) peer).getAlignPanel().alignFrame;
+      AlignFrame linkedAlignFrame = ((AlignViewport) peer)
+              .getAlignPanel().alignFrame;
       if (linkedAlignFrame.tabbedPane.getTabCount() > index)
       {
         linkedAlignFrame.tabbedPane.setSelectedIndex(index);
@@ -4755,7 +4904,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       if (reply != null)
       {
-        viewport.viewName = reply;
+        viewport.setViewName(reply);
         // TODO warn if reply is in getExistingViewNames()?
         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
       }
@@ -4848,30 +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()
     {
 
@@ -4885,14 +5035,22 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           {
             boolean isNucleotide = alignPanel.alignFrame.getViewport()
                     .getAlignment().isNucleotide();
-            DBRefFetcher dbRefFetcher = new DBRefFetcher(alignPanel.av
-                    .getSequenceSelection(), alignPanel.alignFrame, null,
+            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();
               }
             });
@@ -4978,6 +5136,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                                   @Override
                                   public void finished()
                                   {
+                                    FeatureSettingsModelI srcSettings = dassource[0]
+                                            .getFeatureColourScheme();
+                                    alignPanel.av.mergeFeaturesStyle(
+                                            srcSettings);
                                     AlignFrame.this.setMenusForViewport();
                                   }
                                 });
@@ -4989,8 +5151,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 });
                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true,
                         MessageManager.formatMessage(
-                                "label.fetch_retrieve_from",
-                                new Object[] { src.getDbName() })));
+                                "label.fetch_retrieve_from", new Object[]
+                                { src.getDbName() })));
                 dfetch.add(fetchr);
                 comp++;
               }
@@ -5000,9 +5162,9 @@ 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 Object[] { src.getDbSource() }));
+                fetchr = new JMenuItem(MessageManager
+                        .formatMessage("label.fetch_all_param", new Object[]
+                        { src.getDbSource() }));
                 fetchr.addActionListener(new ActionListener()
                 {
                   @Override
@@ -5040,30 +5202,33 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true,
                         MessageManager.formatMessage(
                                 "label.fetch_retrieve_from_all_sources",
-                                new Object[] {
-                                    Integer.valueOf(otherdb.size())
-                                            .toString(), src.getDbSource(),
-                                    src.getDbName() })));
+                                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 Object[] { src.getDbSource() }));
+                        "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 Object[] { sname });
+                    imname = MessageManager
+                            .formatMessage("label.from_msname", new Object[]
+                            { sname });
                   }
                   fetchr = new JMenuItem(msname);
                   final DbSourceProxy[] dassrc = { sproxy };
@@ -5102,8 +5267,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                     }
 
                   });
-                  fetchr.setToolTipText("<html>"
-                          + MessageManager.formatMessage(
+                  fetchr.setToolTipText(
+                          "<html>" + MessageManager.formatMessage(
                                   "label.fetch_retrieve_from", new Object[]
                                   { dbname }));
                   ifetch.add(fetchr);
@@ -5178,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);
   }
 
   /*
@@ -5267,7 +5432,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
       alignPanel.updateAnnotation();
-      alignPanel.paintAlignment(true);
+      alignPanel.paintAlignment(true,
+              viewport.needToUpdateStructureViews());
     }
   }
 
@@ -5279,7 +5445,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       viewport.getAlignment().setSeqrep(null);
       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
       alignPanel.updateAnnotation();
-      alignPanel.paintAlignment(true);
+      alignPanel.paintAlignment(true, true);
     }
   }
 
@@ -5288,6 +5454,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (avc.createGroup())
     {
+      if (applyAutoAnnotationSettings.isSelected())
+      {
+        alignPanel.updateAnnotation(true, false);
+      }
       alignPanel.alignmentChanged();
     }
   }
@@ -5308,17 +5478,14 @@ 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
-            && tabbedPane.getTabCount() > 0
-            && alignPanels.indexOf(alignmentPanel) != tabbedPane
-                    .getSelectedIndex())
+    if (tabbedPane != null && tabbedPane.getTabCount() > 0 && alignPanels
+            .indexOf(alignmentPanel) != tabbedPane.getSelectedIndex())
     {
       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
     }
@@ -5372,7 +5539,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
     this.alignPanel.av
             .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false, false);
   }
 
   /**
@@ -5381,7 +5548,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   public List<? extends AlignmentViewPanel> getAlignPanels()
   {
-    return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
+    // alignPanels is never null
+    // return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
+    return alignPanels;
   }
 
   /**
@@ -5421,15 +5590,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // show a warning dialog no mapped cDNA
       return;
     }
-    AlignmentI cdna = new Alignment(cdnaSeqs.toArray(new SequenceI[cdnaSeqs
-            .size()]));
+    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);
+    Desktop.addInternalFrame(alignFrame, newtitle, AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
   }
 
   /**
@@ -5462,8 +5631,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       al = dna.reverseCdna(complement);
       viewport.addAlignment(al, "");
       addHistoryItem(new EditCommand(
-              MessageManager.getString("label.add_sequences"),
-              Action.PASTE, al.getSequencesArray(), 0, al.getWidth(),
+              MessageManager.getString("label.add_sequences"), Action.PASTE,
+              al.getSequencesArray(), 0, al.getWidth(),
               viewport.getAlignment()));
     } catch (Exception ex)
     {
@@ -5490,12 +5659,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       } 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);
+        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+                MessageManager.getString("label.couldnt_run_groovy_script"),
+                MessageManager.getString("label.groovy_support_failed"),
+                JvOptionPane.ERROR_MESSAGE);
       }
     }
     else
@@ -5535,9 +5702,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     // 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);
+            (actionEvent.getModifiers() & ActionEvent.ALT_MASK) != 0, true,
+            (actionEvent.getModifiers() & (ActionEvent.META_MASK
+                    | ActionEvent.CTRL_MASK)) != 0);
   }
 
   /**
@@ -5552,15 +5719,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     colourMenu.add(textColour);
     colourMenu.addSeparator();
 
-    ColourMenuHelper.addMenuItems(colourMenu, this,
+    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);
-    colourMenu.add(annotationColour);
 
     ColourSchemeI colourScheme = viewport.getGlobalColourScheme();
     ColourMenuHelper.setColourSelected(colourMenu, colourScheme);
@@ -5577,6 +5745,41 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       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