update author list in license for (JAL-826)
[jalview.git] / src / jalview / gui / AlignFrame.java
index 6302efb..9b91255 100755 (executable)
 /*
- * Jalview - A Sequence Alignment Editor and Viewer
- * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Softwarechang
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
+ * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ * 
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  */
 package jalview.gui;
 
-import java.beans.*;
-import java.io.*;
-import java.util.*;
-
-import java.awt.*;
-import java.awt.datatransfer.*;
-import java.awt.dnd.*;
-import java.awt.event.*;
-import java.awt.print.*;
-import javax.swing.*;
-import javax.swing.event.MenuEvent;
-
-import jalview.analysis.*;
-import jalview.commands.*;
-import jalview.datamodel.*;
-import jalview.io.*;
-import jalview.jbgui.*;
-import jalview.schemes.*;
-import jalview.ws.*;
+import jalview.analysis.AAFrequency;
+import jalview.analysis.AlignmentSorter;
+import jalview.analysis.Conservation;
+import jalview.analysis.CrossRef;
+import jalview.analysis.NJTree;
+import jalview.analysis.ParseProperties;
+import jalview.analysis.SequenceIdMatcher;
+import jalview.bin.Cache;
+import jalview.commands.CommandI;
+import jalview.commands.EditCommand;
+import jalview.commands.OrderCommand;
+import jalview.commands.RemoveGapColCommand;
+import jalview.commands.RemoveGapsCommand;
+import jalview.commands.SlideSequencesCommand;
+import jalview.commands.TrimRegionCommand;
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SeqCigar;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.io.AlignmentProperties;
+import jalview.io.AnnotationFile;
+import jalview.io.FeaturesFile;
+import jalview.io.FileLoader;
+import jalview.io.FormatAdapter;
+import jalview.io.HTMLOutput;
+import jalview.io.IdentifyFile;
+import jalview.io.JalviewFileChooser;
+import jalview.io.JalviewFileView;
+import jalview.io.JnetAnnotationMaker;
+import jalview.io.NewickFile;
+import jalview.jbgui.GAlignFrame;
+import jalview.schemes.Blosum62ColourScheme;
+import jalview.schemes.BuriedColourScheme;
+import jalview.schemes.ClustalxColourScheme;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.HelixColourScheme;
+import jalview.schemes.HydrophobicColourScheme;
+import jalview.schemes.NucleotideColourScheme;
+import jalview.schemes.PIDColourScheme;
+import jalview.schemes.PurinePyrimidineColourScheme;
+import jalview.schemes.ResidueProperties;
+import jalview.schemes.StrandColourScheme;
+import jalview.schemes.TaylorColourScheme;
+import jalview.schemes.TurnColourScheme;
+import jalview.schemes.UserColourScheme;
+import jalview.schemes.ZappoColourScheme;
+import jalview.ws.WSMenuEntryProviderI;
+import jalview.ws.jws1.Discoverer;
+import jalview.ws.jws2.Jws2Discoverer;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.GridLayout;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.print.PageFormat;
+import java.awt.print.PrinterJob;
+import java.beans.PropertyChangeEvent;
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.swing.JButton;
+import javax.swing.JEditorPane;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
 
 /**
  * DOCUMENT ME!
- *
+ * 
  * @author $author$
  * @version $Revision$
  */
-public class AlignFrame
-    extends GAlignFrame implements DropTargetListener
+public class AlignFrame extends GAlignFrame implements DropTargetListener,
+        IProgressIndicator
 {
+
   /** DOCUMENT ME!! */
   public static final int DEFAULT_WIDTH = 700;
 
   /** DOCUMENT ME!! */
   public static final int DEFAULT_HEIGHT = 500;
+
   public AlignmentPanel alignPanel;
 
   AlignViewport viewport;
 
   Vector alignPanels = new Vector();
 
-
-  /** DOCUMENT ME!! */
+  /**
+   * Last format used to load or save alignments in this window
+   */
   String currentFileFormat = null;
 
+  /**
+   * Current filename for this alignment
+   */
   String fileName = null;
 
-
   /**
-   * Creates a new AlignFrame object.
-   *
-   * @param al DOCUMENT ME!
+   * Creates a new AlignFrame object with specific width and height.
+   * 
+   * @param al
+   * @param width
+   * @param height
    */
   public AlignFrame(AlignmentI al, int width, int height)
   {
     this(al, null, width, height);
   }
 
+  /**
+   * Creates a new AlignFrame object with specific width, height and
+   * sequenceSetId
+   * 
+   * @param al
+   * @param width
+   * @param height
+   * @param sequenceSetId
+   */
+  public AlignFrame(AlignmentI al, int width, int height,
+          String sequenceSetId)
+  {
+    this(al, null, width, height, sequenceSetId);
+  }
+
+  /**
+   * Creates a new AlignFrame object with specific width, height and
+   * sequenceSetId
+   * 
+   * @param al
+   * @param width
+   * @param height
+   * @param sequenceSetId
+   * @param viewId
+   */
+  public AlignFrame(AlignmentI al, int width, int height,
+          String sequenceSetId, String viewId)
+  {
+    this(al, null, width, height, sequenceSetId, viewId);
+  }
 
   /**
    * new alignment window with hidden columns
-   * @param al AlignmentI
-   * @param hiddenColumns ColumnSelection or null
+   * 
+   * @param al
+   *          AlignmentI
+   * @param hiddenColumns
+   *          ColumnSelection or null
+   * @param width
+   *          Width of alignment frame
+   * @param height
+   *          height of frame.
+   */
+  public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
+          int width, int height)
+  {
+    this(al, hiddenColumns, width, height, null);
+  }
+
+  /**
+   * Create alignment frame for al with hiddenColumns, a specific width and
+   * height, and specific sequenceId
+   * 
+   * @param al
+   * @param hiddenColumns
+   * @param width
+   * @param height
+   * @param sequenceSetId
+   *          (may be null)
    */
   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
-                    int width, int height)
+          int width, int height, String sequenceSetId)
   {
-    this.setSize(width, height);
-    viewport = new AlignViewport(al, hiddenColumns);
+    this(al, hiddenColumns, width, height, sequenceSetId, null);
+  }
+
+  /**
+   * Create alignment frame for al with hiddenColumns, a specific width and
+   * height, and specific sequenceId
+   * 
+   * @param al
+   * @param hiddenColumns
+   * @param width
+   * @param height
+   * @param sequenceSetId
+   *          (may be null)
+   * @param viewId
+   *          (may be null)
+   */
+  public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
+          int width, int height, String sequenceSetId, String viewId)
+  {
+    setSize(width, height);
+    viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
 
     alignPanel = new AlignmentPanel(this, viewport);
 
-    if(al.getDataset()==null)
+    if (al.getDataset() == null)
     {
       al.setDataset(null);
     }
@@ -100,8 +263,11 @@ public class AlignFrame
 
   /**
    * Make a new AlignFrame from exisiting alignmentPanels
-   * @param ap AlignmentPanel
-   * @param av AlignViewport
+   * 
+   * @param ap
+   *          AlignmentPanel
+   * @param av
+   *          AlignViewport
    */
   public AlignFrame(AlignmentPanel ap)
   {
@@ -111,6 +277,10 @@ public class AlignFrame
     init();
   }
 
+  /**
+   * initalise the alignframe from the underlying viewport data and the
+   * configurations
+   */
   void init()
   {
     if (viewport.conservation == null)
@@ -118,12 +288,13 @@ public class AlignFrame
       BLOSUM62Colour.setEnabled(false);
       conservationMenuItem.setEnabled(false);
       modifyConservation.setEnabled(false);
-    //  PIDColour.setEnabled(false);
-    //  abovePIDThreshold.setEnabled(false);
-    //  modifyPID.setEnabled(false);
+      // PIDColour.setEnabled(false);
+      // abovePIDThreshold.setEnabled(false);
+      // modifyPID.setEnabled(false);
     }
 
-    String sortby = jalview.bin.Cache.getDefault("SORT_ALIGNMENT", "No sort");
+    String sortby = jalview.bin.Cache.getDefault("SORT_ALIGNMENT",
+            "No sort");
 
     if (sortby.equals("Id"))
     {
@@ -135,274 +306,253 @@ public class AlignFrame
     }
 
     if (Desktop.desktop != null)
-   {
-     this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
-     addServiceListeners();
-     setGUINucleotide(viewport.alignment.isNucleotide());
-   }
+    {
+      this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
+      addServiceListeners();
+      setGUINucleotide(viewport.alignment.isNucleotide());
+    }
 
-   setMenusFromViewport(viewport);
-   buildSortByAnnotationScoresMenu();
-   if (viewport.wrapAlignment)
-   {
-     wrapMenuItem_actionPerformed(null);
-   }
+    setMenusFromViewport(viewport);
+    buildSortByAnnotationScoresMenu();
+    if (viewport.wrapAlignment)
+    {
+      wrapMenuItem_actionPerformed(null);
+    }
 
-    if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW",false))
+    if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW", false))
     {
       this.overviewMenuItem_actionPerformed(null);
     }
 
-   addKeyListener();
+    addKeyListener();
 
   }
 
+  /**
+   * Change the filename and format for the alignment, and enable the 'reload'
+   * button functionality.
+   * 
+   * @param file
+   *          valid filename
+   * @param format
+   *          format of file
+   */
   public void setFileName(String file, String format)
   {
-     fileName = file;
-     currentFileFormat = format;
-     reload.setEnabled(true);
+    fileName = file;
+    currentFileFormat = format;
+    reload.setEnabled(true);
   }
 
   void addKeyListener()
   {
-      addKeyListener(new KeyAdapter()
+    addKeyListener(new KeyAdapter()
+    {
+      public void keyPressed(KeyEvent evt)
       {
-        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))
+                && Character.isDigit(evt.getKeyChar()))
+          alignPanel.seqPanel.numberPressed(evt.getKeyChar());
+
+        switch (evt.getKeyCode())
         {
-          if (viewport.cursorMode &&
-              ( (evt.getKeyCode() >= KeyEvent.VK_0 &&
-                 evt.getKeyCode() <= KeyEvent.VK_9)
-               ||
-               (evt.getKeyCode() >= KeyEvent.VK_NUMPAD0 &&
-                evt.getKeyCode() <= KeyEvent.VK_NUMPAD9)
-              )
-              && Character.isDigit(evt.getKeyChar()))
-            alignPanel.seqPanel.numberPressed(evt.getKeyChar());
-
-          switch (evt.getKeyCode())
-          {
-
-            case 27: // escape key
-              deselectAllSequenceMenuItem_actionPerformed(null);
 
-              break;
+        case 27: // escape key
+          deselectAllSequenceMenuItem_actionPerformed(null);
 
-            case KeyEvent.VK_DOWN:
-             if (evt.isAltDown() || !viewport.cursorMode)
-              moveSelectedSequences(false);
-              if (viewport.cursorMode)
-                alignPanel.seqPanel.moveCursor(0, 1);
-              break;
+          break;
 
-            case KeyEvent.VK_UP:
-            if (evt.isAltDown() || !viewport.cursorMode)
-              moveSelectedSequences(true);
-              if (viewport.cursorMode)
-                alignPanel.seqPanel.moveCursor(0, -1);
+        case KeyEvent.VK_DOWN:
+          if (evt.isAltDown() || !viewport.cursorMode)
+            moveSelectedSequences(false);
+          if (viewport.cursorMode)
+            alignPanel.seqPanel.moveCursor(0, 1);
+          break;
 
-              break;
-
-            case KeyEvent.VK_LEFT:
-            if (evt.isAltDown() || !viewport.cursorMode)
-              slideSequences(false,
-                             alignPanel.seqPanel.getKeyboardNo1());
-            else
-                alignPanel.seqPanel.moveCursor( -1, 0);
+        case KeyEvent.VK_UP:
+          if (evt.isAltDown() || !viewport.cursorMode)
+            moveSelectedSequences(true);
+          if (viewport.cursorMode)
+            alignPanel.seqPanel.moveCursor(0, -1);
 
+          break;
 
-              break;
+        case KeyEvent.VK_LEFT:
+          if (evt.isAltDown() || !viewport.cursorMode)
+            slideSequences(false, alignPanel.seqPanel.getKeyboardNo1());
+          else
+            alignPanel.seqPanel.moveCursor(-1, 0);
 
-            case KeyEvent.VK_RIGHT:
-            if (evt.isAltDown() || !viewport.cursorMode)
-              slideSequences(true,
-                             alignPanel.seqPanel.getKeyboardNo1());
-            else
-                alignPanel.seqPanel.moveCursor(1, 0);
-              break;
+          break;
 
-            case KeyEvent.VK_SPACE:
-              if (viewport.cursorMode)
-              {
-                alignPanel.seqPanel.insertGapAtCursor(evt.isControlDown()
-                                           || evt.isShiftDown()
-                                           || evt.isAltDown());
-              }
-              break;
+        case KeyEvent.VK_RIGHT:
+          if (evt.isAltDown() || !viewport.cursorMode)
+            slideSequences(true, alignPanel.seqPanel.getKeyboardNo1());
+          else
+            alignPanel.seqPanel.moveCursor(1, 0);
+          break;
 
-            case KeyEvent.VK_DELETE:
-            case KeyEvent.VK_BACK_SPACE:
-              if (!viewport.cursorMode)
-              {
-                cut_actionPerformed(null);
-              }
-              else
+        case KeyEvent.VK_SPACE:
+          if (viewport.cursorMode)
+          {
+            alignPanel.seqPanel.insertGapAtCursor(evt.isControlDown()
+                    || evt.isShiftDown() || evt.isAltDown());
+          }
+          break;
+          
+        //case KeyEvent.VK_A:
+       //      if (viewport.cursorMode)
+       //     {
+       //              alignPanel.seqPanel.insertNucAtCursor(false,"A");
+       //              //System.out.println("A");
+       //     }
+       //      break;
+        /*     
+        case KeyEvent.VK_CLOSE_BRACKET:
+               if (viewport.cursorMode)
             {
-                alignPanel.seqPanel.deleteGapAtCursor(evt.isControlDown()
-                                           || evt.isShiftDown()
-                                           || evt.isAltDown());
+                       System.out.println("closing bracket");
             }
+               break;
+         */
+        case KeyEvent.VK_DELETE:
+        case KeyEvent.VK_BACK_SPACE:
+          if (!viewport.cursorMode)
+          {
+            cut_actionPerformed(null);
+          }
+          else
+          {
+            alignPanel.seqPanel.deleteGapAtCursor(evt.isControlDown()
+                    || evt.isShiftDown() || evt.isAltDown());
+          }
 
-              break;
-
-            case KeyEvent.VK_S:
-              if (viewport.cursorMode)
-              {
-                alignPanel.seqPanel.setCursorRow();
-              }
-              break;
-            case KeyEvent.VK_C:
-              if (viewport.cursorMode && !evt.isControlDown())
-              {
-                alignPanel.seqPanel.setCursorColumn();
-              }
-              break;
-            case KeyEvent.VK_P:
-              if (viewport.cursorMode)
-              {
-                alignPanel.seqPanel.setCursorPosition();
-              }
-              break;
-
-            case KeyEvent.VK_ENTER:
-            case KeyEvent.VK_COMMA:
-              if (viewport.cursorMode)
-              {
-                alignPanel.seqPanel.setCursorRowAndColumn();
-              }
-              break;
-
-            case KeyEvent.VK_Q:
-              if (viewport.cursorMode)
-              {
-                alignPanel.seqPanel.setSelectionAreaAtCursor(true);
-              }
-              break;
-            case KeyEvent.VK_M:
-              if (viewport.cursorMode)
-              {
-                alignPanel.seqPanel.setSelectionAreaAtCursor(false);
-              }
-              break;
-
-            case KeyEvent.VK_F2:
-              viewport.cursorMode = !viewport.cursorMode;
-              statusBar.setText("Keyboard editing mode is " +
-                                           (viewport.cursorMode ? "on" : "off"));
-              if (viewport.cursorMode)
-              {
-                alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;
-                alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;
-              }
-              alignPanel.seqPanel.seqCanvas.repaint();
-              break;
+          break;
 
-            case KeyEvent.VK_F1:
-              try
-              {
-                ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
-                java.net.URL url = javax.help.HelpSet.findHelpSet(cl, "help/help");
-                javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
+        case KeyEvent.VK_S:
+          if (viewport.cursorMode)
+          {
+            alignPanel.seqPanel.setCursorRow();
+          }
+          break;
+        case KeyEvent.VK_C:
+          if (viewport.cursorMode && !evt.isControlDown())
+          {
+            alignPanel.seqPanel.setCursorColumn();
+          }
+          break;
+        case KeyEvent.VK_P:
+          if (viewport.cursorMode)
+          {
+            alignPanel.seqPanel.setCursorPosition();
+          }
+          break;
 
-                javax.help.HelpBroker hb = hs.createHelpBroker();
-                hb.setCurrentID("home");
-                hb.setDisplayed(true);
-              }
-              catch (Exception ex)
-              {
-                ex.printStackTrace();
-              }
-              break
-                  ;
-            case KeyEvent.VK_H:
-            {
-              boolean toggleSeqs = !evt.isControlDown();
-              boolean toggleCols = !evt.isShiftDown();
+        case KeyEvent.VK_ENTER:
+        case KeyEvent.VK_COMMA:
+          if (viewport.cursorMode)
+          {
+            alignPanel.seqPanel.setCursorRowAndColumn();
+          }
+          break;
 
-              boolean hide = false;
+        case KeyEvent.VK_Q:
+          if (viewport.cursorMode)
+          {
+            alignPanel.seqPanel.setSelectionAreaAtCursor(true);
+          }
+          break;
+        case KeyEvent.VK_M:
+          if (viewport.cursorMode)
+          {
+            alignPanel.seqPanel.setSelectionAreaAtCursor(false);
+          }
+          break;
 
-              SequenceGroup sg = viewport.getSelectionGroup();
-              if (toggleSeqs)
-              {
-                if (sg != null && sg.getSize() != viewport.alignment.getHeight())
-                {
-                  hideSelSequences_actionPerformed(null);
-                  hide = true;
-                }
-              else if (! (toggleCols &&
-                          viewport.colSel.getSelected().size() > 0))
-              {
-                  showAllSeqs_actionPerformed(null);
-              }
-            }
+        case KeyEvent.VK_F2:
+          viewport.cursorMode = !viewport.cursorMode;
+          statusBar.setText("Keyboard editing mode is "
+                  + (viewport.cursorMode ? "on" : "off"));
+          if (viewport.cursorMode)
+          {
+            alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;
+            alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;
+          }
+          alignPanel.seqPanel.seqCanvas.repaint();
+          break;
 
-              if (toggleCols)
-              {
-                if (viewport.colSel.getSelected().size() > 0)
-                {
-                  hideSelColumns_actionPerformed(null);
-                  if (!toggleSeqs)
-                {
-                    viewport.selectionGroup = sg;
-                }
-              }
-                else if (!hide)
-              {
-                  showAllColumns_actionPerformed(null);
-              }
-            }
-              break;
-            }
-            case KeyEvent.VK_PAGE_UP:
-              if (viewport.wrapAlignment)
-            {
-                alignPanel.scrollUp(true);
-            }
-              else
-            {
-                alignPanel.setScrollValues(viewport.startRes,
-                                           viewport.startSeq
-                                           - viewport.endSeq + viewport.startSeq);
-            }
-              break;
-            case KeyEvent.VK_PAGE_DOWN:
-              if (viewport.wrapAlignment)
-            {
-                alignPanel.scrollUp(false);
-            }
-              else
-            {
-                alignPanel.setScrollValues(viewport.startRes,
-                                           viewport.startSeq
-                                           + viewport.endSeq - viewport.startSeq);
-            }
-              break;
+        case KeyEvent.VK_F1:
+          try
+          {
+            ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
+            java.net.URL url = javax.help.HelpSet.findHelpSet(cl,
+                    "help/help");
+            javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
+
+            javax.help.HelpBroker hb = hs.createHelpBroker();
+            hb.setCurrentID("home");
+            hb.setDisplayed(true);
+          } catch (Exception ex)
+          {
+            ex.printStackTrace();
+          }
+          break;
+        case KeyEvent.VK_H:
+        {
+          boolean toggleSeqs = !evt.isControlDown();
+          boolean toggleCols = !evt.isShiftDown();
+          toggleHiddenRegions(toggleSeqs, toggleCols);
+          break;
+        }
+        case KeyEvent.VK_PAGE_UP:
+          if (viewport.wrapAlignment)
+          {
+            alignPanel.scrollUp(true);
+          }
+          else
+          {
+            alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
+                    - viewport.endSeq + viewport.startSeq);
+          }
+          break;
+        case KeyEvent.VK_PAGE_DOWN:
+          if (viewport.wrapAlignment)
+          {
+            alignPanel.scrollUp(false);
+          }
+          else
+          {
+            alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
+                    + viewport.endSeq - viewport.startSeq);
           }
+          break;
         }
+      }
 
       public void keyReleased(KeyEvent evt)
       {
-        switch(evt.getKeyCode())
+        switch (evt.getKeyCode())
         {
-          case KeyEvent.VK_LEFT:
-            if (evt.isAltDown() || !viewport.cursorMode)
-              viewport.firePropertyChange("alignment", null,
-                                          viewport.getAlignment().getSequences());
-            break;
-
-          case KeyEvent.VK_RIGHT:
-            if (evt.isAltDown() || !viewport.cursorMode)
-              viewport.firePropertyChange("alignment", null,
-                                          viewport.getAlignment().getSequences());
-            break;
+        case KeyEvent.VK_LEFT:
+          if (evt.isAltDown() || !viewport.cursorMode)
+            viewport.firePropertyChange("alignment", null, viewport
+                    .getAlignment().getSequences());
+          break;
+
+        case KeyEvent.VK_RIGHT:
+          if (evt.isAltDown() || !viewport.cursorMode)
+            viewport.firePropertyChange("alignment", null, viewport
+                    .getAlignment().getSequences());
+          break;
         }
       }
-      });
+    });
   }
 
-
-  public void addAlignmentPanel(final AlignmentPanel ap,
-                                boolean newPanel)
+  public void addAlignmentPanel(final AlignmentPanel ap, boolean newPanel)
   {
     ap.alignFrame = this;
 
@@ -412,9 +562,9 @@ public class AlignFrame
 
     int aSize = alignPanels.size();
 
-    tabbedPane.setVisible(aSize>1 || ap.av.viewName!=null);
+    tabbedPane.setVisible(aSize > 1 || ap.av.viewName != null);
 
-    if (aSize == 1 && ap.av.viewName==null)
+    if (aSize == 1 && ap.av.viewName == null)
     {
       this.getContentPane().add(ap, BorderLayout.CENTER);
     }
@@ -432,7 +582,7 @@ public class AlignFrame
       ap.setVisible(false);
     }
 
-    if(newPanel)
+    if (newPanel)
     {
       if (ap.av.padGaps)
       {
@@ -440,6 +590,7 @@ public class AlignFrame
       }
       ap.av.updateConservation(ap);
       ap.av.updateConsensus(ap);
+      ap.av.updateStrucConsensus(ap);
     }
   }
 
@@ -449,11 +600,10 @@ public class AlignFrame
     gatherViews.setEnabled(true);
     tabbedPane.setVisible(true);
     AlignmentPanel first = (AlignmentPanel) alignPanels.firstElement();
-    tabbedPane.addTab(first.av.viewName,first);
+    tabbedPane.addTab(first.av.viewName, first);
     this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
   }
 
-
   public AlignViewport getViewport()
   {
     return viewport;
@@ -463,56 +613,81 @@ public class AlignFrame
   private void addServiceListeners()
   {
     final java.beans.PropertyChangeListener thisListener;
-    // Do this once to get current state
-    BuildWebServiceMenu();
-    Desktop.discoverer.addPropertyChangeListener(
-        thisListener = new java.beans.PropertyChangeListener()
-    {
-      public void propertyChange(PropertyChangeEvent evt)
-      {
-        // System.out.println("Discoverer property change.");
-        if (evt.getPropertyName().equals("services"))
-        {
-          // System.out.println("Rebuilding web service menu");
-          BuildWebServiceMenu();
-        }
-      }
-    });
+    Desktop.instance.addJalviewPropertyChangeListener("services",
+            thisListener = new java.beans.PropertyChangeListener()
+            {
+              public void propertyChange(PropertyChangeEvent evt)
+              {
+                // // System.out.println("Discoverer property change.");
+                // if (evt.getPropertyName().equals("services"))
+                {
+                  SwingUtilities.invokeLater(new Runnable()
+                  {
 
-    addInternalFrameListener(new javax.swing.event.
-                             InternalFrameAdapter()
+                    public void run()
+                    {
+                      System.err
+                              .println("Rebuild WS Menu for service change");
+                      BuildWebServiceMenu();
+                    }
+
+                  });
+                }
+              }
+            });
+    addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
     {
       public void internalFrameClosed(
-          javax.swing.event.InternalFrameEvent evt)
+              javax.swing.event.InternalFrameEvent evt)
       {
-        // System.out.println("deregistering discoverer listener");
-        Desktop.discoverer.removePropertyChangeListener(thisListener);
+        System.out.println("deregistering discoverer listener");
+        Desktop.instance.removeJalviewPropertyChangeListener("services",
+                thisListener);
         closeMenuItem_actionPerformed(true);
-      }
-      ;
+      };
     });
+    // Finally, build the menu once to get current service state
+    new Thread(new Runnable()
+    {
+      public void run()
+      {
+        BuildWebServiceMenu();
+      }
+    }).start();
   }
 
   public void setGUINucleotide(boolean nucleotide)
   {
-    showTranslation.setVisible( nucleotide );
-    conservationMenuItem.setEnabled( !nucleotide );
-    modifyConservation.setEnabled(   !nucleotide );
-    
-    //Remember AlignFrame always starts as protein
-    if(!nucleotide)
-    {
-      calculateMenu.remove(calculateMenu.getItemCount()-2);
-    }
-    setShowProductsEnabled();
+    showTranslation.setVisible(nucleotide);
+    conservationMenuItem.setEnabled(!nucleotide);
+    modifyConservation.setEnabled(!nucleotide);
+    showGroupConservation.setEnabled(!nucleotide);
+    rnahelicesColour.setEnabled(nucleotide);
+    purinePyrimidineColour.setEnabled(nucleotide);
+    // Remember AlignFrame always starts as protein
+    //if (!nucleotide)
+   // {
+   //   showTr
+   //   calculateMenu.remove(calculateMenu.getItemCount() - 2);
+   // }
   }
 
-
+  /**
+   * set up menus for the currently viewport. This may be called after any
+   * operation that affects the data in the current view (selection changed,
+   * etc) to update the menus to reflect the new state.
+   */
+  public void setMenusForViewport()
+  {
+    setMenusFromViewport(viewport);
+  }
 
   /**
-   * Need to call this method when tabs are selected for multiple views,
-   * or when loading from Jalview2XML.java
-   * @param av AlignViewport
+   * Need to call this method when tabs are selected for multiple views, or when
+   * loading from Jalview2XML.java
+   * 
+   * @param av
+   *          AlignViewport
    */
   void setMenusFromViewport(AlignViewport av)
   {
@@ -522,6 +697,7 @@ public class AlignFrame
     conservationMenuItem.setSelected(av.getConservationSelected());
     seqLimits.setSelected(av.getShowJVSuffix());
     idRightAlign.setSelected(av.rightAlignIds);
+    centreColumnLabelsMenuItem.setState(av.centreColumnLabels);
     renderGapsMenuItem.setSelected(av.renderGaps);
     wrapMenuItem.setSelected(av.wrapAlignment);
     scaleAbove.setVisible(av.wrapAlignment);
@@ -530,41 +706,63 @@ public class AlignFrame
     annotationPanelMenuItem.setState(av.showAnnotation);
     viewBoxesMenuItem.setSelected(av.showBoxes);
     viewTextMenuItem.setSelected(av.showText);
-
-    setColourSelected(ColourSchemeProperty.
-                      getColourName(av.getGlobalColourScheme()));
+    showNonconservedMenuItem.setSelected(av.showUnconserved);
+    showGroupConsensus.setSelected(av.showGroupConsensus);
+    showGroupConservation.setSelected(av.showGroupConservation);
+    showConsensusHistogram.setSelected(av.showConsensusHistogram);
+    showSequenceLogo.setSelected(av.showSequenceLogo);
+    setColourSelected(ColourSchemeProperty.getColourName(av
+            .getGlobalColourScheme()));
 
     showSeqFeatures.setSelected(av.showSequenceFeatures);
     hiddenMarkers.setState(av.showHiddenMarkers);
     applyToAllGroups.setState(av.colourAppliesToAllGroups);
+    showNpFeatsMenuitem.setSelected(av.isShowNpFeats());
+    showDbRefsMenuitem.setSelected(av.isShowDbRefs());
+    autoCalculate.setSelected(av.autoCalculateConsensus);
+    sortByTree.setSelected(av.sortByTree);
+    listenToViewSelections.setSelected(av.followSelection);
+    rnahelicesColour.setEnabled(av.alignment.hasRNAStructure());
+    rnahelicesColour.setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
+    setShowProductsEnabled();
 
     updateEditMenuBar();
   }
 
+  Hashtable progressBars, progressBarHandlers;
 
-  Hashtable progressBars;
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
+   */
   public void setProgressBar(String message, long id)
   {
-    if(progressBars == null)
+    if (progressBars == null)
     {
       progressBars = new Hashtable();
+      progressBarHandlers = new Hashtable();
     }
 
     JPanel progressPanel;
+    Long lId = new Long(id);
     GridLayout layout = (GridLayout) statusPanel.getLayout();
-    if(progressBars.get( new Long(id) )!=null)
-     {
-       progressPanel = (JPanel)progressBars.get( new Long(id) );
-       statusPanel.remove(progressPanel);
-       progressBars.remove( progressPanel );
-       progressPanel = null;
-       if(message!=null)
+    if (progressBars.get(lId) != null)
+    {
+      progressPanel = (JPanel) progressBars.get(new Long(id));
+      statusPanel.remove(progressPanel);
+      progressBars.remove(lId);
+      progressPanel = null;
+      if (message != null)
       {
-         statusBar.setText(message);
+        statusBar.setText(message);
       }
-
-       layout.setRows(layout.getRows() - 1);
-     }
+      if (progressBarHandlers.contains(lId))
+      {
+        progressBarHandlers.remove(lId);
+      }
+      layout.setRows(layout.getRows() - 1);
+    }
     else
     {
       progressPanel = new JPanel(new BorderLayout(10, 5));
@@ -578,21 +776,62 @@ public class AlignFrame
       layout.setRows(layout.getRows() + 1);
       statusPanel.add(progressPanel);
 
-      progressBars.put(new Long(id), progressPanel);
+      progressBars.put(lId, progressPanel);
     }
-
+    // update GUI
+    // setMenusForViewport();
     validate();
   }
 
+  public void registerHandler(final long id,
+          final IProgressIndicatorHandler handler)
+  {
+    if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
+    {
+      throw new Error(
+              "call setProgressBar before registering the progress bar's handler.");
+    }
+    progressBarHandlers.put(new Long(id), handler);
+    final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
+    if (handler.canCancel())
+    {
+      JButton cancel = new JButton("Cancel");
+      final IProgressIndicator us = this;
+      cancel.addActionListener(new ActionListener()
+      {
 
+        public void actionPerformed(ActionEvent e)
+        {
+          handler.cancelActivity(id);
+          us.setProgressBar(
+                  "Cancelled "
+                          + ((JLabel) progressPanel.getComponent(0))
+                                  .getText(), id);
+        }
+      });
+      progressPanel.add(cancel, BorderLayout.EAST);
+    }
+  }
 
+  /**
+   * 
+   * @return true if any progress bars are still active
+   */
+  public boolean operationInProgress()
+  {
+    if (progressBars != null && progressBars.size() > 0)
+    {
+      return true;
+    }
+    return false;
+  }
 
   /*
-   Added so Castor Mapping file can obtain Jalview Version
-  */
+   * Added so Castor Mapping file can obtain Jalview Version
+   */
   public String getVersion()
   {
-    return  jalview.bin.Cache.getProperty("VERSION");
+    return jalview.bin.Cache.getProperty("VERSION");
   }
 
   public FeatureRenderer getFeatureRenderer()
@@ -600,7 +839,6 @@ public class AlignFrame
     return alignPanel.seqPanel.seqCanvas.getFeatureRenderer();
   }
 
-
   public void fetchSequence_actionPerformed(ActionEvent e)
   {
     new SequenceFetcher(this);
@@ -613,31 +851,32 @@ public class AlignFrame
 
   public void reload_actionPerformed(ActionEvent e)
   {
-    if(fileName!=null)
+    if (fileName != null)
     {
-      if(currentFileFormat.equals("Jalview"))
+      // TODO: work out how to recover feature settings for correct view(s) when
+      // file is reloaded.
+      if (currentFileFormat.equals("Jalview"))
       {
-        JInternalFrame [] frames = Desktop.desktop.getAllFrames();
-        for(int i=0; i<frames.length; i++)
+        JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+        for (int i = 0; i < frames.length; i++)
         {
-          if (frames[i] instanceof AlignFrame
-              && frames[i] != this
-              && ( (AlignFrame) frames[i]).fileName.equals(fileName))
+          if (frames[i] instanceof AlignFrame && frames[i] != this
+                  && ((AlignFrame) frames[i]).fileName.equals(fileName))
           {
             try
             {
               frames[i].setSelected(true);
               Desktop.instance.closeAssociatedWindows();
-          }
-            catch (java.beans.PropertyVetoException ex)
-            {}
+            } catch (java.beans.PropertyVetoException ex)
+            {
+            }
           }
 
         }
         Desktop.instance.closeAssociatedWindows();
 
         FileLoader loader = new FileLoader();
-        String protocol = fileName.startsWith("http:")? "URL":"File";
+        String protocol = fileName.startsWith("http:") ? "URL" : "File";
         loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
       }
       else
@@ -646,17 +885,32 @@ public class AlignFrame
 
         FileLoader loader = new FileLoader();
         String protocol = fileName.startsWith("http:") ? "URL" : "File";
-        AlignFrame newframe =
-            loader.LoadFileWaitTillLoaded(fileName, protocol, currentFileFormat);
+        AlignFrame newframe = loader.LoadFileWaitTillLoaded(fileName,
+                protocol, currentFileFormat);
 
         newframe.setBounds(bounds);
-
+        if (featureSettings != null && featureSettings.isShowing())
+        {
+          final Rectangle fspos = featureSettings.frame.getBounds();
+          // TODO: need a 'show feature settings' function that takes bounds -
+          // need to refactor Desktop.addFrame
+          newframe.featureSettings_actionPerformed(null);
+          final FeatureSettings nfs = newframe.featureSettings;
+          SwingUtilities.invokeLater(new Runnable()
+          {
+            public void run()
+            {
+              nfs.frame.setBounds(fspos);
+            }
+          });
+          this.featureSettings.close();
+          this.featureSettings = null;
+        }
         this.closeMenuItem_actionPerformed(true);
       }
     }
   }
 
-
   public void addFromText_actionPerformed(ActionEvent e)
   {
     Desktop.instance.inputTextboxMenuItem_actionPerformed(viewport);
@@ -667,38 +921,34 @@ public class AlignFrame
     Desktop.instance.inputURLMenuItem_actionPerformed(viewport);
   }
 
-
   public void save_actionPerformed(ActionEvent e)
   {
-    if(fileName==null
-       || currentFileFormat==null
-       || fileName.startsWith("http")
-        )
+    if (fileName == null
+            || (currentFileFormat == null || !jalview.io.FormatAdapter
+                    .isValidIOFormat(currentFileFormat, true))
+            || fileName.startsWith("http"))
     {
       saveAs_actionPerformed(null);
     }
     else
     {
       saveAlignment(fileName, currentFileFormat);
-  }
+    }
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void saveAs_actionPerformed(ActionEvent e)
   {
-    JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
-        getProperty( "LAST_DIRECTORY"),
-        new String[]
-        { "fa, fasta, fastq", "aln", "pfam", "msf", "pir", "blc","amsa","jar" },
-        new String[]
-        { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "Jalview" },
-        currentFileFormat,
-        false);
-
+    JalviewFileChooser chooser = new JalviewFileChooser(
+            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
+            jalview.io.AppletFormatAdapter.WRITABLE_EXTENSIONS,
+            jalview.io.AppletFormatAdapter.WRITABLE_FNAMES,
+            currentFileFormat, false);
 
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle("Save Alignment to file");
@@ -708,25 +958,28 @@ public class AlignFrame
 
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
-        currentFileFormat = chooser.getSelectedFormat();
-        if (currentFileFormat == null)
-        {
-          JOptionPane.showInternalMessageDialog(Desktop.desktop,
-                                                "You must select a file format before saving!",
-                                                "File format not specified",
-                                                JOptionPane.WARNING_MESSAGE);
-          value = chooser.showSaveDialog(this);
-          return;
-        }
+      currentFileFormat = chooser.getSelectedFormat();
+      if (currentFileFormat == null)
+      {
+        JOptionPane.showInternalMessageDialog(Desktop.desktop,
+                "You must select a file format before saving!",
+                "File format not specified", JOptionPane.WARNING_MESSAGE);
+        value = chooser.showSaveDialog(this);
+        return;
+      }
 
-        fileName = chooser.getSelectedFile().getPath();
+      fileName = chooser.getSelectedFile().getPath();
 
       jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT",
-                                    currentFileFormat);
+              currentFileFormat);
 
       jalview.bin.Cache.setProperty("LAST_DIRECTORY", fileName);
-
-      saveAlignment(fileName, currentFileFormat.substring(0,currentFileFormat.indexOf(" ")));
+      if (currentFileFormat.indexOf(" ") > -1)
+      {
+        currentFileFormat = currentFileFormat.substring(0,
+                currentFileFormat.indexOf(" "));
+      }
+      saveAlignment(fileName, currentFileFormat);
     }
   }
 
@@ -740,41 +993,50 @@ public class AlignFrame
 
       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);
 
-      statusBar.setText("Successfully saved to file: "
-                          +fileName+" in "
-                          +format +" format.");
+      statusBar.setText("Successfully saved to file: " + fileName + " in "
+              + format + " format.");
 
     }
     else
     {
+      if (!jalview.io.AppletFormatAdapter.isValidFormat(format, true))
+      {
+        warningMessage("Cannot save file " + fileName + " using format "
+                + format, "Alignment output format not supported");
+        saveAs_actionPerformed(null);
+        // JBPNote need to have a raise_gui flag here
+        return false;
+      }
 
       String[] omitHidden = null;
 
       if (viewport.hasHiddenColumns)
       {
-        int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
-            "The Alignment contains hidden columns."
-            + "\nDo you want to save only the visible alignment?",
-            "Save / Omit Hidden Columns",
-            JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
+        int reply = JOptionPane
+                .showInternalConfirmDialog(
+                        Desktop.desktop,
+                        "The Alignment contains hidden columns."
+                                + "\nDo you want to save only the visible alignment?",
+                        "Save / Omit Hidden Columns",
+                        JOptionPane.YES_NO_OPTION,
+                        JOptionPane.QUESTION_MESSAGE);
 
         if (reply == JOptionPane.YES_OPTION)
         {
           omitHidden = viewport.getViewAsString(false);
-      }
+        }
       }
       FormatAdapter f = new FormatAdapter();
-
-      String output = f.formatSequences(
-          format,
-          (Alignment) viewport.alignment, // class cast exceptions will occur in the distant future
-          omitHidden, f.getCacheSuffixDefault(format));
+      String output = f.formatSequences(format,
+              (Alignment) viewport.alignment, // class cast exceptions will
+              // occur in the distant future
+              omitHidden, f.getCacheSuffixDefault(format), viewport.colSel);
 
       if (output == null)
       {
@@ -785,16 +1047,14 @@ public class AlignFrame
         try
         {
           java.io.PrintWriter out = new java.io.PrintWriter(
-              new java.io.FileWriter(file));
+                  new java.io.FileWriter(file));
 
           out.print(output);
           out.close();
           this.setTitle(file);
-          statusBar.setText("Successfully saved to file: "
-                            + fileName + " in "
-                            + format + " format.");
-        }
-        catch (Exception ex)
+          statusBar.setText("Successfully saved to file: " + fileName
+                  + " in " + format + " format.");
+        } catch (Exception ex)
         {
           success = false;
           ex.printStackTrace();
@@ -804,33 +1064,50 @@ public class AlignFrame
 
     if (!success)
     {
-      JOptionPane.showInternalMessageDialog(
-          this, "Couldn't save file: " + fileName,
-          "Error Saving File",
-          JOptionPane.WARNING_MESSAGE);
+      JOptionPane.showInternalMessageDialog(this, "Couldn't save file: "
+              + fileName, "Error Saving File", JOptionPane.WARNING_MESSAGE);
     }
 
     return success;
   }
 
+  private void warningMessage(String warning, String title)
+  {
+    if (new jalview.util.Platform().isHeadless())
+    {
+      System.err.println("Warning: " + title + "\nWarning: " + warning);
+
+    }
+    else
+    {
+      JOptionPane.showInternalMessageDialog(this, warning, title,
+              JOptionPane.WARNING_MESSAGE);
+    }
+    return;
+  }
+
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void outputText_actionPerformed(ActionEvent e)
   {
-    String [] omitHidden = null;
+    String[] omitHidden = null;
 
-    if(viewport.hasHiddenColumns)
+    if (viewport.hasHiddenColumns)
     {
-      int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
-          "The Alignment contains hidden columns."
-      +"\nDo you want to output only the visible alignment?",
-      "Save / Omit Hidden Columns",
-      JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
+      int reply = JOptionPane
+              .showInternalConfirmDialog(
+                      Desktop.desktop,
+                      "The Alignment contains hidden columns."
+                              + "\nDo you want to output only the visible alignment?",
+                      "Save / Omit Hidden Columns",
+                      JOptionPane.YES_NO_OPTION,
+                      JOptionPane.QUESTION_MESSAGE);
 
-      if(reply==JOptionPane.YES_OPTION)
+      if (reply == JOptionPane.YES_OPTION)
       {
         omitHidden = viewport.getViewAsString(false);
       }
@@ -838,27 +1115,32 @@ public class AlignFrame
 
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
     cap.setForInput(null);
-    Desktop.addInternalFrame(cap,
-                             "Alignment output - " + e.getActionCommand(), 600,
-                             500);
 
+    try
+    {
+      cap.setText(new FormatAdapter().formatSequences(e.getActionCommand(),
+              viewport.alignment, omitHidden, viewport.colSel));
+      Desktop.addInternalFrame(cap,
+              "Alignment output - " + e.getActionCommand(), 600, 500);
+    } catch (OutOfMemoryError oom)
+    {
+      new OOMWarning("Outputting alignment as " + e.getActionCommand(), oom);
+      cap.dispose();
+    }
 
-    cap.setText(new FormatAdapter().formatSequences(
-        e.getActionCommand(),
-        viewport.alignment,
-        omitHidden));
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void htmlMenuItem_actionPerformed(ActionEvent e)
   {
     new HTMLOutput(alignPanel,
-                   alignPanel.seqPanel.seqCanvas.getSequenceRenderer(),
-        alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
+            alignPanel.seqPanel.seqCanvas.getSequenceRenderer(),
+            alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
   }
 
   public void createImageMap(File file, String image)
@@ -868,8 +1150,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void createPNG(File f)
   {
@@ -878,15 +1161,15 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void createEPS(File f)
   {
     alignPanel.makeEPS(f);
   }
 
-
   public void pageSetup_actionPerformed(ActionEvent e)
   {
     PrinterJob printJob = PrinterJob.getPrinterJob();
@@ -895,12 +1178,13 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void printMenuItem_actionPerformed(ActionEvent e)
   {
-    //Putting in a thread avoids Swing painting problems
+    // Putting in a thread avoids Swing painting problems
     PrintThread thread = new PrintThread(alignPanel);
     thread.start();
   }
@@ -910,24 +1194,21 @@ public class AlignFrame
     new AnnotationExporter().exportFeatures(alignPanel);
   }
 
-
   public void exportAnnotations_actionPerformed(ActionEvent e)
   {
     new AnnotationExporter().exportAnnotations(
-      alignPanel,
-        viewport.showAnnotation ? viewport.alignment.getAlignmentAnnotation() : null,
-      viewport.alignment.getGroups(),
-      ((Alignment)viewport.alignment).alignmentProperties
-        );
+            alignPanel,
+            viewport.showAnnotation ? viewport.alignment
+                    .getAlignmentAnnotation() : null, viewport.alignment
+                    .getGroups(),
+            ((Alignment) viewport.alignment).alignmentProperties);
   }
 
-
   public void associatedData_actionPerformed(ActionEvent e)
   {
     // Pick the tree file
-    JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
-        getProperty(
-            "LAST_DIRECTORY"));
+    JalviewFileChooser chooser = new JalviewFileChooser(
+            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle("Load Jalview Annotations or Features File");
     chooser.setToolTipText("Load Jalview Annotations / Features file");
@@ -938,76 +1219,86 @@ public class AlignFrame
     {
       String choice = chooser.getSelectedFile().getPath();
       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
-      loadJalviewDataFile(choice);
+      loadJalviewDataFile(choice, null, null, null);
     }
 
   }
 
-
   /**
-   * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * Close the current view or all views in the alignment frame. If the frame
+   * only contains one view then the alignment will be removed from memory.
+   * 
+   * @param closeAllTabs
    */
   public void closeMenuItem_actionPerformed(boolean closeAllTabs)
   {
-    if(alignPanels!=null && alignPanels.size()<2)
+    if (alignPanels != null && alignPanels.size() < 2)
     {
       closeAllTabs = true;
     }
 
     try
     {
-      if(alignPanels!=null)
+      if (alignPanels != null)
       {
         if (closeAllTabs)
         {
-          for (int i = 0; i < alignPanels.size(); i++)
+          if (this.isClosed())
           {
-            AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
-            jalview.structure.StructureSelectionManager.getStructureSelectionManager()
-                .removeStructureViewerListener(ap.seqPanel, null);
-            PaintRefresher.RemoveComponent(ap.seqPanel.seqCanvas);
-            PaintRefresher.RemoveComponent(ap.idPanel.idCanvas);
-            PaintRefresher.RemoveComponent(ap);
-            ap.av.alignment = null;
+            // really close all the windows - otherwise wait till
+            // setClosed(true) is called
+            for (int i = 0; i < alignPanels.size(); i++)
+            {
+              AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
+              ap.closePanel();
+            }
           }
         }
         else
         {
-          int index = tabbedPane.getSelectedIndex();
-
-          alignPanels.removeElement(alignPanel);
-          PaintRefresher.RemoveComponent(alignPanel.seqPanel.seqCanvas);
-          PaintRefresher.RemoveComponent(alignPanel.idPanel.idCanvas);
-          PaintRefresher.RemoveComponent(alignPanel);
-          viewport.alignment = null;
-          alignPanel = null;
-          viewport = null;
-
-          tabbedPane.removeTabAt(index);
-          tabbedPane.validate();
-
-          if(index==tabbedPane.getTabCount())
-          {
-            index --;
-          }
-
-          this.tabSelectionChanged(index);
+          closeView(alignPanel);
         }
       }
 
       if (closeAllTabs)
       {
         this.setClosed(true);
-    }
-    }
-    catch (Exception ex)
+      }
+    } catch (Exception ex)
     {
       ex.printStackTrace();
     }
   }
 
+  /**
+   * close alignPanel2 and shuffle tabs appropriately.
+   * 
+   * @param alignPanel2
+   */
+  public void closeView(AlignmentPanel alignPanel2)
+  {
+    int index = tabbedPane.getSelectedIndex();
+    int closedindex = tabbedPane.indexOfComponent(alignPanel2);
+    alignPanels.removeElement(alignPanel2);
+    // Unnecessary
+    // if (viewport == alignPanel2.av)
+    // {
+    // viewport = null;
+    // }
+    alignPanel2.closePanel();
+    alignPanel2 = null;
+
+    tabbedPane.removeTabAt(closedindex);
+    tabbedPane.validate();
+
+    if (index > closedindex || index == tabbedPane.getTabCount())
+    {
+      // modify currently selected tab index if necessary.
+      index--;
+    }
+
+    this.tabSelectionChanged(index);
+  }
 
   /**
    * DOCUMENT ME!
@@ -1041,73 +1332,79 @@ public class AlignFrame
     }
   }
 
-
   public void addHistoryItem(CommandI command)
   {
-    if(command.getSize()>0)
+    if (command.getSize() > 0)
     {
       viewport.historyList.push(command);
       viewport.redoList.clear();
       updateEditMenuBar();
-      viewport.hasHiddenColumns = viewport.colSel.getHiddenColumns() != null;
+      viewport.hasHiddenColumns = (viewport.colSel != null
+              && viewport.colSel.getHiddenColumns() != null && viewport.colSel
+              .getHiddenColumns().size() > 0);
     }
   }
 
   /**
-   *
+   * 
    * @return alignment objects for all views
    */
   AlignmentI[] getViewAlignments()
   {
-    if (alignPanels!=null)
+    if (alignPanels != null)
     {
       Enumeration e = alignPanels.elements();
       AlignmentI[] als = new AlignmentI[alignPanels.size()];
-      for (int i=0; e.hasMoreElements(); i++)
+      for (int i = 0; e.hasMoreElements(); i++)
       {
         als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
       }
       return als;
     }
-    if (viewport!=null)
+    if (viewport != null)
     {
-      return new AlignmentI[] { viewport.alignment };
+      return new AlignmentI[]
+      { viewport.alignment };
     }
     return null;
   }
+
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void undoMenuItem_actionPerformed(ActionEvent e)
   {
     if (viewport.historyList.empty())
       return;
-    CommandI command = (CommandI)viewport.historyList.pop();
+    CommandI command = (CommandI) viewport.historyList.pop();
     viewport.redoList.push(command);
     command.undoCommand(getViewAlignments());
 
     AlignViewport originalSource = getOriginatingSource(command);
     updateEditMenuBar();
 
-    if(originalSource!=null)
+    if (originalSource != null)
     {
-      originalSource.hasHiddenColumns = viewport.colSel.getHiddenColumns() != null;
-      originalSource.firePropertyChange("alignment",
-                                        null,
-                                        originalSource.alignment.getSequences());
+      originalSource.hasHiddenColumns = (viewport.colSel != null
+              && viewport.colSel.getHiddenColumns() != null && viewport.colSel
+              .getHiddenColumns().size() > 0);
+      originalSource.firePropertyChange("alignment", null,
+              originalSource.alignment.getSequences());
     }
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void redoMenuItem_actionPerformed(ActionEvent e)
   {
-    if(viewport.redoList.size()<1)
+    if (viewport.redoList.size() < 1)
     {
       return;
     }
@@ -1119,36 +1416,37 @@ public class AlignFrame
     AlignViewport originalSource = getOriginatingSource(command);
     updateEditMenuBar();
 
-    if(originalSource!=null)
+    if (originalSource != null)
     {
-      originalSource.hasHiddenColumns = viewport.colSel.getHiddenColumns() != null;
-      originalSource.firePropertyChange("alignment",
-                                        null,
-                                        originalSource.alignment.getSequences());
+      originalSource.hasHiddenColumns = (viewport.colSel != null
+              && viewport.colSel.getHiddenColumns() != null && viewport.colSel
+              .getHiddenColumns().size() > 0);
+      originalSource.firePropertyChange("alignment", null,
+              originalSource.alignment.getSequences());
     }
   }
 
   AlignViewport getOriginatingSource(CommandI command)
   {
     AlignViewport originalSource = null;
-    //For sequence removal and addition, we need to fire
-   //the property change event FROM the viewport where the
-   //original alignment was altered
-    AlignmentI al=null;
+    // For sequence removal and addition, we need to fire
+    // the property change event FROM the viewport where the
+    // original alignment was altered
+    AlignmentI al = null;
     if (command instanceof EditCommand)
     {
       EditCommand editCommand = (EditCommand) command;
       al = editCommand.getAlignment();
-      Vector comps = (Vector) PaintRefresher.components
-          .get(viewport.getSequenceSetId());
+      Vector comps = (Vector) PaintRefresher.components.get(viewport
+              .getSequenceSetId());
 
       for (int i = 0; i < comps.size(); i++)
       {
         if (comps.elementAt(i) instanceof AlignmentPanel)
         {
-          if (al == ( (AlignmentPanel) comps.elementAt(i)).av.alignment)
+          if (al == ((AlignmentPanel) comps.elementAt(i)).av.alignment)
           {
-            originalSource = ( (AlignmentPanel) comps.elementAt(i)).av;
+            originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
             break;
           }
         }
@@ -1157,8 +1455,8 @@ public class AlignFrame
 
     if (originalSource == null)
     {
-      //The original view is closed, we must validate
-      //the current view against the closed view first
+      // The original view is closed, we must validate
+      // the current view against the closed view first
       if (al != null)
       {
         PaintRefresher.validateSequences(al, viewport.alignment);
@@ -1172,8 +1470,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param up DOCUMENT ME!
+   * 
+   * @param up
+   *          DOCUMENT ME!
    */
   public void moveSelectedSequences(boolean up)
   {
@@ -1232,25 +1531,23 @@ public class AlignFrame
     alignPanel.paintAlignment(true);
   }
 
-
-
-
   synchronized void slideSequences(boolean right, int size)
   {
     Vector sg = new Vector();
-    if(viewport.cursorMode)
+    if (viewport.cursorMode)
+    {
+      sg.addElement(viewport.alignment
+              .getSequenceAt(alignPanel.seqPanel.seqCanvas.cursorY));
+    }
+    else if (viewport.getSelectionGroup() != null
+            && viewport.getSelectionGroup().getSize() != viewport.alignment
+                    .getHeight())
     {
-      sg.addElement(viewport.alignment.getSequenceAt(
-          alignPanel.seqPanel.seqCanvas.cursorY));
+      sg = viewport.getSelectionGroup().getSequences(
+              viewport.hiddenRepSequences);
     }
-    else if(viewport.getSelectionGroup()!=null
-        && viewport.getSelectionGroup().getSize()!=viewport.alignment.getHeight())
-   {
-     sg = viewport.getSelectionGroup().getSequences(
-         viewport.hiddenRepSequences);
-   }
 
-    if(sg.size()<1)
+    if (sg.size() < 1)
     {
       return;
     }
@@ -1259,8 +1556,8 @@ public class AlignFrame
 
     for (int i = 0; i < viewport.alignment.getHeight(); i++)
     {
-      if(!sg.contains(viewport.alignment.getSequenceAt(i)))
-         invertGroup.add(viewport.alignment.getSequenceAt(i));
+      if (!sg.contains(viewport.alignment.getSequenceAt(i)))
+        invertGroup.add(viewport.alignment.getSequenceAt(i));
     }
 
     SequenceI[] seqs1 = new SequenceI[sg.size()];
@@ -1273,15 +1570,11 @@ public class AlignFrame
 
     SlideSequencesCommand ssc;
     if (right)
-      ssc = new SlideSequencesCommand("Slide Sequences",
-                                      seqs2, seqs1, size,
-                                      viewport.getGapCharacter()
-          );
+      ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
+              size, viewport.getGapCharacter());
     else
-      ssc = new SlideSequencesCommand("Slide Sequences",
-                                      seqs1, seqs2, size,
-                                      viewport.getGapCharacter()
-          );
+      ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
+              size, viewport.getGapCharacter());
 
     int groupAdjustment = 0;
     if (ssc.getGapsInsertedBegin() && right)
@@ -1294,7 +1587,7 @@ public class AlignFrame
     else if (!ssc.getGapsInsertedBegin() && !right)
     {
       if (viewport.cursorMode)
-        alignPanel.seqPanel.moveCursor( -size, 0);
+        alignPanel.seqPanel.moveCursor(-size, 0);
       else
         groupAdjustment = -size;
     }
@@ -1302,33 +1595,31 @@ public class AlignFrame
     if (groupAdjustment != 0)
     {
       viewport.getSelectionGroup().setStartRes(
-          viewport.getSelectionGroup().getStartRes() + groupAdjustment);
+              viewport.getSelectionGroup().getStartRes() + groupAdjustment);
       viewport.getSelectionGroup().setEndRes(
-          viewport.getSelectionGroup().getEndRes() + groupAdjustment);
+              viewport.getSelectionGroup().getEndRes() + groupAdjustment);
     }
 
-
     boolean appendHistoryItem = false;
-    if(viewport.historyList!=null
-       && viewport.historyList.size()>0
-      && viewport.historyList.peek() instanceof SlideSequencesCommand)
+    if (viewport.historyList != null && viewport.historyList.size() > 0
+            && viewport.historyList.peek() instanceof SlideSequencesCommand)
     {
-      appendHistoryItem = ssc.appendSlideCommand(
-          (SlideSequencesCommand)viewport.historyList.peek())
-          ;
+      appendHistoryItem = ssc
+              .appendSlideCommand((SlideSequencesCommand) viewport.historyList
+                      .peek());
     }
 
-    if(!appendHistoryItem)
+    if (!appendHistoryItem)
       addHistoryItem(ssc);
 
     repaint();
   }
 
-
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void copy_actionPerformed(ActionEvent e)
   {
@@ -1337,8 +1628,9 @@ public class AlignFrame
     {
       return;
     }
-    //  TODO: preserve the ordering of displayed alignment annotation in any internal paste (particularly sequence associated annotation)
-    SequenceI [] seqs = viewport.getSelectionAsNewSequence();
+    // TODO: preserve the ordering of displayed alignment annotation in any
+    // internal paste (particularly sequence associated annotation)
+    SequenceI[] seqs = viewport.getSelectionAsNewSequence();
     String[] omitHidden = null;
 
     if (viewport.hasHiddenColumns)
@@ -1346,72 +1638,56 @@ public class AlignFrame
       omitHidden = viewport.getViewAsString(true);
     }
 
-    String output = new FormatAdapter().formatSequences(
-        "Fasta",
-        seqs,
-        omitHidden);
+    String output = new FormatAdapter().formatSequences("Fasta", seqs,
+            omitHidden);
 
     StringSelection ss = new StringSelection(output);
 
     try
     {
       jalview.gui.Desktop.internalCopy = true;
-      //Its really worth setting the clipboard contents
-      //to empty before setting the large StringSelection!!
+      // Its really worth setting the clipboard contents
+      // to empty before setting the large StringSelection!!
       Toolkit.getDefaultToolkit().getSystemClipboard()
-          .setContents(new StringSelection(""), null);
+              .setContents(new StringSelection(""), null);
 
       Toolkit.getDefaultToolkit().getSystemClipboard()
-          .setContents(ss, Desktop.instance);
-    }
-    catch (OutOfMemoryError er)
+              .setContents(ss, Desktop.instance);
+    } catch (OutOfMemoryError er)
     {
-      er.printStackTrace();
-      javax.swing.SwingUtilities.invokeLater(new Runnable()
-          {
-            public void run()
-            {
-              javax.swing.JOptionPane.showInternalMessageDialog(Desktop.desktop,
-                  "Out of memory copying region!!"
-                  +
-                  "\nSee help files for increasing Java Virtual Machine memory."
-                  , "Out of memory",
-                  javax.swing.JOptionPane.WARNING_MESSAGE);
-            }
-          });
-
+      new OOMWarning("copying region", er);
       return;
     }
 
     Vector hiddenColumns = null;
-    if(viewport.hasHiddenColumns)
+    if (viewport.hasHiddenColumns)
     {
-      hiddenColumns =new Vector();
-      int hiddenOffset = viewport.getSelectionGroup().getStartRes();
-      for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns().size();
-           i++)
+      hiddenColumns = new Vector();
+      int hiddenOffset = viewport.getSelectionGroup().getStartRes(), hiddenCutoff = viewport
+              .getSelectionGroup().getEndRes();
+      for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns()
+              .size(); i++)
       {
-        int[] region = (int[])
-            viewport.getColumnSelection().getHiddenColumns().elementAt(i);
-
-        hiddenColumns.addElement(new int[]
-                                 {region[0] - hiddenOffset,
-                          region[1]-hiddenOffset});
+        int[] region = (int[]) viewport.getColumnSelection()
+                .getHiddenColumns().elementAt(i);
+        if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
+        {
+          hiddenColumns.addElement(new int[]
+          { region[0] - hiddenOffset, region[1] - hiddenOffset });
+        }
       }
     }
 
     Desktop.jalviewClipboard = new Object[]
-        {
-        seqs,
-        viewport.alignment.getDataset(),
-        hiddenColumns};
-    statusBar.setText("Copied "+seqs.length+" sequences to clipboard.");
+    { seqs, viewport.alignment.getDataset(), hiddenColumns };
+    statusBar.setText("Copied " + seqs.length + " sequences to clipboard.");
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void pasteNew_actionPerformed(ActionEvent e)
   {
@@ -1420,8 +1696,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void pasteThis_actionPerformed(ActionEvent e)
   {
@@ -1429,13 +1706,14 @@ public class AlignFrame
   }
 
   /**
-   * DOCUMENT ME!
-   *
-   * @param newAlignment DOCUMENT ME!
+   * Paste contents of Jalview clipboard
+   * 
+   * @param newAlignment
+   *          true to paste to a new alignment, otherwise add to this.
    */
   void paste(boolean newAlignment)
   {
-    boolean externalPaste=true;
+    boolean externalPaste = true;
     try
     {
       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
@@ -1457,23 +1735,9 @@ public class AlignFrame
 
         format = new IdentifyFile().Identify(str, "Paste");
 
-      }
-      catch (OutOfMemoryError er)
+      } catch (OutOfMemoryError er)
       {
-        er.printStackTrace();
-        javax.swing.SwingUtilities.invokeLater(new Runnable()
-        {
-          public void run()
-          {
-            javax.swing.JOptionPane.showInternalMessageDialog(Desktop.desktop,
-                "Out of memory pasting sequences!!"
-                +
-                "\nSee help files for increasing Java Virtual Machine memory."
-                , "Out of memory",
-                javax.swing.JOptionPane.WARNING_MESSAGE);
-          }
-        });
-
+        new OOMWarning("Out of memory pasting sequences!!", er);
         return;
       }
 
@@ -1481,56 +1745,58 @@ public class AlignFrame
       boolean annotationAdded = false;
       AlignmentI alignment = null;
 
-     if(Desktop.jalviewClipboard!=null)
-     {
-       // The clipboard was filled from within Jalview, we must use the sequences
-       // And dataset from the copied alignment
-       SequenceI[] newseq = (SequenceI[])Desktop.jalviewClipboard[0];
-       // be doubly sure that we create *new* sequence objects.
-       sequences = new SequenceI[newseq.length];
-       for (int i=0;i<newseq.length;i++) {
-         sequences[i] = new Sequence(newseq[i]);
-       }
-       alignment = new Alignment(sequences);
-       externalPaste = false;
-     }
-     else
-     {
-       // parse the clipboard as an alignment.
-       alignment = new FormatAdapter().readFile(str, "Paste", format);
-       sequences = alignment.getSequencesArray();
-     }
-
-     int alwidth=0;
-
-     if (newAlignment)
-     {
-
-       if (Desktop.jalviewClipboard != null)
-       {
-         // dataset is inherited
-         alignment.setDataset( (Alignment) Desktop.jalviewClipboard[1]);
-       }
-       else
-       {
-         // new dataset is constructed
-         alignment.setDataset(null);
-       }
-       alwidth = alignment.getWidth()+1;
-     }
-     else
-     {
-       AlignmentI pastedal = alignment; // preserve pasted alignment object
-       // Add pasted sequences and dataset into existing alignment.
-       alignment = viewport.getAlignment();
-       alwidth = alignment.getWidth()+1;
+      if (Desktop.jalviewClipboard != null)
+      {
+        // The clipboard was filled from within Jalview, we must use the
+        // sequences
+        // And dataset from the copied alignment
+        SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
+        // be doubly sure that we create *new* sequence objects.
+        sequences = new SequenceI[newseq.length];
+        for (int i = 0; i < newseq.length; i++)
+        {
+          sequences[i] = new Sequence(newseq[i]);
+        }
+        alignment = new Alignment(sequences);
+        externalPaste = false;
+      }
+      else
+      {
+        // parse the clipboard as an alignment.
+        alignment = new FormatAdapter().readFile(str, "Paste", format);
+        sequences = alignment.getSequencesArray();
+      }
+
+      int alwidth = 0;
+
+      if (newAlignment)
+      {
+
+        if (Desktop.jalviewClipboard != null)
+        {
+          // dataset is inherited
+          alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
+        }
+        else
+        {
+          // new dataset is constructed
+          alignment.setDataset(null);
+        }
+        alwidth = alignment.getWidth() + 1;
+      }
+      else
+      {
+        AlignmentI pastedal = alignment; // preserve pasted alignment object
+        // Add pasted sequences and dataset into existing alignment.
+        alignment = viewport.getAlignment();
+        alwidth = alignment.getWidth() + 1;
         // decide if we need to import sequences from an existing dataset
         boolean importDs = Desktop.jalviewClipboard != null
                 && Desktop.jalviewClipboard[1] != alignment.getDataset();
         // importDs==true instructs us to copy over new dataset sequences from
         // an existing alignment
         Vector newDs = (importDs) ? new Vector() : null; // used to create
-                                                          // minimum dataset set
+        // minimum dataset set
 
         for (int i = 0; i < sequences.length; i++)
         {
@@ -1539,7 +1805,7 @@ public class AlignFrame
             newDs.addElement(null);
           }
           SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
-                                                            // paste
+          // paste
           if (importDs && ds != null)
           {
             if (!newDs.contains(ds))
@@ -1558,7 +1824,8 @@ public class AlignFrame
           {
             // 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()
           }
@@ -1568,138 +1835,150 @@ public class AlignFrame
         {
           newDs.clear(); // tidy up
         }
-        if (pastedal.getAlignmentAnnotation()!=null) {
+        if (pastedal.getAlignmentAnnotation() != null)
+        {
           // Add any annotation attached to alignment.
           AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
-          for (int i=0; i<alann.length; i++)
+          for (int i = 0; i < alann.length; i++)
           {
-            annotationAdded=true;
-            if (alann[i].sequenceRef==null && !alann[i].autoCalculated) {
+            annotationAdded = true;
+            if (alann[i].sequenceRef == null && !alann[i].autoCalculated)
+            {
               AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
               newann.padAnnotation(alwidth);
               alignment.addAnnotation(newann);
             }
           }
         }
-     }
-     if (!newAlignment) {
-       ///////
-       // ADD HISTORY ITEM
-       //
-       addHistoryItem(new EditCommand(
-               "Add sequences",
-               EditCommand.PASTE,
-               sequences,
-               0,
-               alignment.getWidth(),
-               alignment)
-              );
-     }
-     // Add any annotations attached to sequences
-     for (int i = 0; i < sequences.length; i++)
-     {
-       if (sequences[i].getAnnotation() != null)
-       {
-         for (int a = 0; a < sequences[i].getAnnotation().length; a++)
-         {
-           annotationAdded=true;
-           sequences[i].getAnnotation()[a].adjustForAlignment();
-           sequences[i].getAnnotation()[a].padAnnotation(alwidth);
-           alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation was duplicated earlier
-           alignment.setAnnotationIndex(sequences[i].getAnnotation()[a], a);
-         }
-       }
-     }
-     if (!newAlignment) {
-
-       // propagate alignment changed.
-       viewport.setEndSeq(alignment.getHeight());
-       if (annotationAdded)
-       {
-         // Duplicate sequence annotation in all views.
-         AlignmentI[] alview = this.getViewAlignments();
-         for (int i = 0; i < sequences.length; i++)
-         {
-           AlignmentAnnotation sann[] = sequences[i].getAnnotation();
-           if (sann == null)
-             continue;
-           for (int avnum=0;avnum<alview.length; avnum++)
-           {
-             if (alview[avnum]!=alignment)
-             {
-               // duplicate in a view other than the one with input focus
-               int avwidth = alview[avnum].getWidth()+1;
-               // this relies on sann being preserved after we 
-               // modify the sequence's annotation array for each duplication
-               for (int a=0; a<sann.length; a++)
-               {
-                 AlignmentAnnotation newann = new AlignmentAnnotation(sann[a]);
-                 sequences[i].addAlignmentAnnotation(newann);
-                 newann.padAnnotation(avwidth);
-                 alview[avnum].addAnnotation(newann); // annotation was duplicated earlier
-                 alview[avnum].setAnnotationIndex(newann, a);
-               }
-             }
-           }
-         }
-         buildSortByAnnotationScoresMenu();
-       }
-       viewport.firePropertyChange("alignment", null, alignment.getSequences());
-
-     } else {
-       AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-       String newtitle = new String("Copied sequences");
-
-       if(Desktop.jalviewClipboard!=null && Desktop.jalviewClipboard[2]!=null)
-         {
-           Vector hc = (Vector)Desktop.jalviewClipboard[2];
-           for(int i=0; i<hc.size(); i++)
-           {
-             int [] region = (int[]) hc.elementAt(i);
-             af.viewport.hideColumns(region[0], region[1]);
-           }
-         }
-
-
-       //>>>This is a fix for the moment, until a better solution is found!!<<<
-       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().transferSettings(
-           alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
-
-       // TODO: maintain provenance of an alignment, rather than just make the title a concatenation of operations.
-       if (!externalPaste) {
-         if (title.startsWith("Copied sequences"))
-         {
-           newtitle = title;
-         }
-         else
-         {
-           newtitle = newtitle.concat("- from " + title);
-         }
-       } else {
-         newtitle = new String("Pasted sequences");
-       }
-
-       Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
-                                DEFAULT_HEIGHT);
-
-     }
-
-
-    }
-    catch (Exception ex)
+      }
+      if (!newAlignment)
+      {
+        // /////
+        // ADD HISTORY ITEM
+        //
+        addHistoryItem(new EditCommand("Add sequences", EditCommand.PASTE,
+                sequences, 0, alignment.getWidth(), alignment));
+      }
+      // Add any annotations attached to sequences
+      for (int i = 0; i < sequences.length; i++)
+      {
+        if (sequences[i].getAnnotation() != null)
+        {
+          for (int a = 0; a < sequences[i].getAnnotation().length; a++)
+          {
+            annotationAdded = true;
+            sequences[i].getAnnotation()[a].adjustForAlignment();
+            sequences[i].getAnnotation()[a].padAnnotation(alwidth);
+            alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation
+            // was
+            // duplicated
+            // earlier
+            alignment
+                    .setAnnotationIndex(sequences[i].getAnnotation()[a], a);
+          }
+        }
+      }
+      if (!newAlignment)
+      {
+
+        // propagate alignment changed.
+        viewport.setEndSeq(alignment.getHeight());
+        if (annotationAdded)
+        {
+          // Duplicate sequence annotation in all views.
+          AlignmentI[] alview = this.getViewAlignments();
+          for (int i = 0; i < sequences.length; i++)
+          {
+            AlignmentAnnotation sann[] = sequences[i].getAnnotation();
+            if (sann == null)
+              continue;
+            for (int avnum = 0; avnum < alview.length; avnum++)
+            {
+              if (alview[avnum] != alignment)
+              {
+                // duplicate in a view other than the one with input focus
+                int avwidth = alview[avnum].getWidth() + 1;
+                // this relies on sann being preserved after we
+                // modify the sequence's annotation array for each duplication
+                for (int a = 0; a < sann.length; a++)
+                {
+                  AlignmentAnnotation newann = new AlignmentAnnotation(
+                          sann[a]);
+                  sequences[i].addAlignmentAnnotation(newann);
+                  newann.padAnnotation(avwidth);
+                  alview[avnum].addAnnotation(newann); // annotation was
+                  // duplicated earlier
+                  alview[avnum].setAnnotationIndex(newann, a);
+                }
+              }
+            }
+          }
+          buildSortByAnnotationScoresMenu();
+        }
+        viewport.firePropertyChange("alignment", null,
+                alignment.getSequences());
+
+      }
+      else
+      {
+        AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
+                DEFAULT_HEIGHT);
+        String newtitle = new String("Copied sequences");
+
+        if (Desktop.jalviewClipboard != null
+                && Desktop.jalviewClipboard[2] != null)
+        {
+          Vector hc = (Vector) Desktop.jalviewClipboard[2];
+          for (int i = 0; i < hc.size(); i++)
+          {
+            int[] region = (int[]) hc.elementAt(i);
+            af.viewport.hideColumns(region[0], region[1]);
+          }
+        }
+
+        // >>>This is a fix for the moment, until a better solution is
+        // found!!<<<
+        af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
+                .transferSettings(
+                        alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
+
+        // TODO: maintain provenance of an alignment, rather than just make the
+        // title a concatenation of operations.
+        if (!externalPaste)
+        {
+          if (title.startsWith("Copied sequences"))
+          {
+            newtitle = title;
+          }
+          else
+          {
+            newtitle = newtitle.concat("- from " + title);
+          }
+        }
+        else
+        {
+          newtitle = new String("Pasted sequences");
+        }
+
+        Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
+                DEFAULT_HEIGHT);
+
+      }
+
+    } catch (Exception ex)
     {
       ex.printStackTrace();
-        System.out.println("Exception whilst pasting: "+ex);
-        // could be anything being pasted in here
+      System.out.println("Exception whilst pasting: " + ex);
+      // could be anything being pasted in here
     }
 
-
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void cut_actionPerformed(ActionEvent e)
   {
@@ -1709,8 +1988,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void delete_actionPerformed(ActionEvent evt)
   {
@@ -1729,46 +2009,38 @@ public class AlignFrame
       seqs.addElement(seq);
     }
 
-
-   // If the cut affects all sequences, remove highlighted columns
-   if (sg.getSize() == viewport.alignment.getHeight())
-   {
-     viewport.getColumnSelection().removeElements(sg.getStartRes(),
-         sg.getEndRes() + 1);
-   }
-
-
-    SequenceI [] cut = new SequenceI[seqs.size()];
-    for(int i=0; i<seqs.size(); i++)
+    // If the cut affects all sequences, remove highlighted columns
+    if (sg.getSize() == viewport.alignment.getHeight())
     {
-      cut[i] = (SequenceI)seqs.elementAt(i);
+      viewport.getColumnSelection().removeElements(sg.getStartRes(),
+              sg.getEndRes() + 1);
     }
 
+    SequenceI[] cut = new SequenceI[seqs.size()];
+    for (int i = 0; i < seqs.size(); i++)
+    {
+      cut[i] = (SequenceI) seqs.elementAt(i);
+    }
 
     /*
-    //ADD HISTORY ITEM
-    */
-    addHistoryItem(new EditCommand("Cut Sequences",
-                                      EditCommand.CUT,
-                                      cut,
-                                      sg.getStartRes(),
-                                      sg.getEndRes()-sg.getStartRes()+1,
-                                      viewport.alignment));
-
+     * //ADD HISTORY ITEM
+     */
+    addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT, cut,
+            sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
+            viewport.alignment));
 
     viewport.setSelectionGroup(null);
+    viewport.sendSelection();
     viewport.alignment.deleteGroup(sg);
 
-    viewport.firePropertyChange("alignment", null,
-                                  viewport.getAlignment().getSequences());
-
+    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
+            .getSequences());
     if (viewport.getAlignment().getHeight() < 1)
     {
       try
       {
         this.setClosed(true);
-      }
-      catch (Exception ex)
+      } catch (Exception ex)
       {
       }
     }
@@ -1776,8 +2048,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void deleteGroups_actionPerformed(ActionEvent e)
   {
@@ -1785,38 +2058,41 @@ public class AlignFrame
     viewport.sequenceColours = null;
     viewport.setSelectionGroup(null);
     PaintRefresher.Refresh(this, viewport.getSequenceSetId());
+    alignPanel.updateAnnotation();
     alignPanel.paintAlignment(true);
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
   {
     SequenceGroup sg = new SequenceGroup();
 
-    for (int i = 0; i < viewport.getAlignment().getSequences().size();
-         i++)
+    for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
     {
       sg.addSequence(viewport.getAlignment().getSequenceAt(i), false);
     }
 
     sg.setEndRes(viewport.alignment.getWidth() - 1);
     viewport.setSelectionGroup(sg);
+    viewport.sendSelection();
     alignPanel.paintAlignment(true);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
   {
-    if(viewport.cursorMode)
+    if (viewport.cursorMode)
     {
       alignPanel.seqPanel.keyboardNo1 = null;
       alignPanel.seqPanel.keyboardNo2 = null;
@@ -1828,12 +2104,14 @@ public class AlignFrame
     alignPanel.idPanel.idCanvas.searchResults = null;
     alignPanel.paintAlignment(true);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+    viewport.sendSelection();
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
   {
@@ -1846,28 +2124,28 @@ public class AlignFrame
       return;
     }
 
-    for (int i = 0; i < viewport.getAlignment().getSequences().size();
-         i++)
+    for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
     {
       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
     }
 
     alignPanel.paintAlignment(true);
-
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+    viewport.sendSelection();
   }
 
   public void invertColSel_actionPerformed(ActionEvent e)
   {
     viewport.invertColumnSelection();
     alignPanel.paintAlignment(true);
+    viewport.sendSelection();
   }
 
-
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
   {
@@ -1876,12 +2154,13 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
   {
-      trimAlignment(false);
+    trimAlignment(false);
   }
 
   void trimAlignment(boolean trimLeft)
@@ -1891,7 +2170,7 @@ public class AlignFrame
 
     if (colSel.size() > 0)
     {
-      if(trimLeft)
+      if (trimLeft)
       {
         column = colSel.getMin();
       }
@@ -1900,43 +2179,35 @@ public class AlignFrame
         column = colSel.getMax();
       }
 
-      SequenceI [] seqs;
-      if(viewport.getSelectionGroup()!=null)
+      SequenceI[] seqs;
+      if (viewport.getSelectionGroup() != null)
       {
-        seqs = viewport.getSelectionGroup().getSequencesAsArray(viewport.
-            hiddenRepSequences);
+        seqs = viewport.getSelectionGroup().getSequencesAsArray(
+                viewport.hiddenRepSequences);
       }
       else
       {
         seqs = viewport.alignment.getSequencesArray();
       }
 
-
       TrimRegionCommand trimRegion;
-      if(trimLeft)
+      if (trimLeft)
       {
         trimRegion = new TrimRegionCommand("Remove Left",
-                                    TrimRegionCommand.TRIM_LEFT,
-                                    seqs,
-                                    column,
-                                    viewport.alignment,
-                                    viewport.colSel,
-                                    viewport.selectionGroup);
+                TrimRegionCommand.TRIM_LEFT, seqs, column,
+                viewport.alignment, viewport.colSel,
+                viewport.selectionGroup);
         viewport.setStartRes(0);
       }
-     else
-     {
-       trimRegion = new TrimRegionCommand("Remove Right",
-                                   TrimRegionCommand.TRIM_RIGHT,
-                                   seqs,
-                                   column,
-                                   viewport.alignment,
-                                   viewport.colSel,
-                                   viewport.selectionGroup);
-     }
-
-     statusBar.setText("Removed "+trimRegion.getSize()+" columns.");
+      else
+      {
+        trimRegion = new TrimRegionCommand("Remove Right",
+                TrimRegionCommand.TRIM_RIGHT, seqs, column,
+                viewport.alignment, viewport.colSel,
+                viewport.selectionGroup);
+      }
 
+      statusBar.setText("Removed " + trimRegion.getSize() + " columns.");
 
       addHistoryItem(trimRegion);
 
@@ -1946,32 +2217,33 @@ public class AlignFrame
       {
         SequenceGroup sg = (SequenceGroup) groups.get(i);
 
-        if ( (trimLeft && !sg.adjustForRemoveLeft(column))
-            || (!trimLeft && !sg.adjustForRemoveRight(column)))
+        if ((trimLeft && !sg.adjustForRemoveLeft(column))
+                || (!trimLeft && !sg.adjustForRemoveRight(column)))
         {
           viewport.alignment.deleteGroup(sg);
         }
       }
 
-      viewport.firePropertyChange("alignment", null,
-                                  viewport.getAlignment().getSequences());
+      viewport.firePropertyChange("alignment", null, viewport
+              .getAlignment().getSequences());
     }
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
   {
-    int start = 0, end = viewport.alignment.getWidth()-1;
+    int start = 0, end = viewport.alignment.getWidth() - 1;
 
     SequenceI[] seqs;
     if (viewport.getSelectionGroup() != null)
     {
-      seqs = viewport.getSelectionGroup().getSequencesAsArray(viewport.
-          hiddenRepSequences);
+      seqs = viewport.getSelectionGroup().getSequencesAsArray(
+              viewport.hiddenRepSequences);
       start = viewport.getSelectionGroup().getStartRes();
       end = viewport.getSelectionGroup().getEndRes();
     }
@@ -1980,46 +2252,44 @@ public class AlignFrame
       seqs = viewport.alignment.getSequencesArray();
     }
 
-
-    RemoveGapColCommand removeGapCols =
-        new RemoveGapColCommand("Remove Gapped Columns",
-                                seqs,
-                                start, end,
-                                viewport.alignment);
+    RemoveGapColCommand removeGapCols = new RemoveGapColCommand(
+            "Remove Gapped Columns", seqs, start, end, viewport.alignment);
 
     addHistoryItem(removeGapCols);
 
-    statusBar.setText("Removed "+removeGapCols.getSize()+" empty columns.");
+    statusBar.setText("Removed " + removeGapCols.getSize()
+            + " empty columns.");
 
-    //This is to maintain viewport position on first residue
-    //of first sequence
+    // This is to maintain viewport position on first residue
+    // of first sequence
     SequenceI seq = viewport.alignment.getSequenceAt(0);
     int startRes = seq.findPosition(viewport.startRes);
-   // ShiftList shifts;
-   // viewport.getAlignment().removeGaps(shifts=new ShiftList());
-   // edit.alColumnChanges=shifts.getInverse();
-   // if (viewport.hasHiddenColumns)
-   //   viewport.getColumnSelection().compensateForEdits(shifts);
-   viewport.setStartRes(seq.findIndex(startRes)-1);
-    viewport.firePropertyChange("alignment", null,
-                                viewport.getAlignment().getSequences());
+    // ShiftList shifts;
+    // viewport.getAlignment().removeGaps(shifts=new ShiftList());
+    // edit.alColumnChanges=shifts.getInverse();
+    // if (viewport.hasHiddenColumns)
+    // viewport.getColumnSelection().compensateForEdits(shifts);
+    viewport.setStartRes(seq.findIndex(startRes) - 1);
+    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
+            .getSequences());
 
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
   {
-    int start = 0, end = viewport.alignment.getWidth()-1;
+    int start = 0, end = viewport.alignment.getWidth() - 1;
 
     SequenceI[] seqs;
     if (viewport.getSelectionGroup() != null)
     {
-      seqs = viewport.getSelectionGroup().getSequencesAsArray(viewport.
-          hiddenRepSequences);
+      seqs = viewport.getSelectionGroup().getSequencesAsArray(
+              viewport.hiddenRepSequences);
       start = viewport.getSelectionGroup().getStartRes();
       end = viewport.getSelectionGroup().getEndRes();
     }
@@ -2028,41 +2298,49 @@ public class AlignFrame
       seqs = viewport.alignment.getSequencesArray();
     }
 
-    //This is to maintain viewport position on first residue
-    //of first sequence
+    // This is to maintain viewport position on first residue
+    // of first sequence
     SequenceI seq = viewport.alignment.getSequenceAt(0);
     int startRes = seq.findPosition(viewport.startRes);
 
-    addHistoryItem(new RemoveGapsCommand("Remove Gaps",
-                                         seqs,
-                                         start, end,
-                                         viewport.alignment));
+    addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
+            viewport.alignment));
 
-    viewport.setStartRes(seq.findIndex(startRes)-1);
+    viewport.setStartRes(seq.findIndex(startRes) - 1);
 
-    viewport.firePropertyChange("alignment", null,
-                                viewport.getAlignment().getSequences());
+    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
+            .getSequences());
 
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void padGapsMenuitem_actionPerformed(ActionEvent e)
   {
     viewport.padGaps = padGapsMenuitem.isSelected();
+    viewport.firePropertyChange("alignment", null, viewport.getAlignment()
+            .getSequences());
+  }
 
-    viewport.firePropertyChange("alignment",
-                                null,
-                                viewport.getAlignment().getSequences());
+  // else
+  {
+    // if (justifySeqs>0)
+    {
+      // alignment.justify(justifySeqs!=RIGHT_JUSTIFY);
+    }
   }
 
+  // }
+
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void findMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2071,8 +2349,57 @@ public class AlignFrame
 
   public void newView_actionPerformed(ActionEvent e)
   {
-    AlignmentPanel newap =
-        new Jalview2XML().copyAlignPanel(alignPanel, true);
+    newView(true);
+  }
+
+  /**
+   * 
+   * @param copyAnnotation
+   *          if true then duplicate all annnotation, groups and settings
+   * @return new alignment panel, already displayed.
+   */
+  public AlignmentPanel newView(boolean copyAnnotation)
+  {
+    return newView(null, copyAnnotation);
+  }
+
+  /**
+   * 
+   * @param viewTitle
+   *          title of newly created view
+   * @return new alignment panel, already displayed.
+   */
+  public AlignmentPanel newView(String viewTitle)
+  {
+    return newView(viewTitle, true);
+  }
+
+  /**
+   * 
+   * @param viewTitle
+   *          title of newly created view
+   * @param copyAnnotation
+   *          if true then duplicate all annnotation, groups and settings
+   * @return new alignment panel, already displayed.
+   */
+  public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
+  {
+    AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
+            true);
+    if (!copyAnnotation)
+    {
+      // just remove all the current annotation except for the automatic stuff
+      newap.av.alignment.deleteAllGroups();
+      for (AlignmentAnnotation alan : newap.av.alignment
+              .getAlignmentAnnotation())
+      {
+        if (!alan.autoCalculated)
+        {
+          newap.av.alignment.deleteAnnotation(alan);
+        }
+        ;
+      }
+    }
 
     newap.av.gatherViewsHere = false;
 
@@ -2085,42 +2412,54 @@ public class AlignFrame
     newap.av.redoList = viewport.redoList;
 
     int index = Desktop.getViewCount(viewport.getSequenceSetId());
-    String newViewName = "View " +index;
-
-    Vector comps = (Vector) PaintRefresher.components.get(viewport.
-        getSequenceSetId());
+    // make sure the new view has a unique name - this is essential for Jalview
+    // 2 archives
+    boolean addFirstIndex = false;
+    if (viewTitle == null || viewTitle.trim().length() == 0)
+    {
+      viewTitle = "View";
+      addFirstIndex = true;
+    }
+    else
+    {
+      index = 1;// we count from 1 if given a specific name
+    }
+    String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
+    Vector comps = (Vector) PaintRefresher.components.get(viewport
+            .getSequenceSetId());
     Vector existingNames = new Vector();
-    for(int i=0; i<comps.size(); i++)
+    for (int i = 0; i < comps.size(); i++)
     {
-      if(comps.elementAt(i) instanceof AlignmentPanel)
+      if (comps.elementAt(i) instanceof AlignmentPanel)
       {
-        AlignmentPanel ap = (AlignmentPanel)comps.elementAt(i);
-        if(!existingNames.contains(ap.av.viewName))
+        AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i);
+        if (!existingNames.contains(ap.av.viewName))
         {
           existingNames.addElement(ap.av.viewName);
+        }
       }
     }
-    }
 
-    while(existingNames.contains(newViewName))
+    while (existingNames.contains(newViewName))
     {
-      newViewName = "View "+ (++index);
+      newViewName = viewTitle + " " + (++index);
     }
 
     newap.av.viewName = newViewName;
 
-    addAlignmentPanel(newap, false);
+    addAlignmentPanel(newap, true);
 
-    if(alignPanels.size()==2)
+    if (alignPanels.size() == 2)
     {
       viewport.gatherViewsHere = true;
     }
     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
+    return newap;
   }
 
   public void expandViews_actionPerformed(ActionEvent e)
   {
-        Desktop.instance.explodeViews(this);
+    Desktop.instance.explodeViews(this);
   }
 
   public void gatherViews_actionPerformed(ActionEvent e)
@@ -2128,29 +2467,29 @@ public class AlignFrame
     Desktop.instance.gatherViews(this);
   }
 
-
-
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void font_actionPerformed(ActionEvent e)
   {
     new FontChooser(alignPanel);
   }
 
-
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void seqLimit_actionPerformed(ActionEvent e)
   {
     viewport.setShowJVSuffix(seqLimits.isSelected());
 
-    alignPanel.idPanel.idCanvas.setPreferredSize(alignPanel.calculateIdWidth());
+    alignPanel.idPanel.idCanvas.setPreferredSize(alignPanel
+            .calculateIdWidth());
     alignPanel.paintAlignment(true);
   }
 
@@ -2160,12 +2499,31 @@ public class AlignFrame
     alignPanel.paintAlignment(true);
   }
 
+  public void centreColumnLabels_actionPerformed(ActionEvent e)
+  {
+    viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
+    alignPanel.paintAlignment(true);
+  }
 
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
+   */
+  protected void followHighlight_actionPerformed()
+  {
+    if (viewport.followHighlight = this.followHighlightMenuItem.getState())
+    {
+      alignPanel.scrollToPosition(
+              alignPanel.seqPanel.seqCanvas.searchResults, false);
+    }
+  }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2175,8 +2533,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void wrapMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2204,6 +2563,116 @@ public class AlignFrame
     alignPanel.paintAlignment(true);
   }
 
+  /**
+   * called by key handler and the hide all/show all menu items
+   * 
+   * @param toggleSeqs
+   * @param toggleCols
+   */
+  private void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
+  {
+
+    boolean hide = false;
+    SequenceGroup sg = viewport.getSelectionGroup();
+    if (!toggleSeqs && !toggleCols)
+    {
+      // 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.colSel != null && viewport.colSel.getSelected() != null && viewport.colSel
+              .getSelected().size() > 0)
+              || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg
+                      .getEndRes()))
+      {
+        // now invert the sequence set, if required - empty selection implies
+        // that no hiding is required.
+        if (sg != null)
+        {
+          invertSequenceMenuItem_actionPerformed(null);
+          sg = viewport.getSelectionGroup();
+          toggleSeqs = true;
+
+        }
+        viewport.expandColSelection(sg, true);
+        // finally invert the column selection and get the new sequence
+        // selection.
+        invertColSel_actionPerformed(null);
+        toggleCols = true;
+      }
+    }
+
+    if (toggleSeqs)
+    {
+      if (sg != null && sg.getSize() != viewport.alignment.getHeight())
+      {
+        hideSelSequences_actionPerformed(null);
+        hide = true;
+      }
+      else if (!(toggleCols && viewport.colSel.getSelected().size() > 0))
+      {
+        showAllSeqs_actionPerformed(null);
+      }
+    }
+
+    if (toggleCols)
+    {
+      if (viewport.colSel.getSelected().size() > 0)
+      {
+        hideSelColumns_actionPerformed(null);
+        if (!toggleSeqs)
+        {
+          viewport.selectionGroup = sg;
+        }
+      }
+      else if (!hide)
+      {
+        showAllColumns_actionPerformed(null);
+      }
+    }
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
+   * event.ActionEvent)
+   */
+  public void hideAllButSelection_actionPerformed(ActionEvent e)
+  {
+    toggleHiddenRegions(false, false);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
+   * .ActionEvent)
+   */
+  public void hideAllSelection_actionPerformed(ActionEvent e)
+  {
+    SequenceGroup sg = viewport.getSelectionGroup();
+    viewport.expandColSelection(sg, false);
+    viewport.hideAllSelectedSeqs();
+    viewport.hideSelectedColumns();
+    alignPanel.paintAlignment(true);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
+   * ActionEvent)
+   */
+  public void showAllhidden_actionPerformed(ActionEvent e)
+  {
+    viewport.showAllHiddenColumns();
+    viewport.showAllHiddenSeqs();
+    alignPanel.paintAlignment(true);
+  }
+
   public void hideSelColumns_actionPerformed(ActionEvent e)
   {
     viewport.hideSelectedColumns();
@@ -2218,8 +2687,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void scaleAbove_actionPerformed(ActionEvent e)
   {
@@ -2229,8 +2699,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void scaleLeft_actionPerformed(ActionEvent e)
   {
@@ -2240,8 +2711,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void scaleRight_actionPerformed(ActionEvent e)
   {
@@ -2251,8 +2723,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2262,8 +2735,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void viewTextMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2273,8 +2747,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2282,22 +2757,29 @@ public class AlignFrame
     alignPanel.paintAlignment(true);
   }
 
-
   public FeatureSettings featureSettings;
+
   public void featureSettings_actionPerformed(ActionEvent e)
   {
-    if(featureSettings !=null )
+    if (featureSettings != null)
     {
       featureSettings.close();
       featureSettings = null;
     }
+    if (!showSeqFeatures.isSelected())
+    {
+      // make sure features are actually displayed
+      showSeqFeatures.setSelected(true);
+      showSeqFeatures_actionPerformed(null);
+    }
     featureSettings = new FeatureSettings(this);
   }
 
   /**
-   * DOCUMENT ME!
-   *
-   * @param evt DOCUMENT ME!
+   * Set or clear 'Show Sequence Features'
+   * 
+   * @param evt
+   *          DOCUMENT ME!
    */
   public void showSeqFeatures_actionPerformed(ActionEvent evt)
   {
@@ -2310,9 +2792,33 @@ public class AlignFrame
   }
 
   /**
+   * Set or clear 'Show Sequence Features'
+   * 
+   * @param evt
+   *          DOCUMENT ME!
+   */
+  public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
+  {
+    viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
+            .isSelected());
+    if (viewport.getShowSequenceFeaturesHeight())
+    {
+      // ensure we're actually displaying features
+      viewport.setShowSequenceFeatures(true);
+      showSeqFeatures.setSelected(true);
+    }
+    alignPanel.paintAlignment(true);
+    if (alignPanel.getOverviewPanel() != null)
+    {
+      alignPanel.getOverviewPanel().updateOverviewImage();
+    }
+  }
+
+  /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2322,67 +2828,23 @@ public class AlignFrame
 
   public void alignmentProperties()
   {
-    JEditorPane editPane = new JEditorPane("text/html","");
+    JEditorPane editPane = new JEditorPane("text/html", "");
     editPane.setEditable(false);
-    StringBuffer contents = new StringBuffer("<html>");
-
-    float avg  = 0;
-    int min=Integer.MAX_VALUE, max=0;
-    for(int i=0; i<viewport.alignment.getHeight(); i++)
-    {
-      int size = viewport.alignment.getSequenceAt(i).getEnd()
-          -viewport.alignment.getSequenceAt(i).getStart();
-      avg += size;
-      if(size>max)
-        max = size;
-      if(size<min)
-        min = size;
-    }
-    avg = avg/(float)viewport.alignment.getHeight();
-
-    contents.append("<br>Sequences: "+ viewport.alignment.getHeight());
-    contents.append("<br>Minimum Sequence Length: "+min);
-    contents.append("<br>Maximum Sequence Length: "+max);
-    contents.append("<br>Average Length: "+(int)avg);
-
-    if (((Alignment)viewport.alignment).getProperties() != null)
-    {
-      Hashtable props = ((Alignment)viewport.alignment).getProperties();
-      Enumeration en = props.keys();
-      contents.append("<br><br><table border=\"1\">");
-      while(en.hasMoreElements())
-      {
-        String key = en.nextElement().toString();
-        StringBuffer val = new StringBuffer();
-        String vals = props.get(key).toString();
-        int pos=0, npos;
-        do {
-          npos = vals.indexOf("\n",pos);
-          if (npos==-1)
-          {
-            val.append(vals.substring(pos));
-          } else {
-            val.append(vals.substring(pos, npos));
-            val.append("<br>");
-          }
-          pos = npos+1;
-        } while (npos!=-1);
-        contents.append("<tr><td>"+key+"</td><td>"+val+"</td></tr>");
-      }
-      contents.append("</table>");
-    }
-    editPane.setText(contents.toString()+"</html>");
+    StringBuffer contents = new AlignmentProperties(viewport.alignment)
+            .formatAsHtml();
+    editPane.setText("<html>" + contents.toString() + "</html>");
     JInternalFrame frame = new JInternalFrame();
     frame.getContentPane().add(new JScrollPane(editPane));
 
-    Desktop.instance.addInternalFrame(frame,"Alignment Properties: "+getTitle(),500,400);
+    Desktop.instance.addInternalFrame(frame, "Alignment Properties: "
+            + getTitle(), 500, 400);
   }
 
-
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void overviewMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2395,17 +2857,16 @@ public class AlignFrame
     OverviewPanel overview = new OverviewPanel(alignPanel);
     frame.setContentPane(overview);
     Desktop.addInternalFrame(frame, "Overview " + this.getTitle(),
-                             frame.getWidth(), frame.getHeight());
+            frame.getWidth(), frame.getHeight());
     frame.pack();
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
     {
       public void internalFrameClosed(
-          javax.swing.event.InternalFrameEvent evt)
+              javax.swing.event.InternalFrameEvent evt)
       {
         alignPanel.setOverviewPanel(null);
-      }
-      ;
+      };
     });
 
     alignPanel.setOverviewPanel(overview);
@@ -2418,8 +2879,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void noColourmenuItem_actionPerformed(ActionEvent e)
   {
@@ -2428,19 +2890,22 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void clustalColour_actionPerformed(ActionEvent e)
   {
     changeColour(new ClustalxColourScheme(
-        viewport.alignment.getSequences(), viewport.alignment.getWidth()));
+            viewport.alignment.getSequences(),
+            viewport.alignment.getWidth()));
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void zappoColour_actionPerformed(ActionEvent e)
   {
@@ -2449,8 +2914,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void taylorColour_actionPerformed(ActionEvent e)
   {
@@ -2459,8 +2925,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void hydrophobicityColour_actionPerformed(ActionEvent e)
   {
@@ -2469,8 +2936,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void helixColour_actionPerformed(ActionEvent e)
   {
@@ -2479,8 +2947,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void strandColour_actionPerformed(ActionEvent e)
   {
@@ -2489,8 +2958,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void turnColour_actionPerformed(ActionEvent e)
   {
@@ -2499,8 +2969,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void buriedColour_actionPerformed(ActionEvent e)
   {
@@ -2509,24 +2980,40 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void nucleotideColour_actionPerformed(ActionEvent e)
   {
     changeColour(new NucleotideColourScheme());
   }
 
+  public void purinePyrimidineColour_actionPerformed(ActionEvent e)
+  {
+    changeColour(new PurinePyrimidineColourScheme());
+  }
+  /*
+  public void covariationColour_actionPerformed(ActionEvent e)
+  {
+    changeColour(new CovariationColourScheme(viewport.alignment.getAlignmentAnnotation()[0]));
+  }
+  */
   public void annotationColour_actionPerformed(ActionEvent e)
   {
     new AnnotationColourChooser(viewport, alignPanel);
   }
-
+  
+  public void rnahelicesColour_actionPerformed(ActionEvent e)
+  {
+    new RNAHelicesColourChooser(viewport, alignPanel);
+  }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void applyToAllGroups_actionPerformed(ActionEvent e)
   {
@@ -2535,22 +3022,22 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param cs DOCUMENT ME!
+   * 
+   * @param cs
+   *          DOCUMENT ME!
    */
   public void changeColour(ColourSchemeI cs)
   {
     int threshold = 0;
 
-    if(cs!=null)
+    if (cs != null)
     {
       if (viewport.getAbovePIDThreshold())
       {
         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
-                                                   "Background");
+                "Background");
 
-        cs.setThreshold(threshold,
-                        viewport.getIgnoreGapsConsensus());
+        cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
 
         viewport.setGlobalColourScheme(cs);
       }
@@ -2564,17 +3051,16 @@ public class AlignFrame
 
         Alignment al = (Alignment) viewport.alignment;
         Conservation c = new Conservation("All",
-                                          ResidueProperties.propHash, 3,
-                                          al.getSequences(), 0,
-                                          al.getWidth() - 1);
+                ResidueProperties.propHash, 3, al.getSequences(), 0,
+                al.getWidth() - 1);
 
         c.calculate();
         c.verdict(false, viewport.ConsPercGaps);
 
         cs.setConservation(c);
 
-        cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel, cs,
-            "Background"));
+        cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
+                cs, "Background"));
       }
       else
       {
@@ -2603,48 +3089,44 @@ public class AlignFrame
         if (cs instanceof ClustalxColourScheme)
         {
           sg.cs = new ClustalxColourScheme(
-              sg.getSequences(viewport.hiddenRepSequences), sg.getWidth());
+                  sg.getSequences(viewport.hiddenRepSequences),
+                  sg.getWidth());
         }
         else if (cs instanceof UserColourScheme)
         {
-          sg.cs = new UserColourScheme( ( (UserColourScheme) cs).getColours());
+          sg.cs = new UserColourScheme(((UserColourScheme) cs).getColours());
         }
         else
         {
           try
           {
             sg.cs = (ColourSchemeI) cs.getClass().newInstance();
-          }
-          catch (Exception ex)
+          } catch (Exception ex)
           {
           }
         }
 
         if (viewport.getAbovePIDThreshold()
-            || cs instanceof PIDColourScheme
-            || cs instanceof Blosum62ColourScheme)
+                || cs instanceof PIDColourScheme
+                || cs instanceof Blosum62ColourScheme)
         {
-         sg.cs.setThreshold(threshold,
-                viewport.getIgnoreGapsConsensus());
+          sg.cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
 
-         sg.cs.setConsensus(AAFrequency.calculate(
-             sg.getSequences(viewport.hiddenRepSequences), sg.getStartRes(),
-             sg.getEndRes()+1));
-       }
+          sg.cs.setConsensus(AAFrequency.calculate(
+                  sg.getSequences(viewport.hiddenRepSequences),
+                  sg.getStartRes(), sg.getEndRes() + 1));
+        }
         else
         {
           sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
         }
 
-
         if (viewport.getConservationSelected())
         {
           Conservation c = new Conservation("Group",
-                                            ResidueProperties.propHash, 3,
-                                            sg.getSequences(viewport.
-              hiddenRepSequences),
-                                            sg.getStartRes(),
-                                            sg.getEndRes()+1);
+                  ResidueProperties.propHash, 3,
+                  sg.getSequences(viewport.hiddenRepSequences),
+                  sg.getStartRes(), sg.getEndRes() + 1);
           c.calculate();
           c.verdict(false, viewport.ConsPercGaps);
           sg.cs.setConservation(c);
@@ -2652,57 +3134,57 @@ public class AlignFrame
         else
         {
           sg.cs.setConservation(null);
+        }
       }
     }
-    }
 
     if (alignPanel.getOverviewPanel() != null)
     {
       alignPanel.getOverviewPanel().updateOverviewImage();
     }
 
-
-
-
     alignPanel.paintAlignment(true);
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void modifyPID_actionPerformed(ActionEvent e)
   {
-    if (viewport.getAbovePIDThreshold() && viewport.globalColourScheme!=null)
+    if (viewport.getAbovePIDThreshold()
+            && viewport.globalColourScheme != null)
     {
       SliderPanel.setPIDSliderSource(alignPanel,
-                                     viewport.getGlobalColourScheme(),
-                                     "Background");
+              viewport.getGlobalColourScheme(), "Background");
       SliderPanel.showPIDSlider();
     }
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void modifyConservation_actionPerformed(ActionEvent e)
   {
-    if (viewport.getConservationSelected() && viewport.globalColourScheme!=null)
+    if (viewport.getConservationSelected()
+            && viewport.globalColourScheme != null)
     {
       SliderPanel.setConservationSlider(alignPanel,
-                                        viewport.globalColourScheme,
-                                        "Background");
+              viewport.globalColourScheme, "Background");
       SliderPanel.showConservationSlider();
     }
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void conservationMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2718,8 +3200,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void abovePIDThreshold_actionPerformed(ActionEvent e)
   {
@@ -2735,8 +3218,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void userDefinedColour_actionPerformed(ActionEvent e)
   {
@@ -2746,8 +3230,8 @@ public class AlignFrame
     }
     else
     {
-      UserColourScheme udc = (UserColourScheme) UserDefinedColours.
-          getUserColourSchemes().get(e.getActionCommand());
+      UserColourScheme udc = (UserColourScheme) UserDefinedColours
+              .getUserColourSchemes().get(e.getActionCommand());
 
       changeColour(udc);
     }
@@ -2760,8 +3244,8 @@ public class AlignFrame
     int i, iSize = menuItems.length;
     for (i = 0; i < iSize; i++)
     {
-      if (menuItems[i].getName() != null &&
-          menuItems[i].getName().equals("USER_DEFINED"))
+      if (menuItems[i].getName() != null
+              && menuItems[i].getName().equals("USER_DEFINED"))
       {
         colourMenu.remove(menuItems[i]);
         iSize--;
@@ -2769,47 +3253,47 @@ public class AlignFrame
     }
     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
     {
-      java.util.Enumeration userColours = jalview.gui.UserDefinedColours.
-          getUserColourSchemes().keys();
+      java.util.Enumeration userColours = jalview.gui.UserDefinedColours
+              .getUserColourSchemes().keys();
 
       while (userColours.hasMoreElements())
       {
         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
-            userColours.
-            nextElement().toString());
+                userColours.nextElement().toString());
         radioItem.setName("USER_DEFINED");
         radioItem.addMouseListener(new MouseAdapter()
+        {
+          public void mousePressed(MouseEvent evt)
+          {
+            if (evt.isControlDown()
+                    || SwingUtilities.isRightMouseButton(evt))
             {
-              public void mousePressed(MouseEvent evt)
-              {
-                if(evt.isControlDown() || SwingUtilities.isRightMouseButton(evt))
-                {
-                  radioItem.removeActionListener(radioItem.getActionListeners()[0]);
+              radioItem.removeActionListener(radioItem.getActionListeners()[0]);
 
-              int option = JOptionPane.showInternalConfirmDialog(jalview.gui.
-                  Desktop.desktop,
+              int option = JOptionPane.showInternalConfirmDialog(
+                      jalview.gui.Desktop.desktop,
                       "Remove from default list?",
                       "Remove user defined colour",
                       JOptionPane.YES_NO_OPTION);
-                  if(option == JOptionPane.YES_OPTION)
+              if (option == JOptionPane.YES_OPTION)
+              {
+                jalview.gui.UserDefinedColours
+                        .removeColourFromDefaults(radioItem.getText());
+                colourMenu.remove(radioItem);
+              }
+              else
+              {
+                radioItem.addActionListener(new ActionListener()
+                {
+                  public void actionPerformed(ActionEvent evt)
                   {
-                jalview.gui.UserDefinedColours.removeColourFromDefaults(
-                    radioItem.getText());
-                    colourMenu.remove(radioItem);
+                    userDefinedColour_actionPerformed(evt);
                   }
-                  else
-              {
-                    radioItem.addActionListener(new ActionListener()
-                    {
-                      public void actionPerformed(ActionEvent evt)
-                      {
-                        userDefinedColour_actionPerformed(evt);
-                      }
-                    });
-                }
+                });
               }
+            }
           }
-            });
+        });
         radioItem.addActionListener(new ActionListener()
         {
           public void actionPerformed(ActionEvent evt)
@@ -2826,8 +3310,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void PIDColour_actionPerformed(ActionEvent e)
   {
@@ -2836,8 +3321,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
   {
@@ -2846,27 +3332,29 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
   {
-    SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
-    AlignmentSorter.sortByPID(viewport.getAlignment(),
-                              viewport.getAlignment().getSequenceAt(0));
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
+    AlignmentSorter.sortByPID(viewport.getAlignment(), viewport
+            .getAlignment().getSequenceAt(0), null);
     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
-                                    viewport.alignment));
+            viewport.alignment));
     alignPanel.paintAlignment(true);
   }
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void sortIDMenuItem_actionPerformed(ActionEvent e)
   {
-    SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
     AlignmentSorter.sortByID(viewport.getAlignment());
     addHistoryItem(new OrderCommand("ID Sort", oldOrder, viewport.alignment));
     alignPanel.paintAlignment(true);
@@ -2874,42 +3362,60 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
+   */
+  public void sortLengthMenuItem_actionPerformed(ActionEvent e)
+  {
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
+    AlignmentSorter.sortByLength(viewport.getAlignment());
+    addHistoryItem(new OrderCommand("Length Sort", oldOrder,
+            viewport.alignment));
+    alignPanel.paintAlignment(true);
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
   {
-    SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
     AlignmentSorter.sortByGroup(viewport.getAlignment());
-    addHistoryItem(new OrderCommand("Group Sort", oldOrder, viewport.alignment));
+    addHistoryItem(new OrderCommand("Group Sort", oldOrder,
+            viewport.alignment));
 
     alignPanel.paintAlignment(true);
   }
+
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
   {
     new RedundancyPanel(alignPanel, this);
   }
 
-
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
   {
-    if ( (viewport.getSelectionGroup() == null) ||
-        (viewport.getSelectionGroup().getSize() < 2))
+    if ((viewport.getSelectionGroup() == null)
+            || (viewport.getSelectionGroup().getSize() < 2))
     {
       JOptionPane.showInternalMessageDialog(this,
-                                            "You must select at least 2 sequences.",
-                                            "Invalid Selection",
-                                            JOptionPane.WARNING_MESSAGE);
+              "You must select at least 2 sequences.", "Invalid Selection",
+              JOptionPane.WARNING_MESSAGE);
     }
     else
     {
@@ -2921,45 +3427,53 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void PCAMenuItem_actionPerformed(ActionEvent e)
   {
-    if ( ( (viewport.getSelectionGroup() != null) &&
-          (viewport.getSelectionGroup().getSize() < 4) &&
-          (viewport.getSelectionGroup().getSize() > 0)) ||
-        (viewport.getAlignment().getHeight() < 4))
+    if (((viewport.getSelectionGroup() != null)
+            && (viewport.getSelectionGroup().getSize() < 4) && (viewport
+            .getSelectionGroup().getSize() > 0))
+            || (viewport.getAlignment().getHeight() < 4))
     {
       JOptionPane.showInternalMessageDialog(this,
-                                            "Principal component analysis must take\n" +
-                                            "at least 4 input sequences.",
-                                            "Sequence selection insufficient",
-                                            JOptionPane.WARNING_MESSAGE);
+              "Principal component analysis must take\n"
+                      + "at least 4 input sequences.",
+              "Sequence selection insufficient",
+              JOptionPane.WARNING_MESSAGE);
 
       return;
     }
 
-     new PCAPanel(alignPanel);
+    new PCAPanel(alignPanel);
   }
 
-
   public void autoCalculate_actionPerformed(ActionEvent e)
   {
     viewport.autoCalculateConsensus = autoCalculate.isSelected();
-    if(viewport.autoCalculateConsensus)
+    if (viewport.autoCalculateConsensus)
     {
-      viewport.firePropertyChange("alignment",
-                                  null,
-                                  viewport.getAlignment().getSequences());
+      viewport.firePropertyChange("alignment", null, viewport
+              .getAlignment().getSequences());
     }
   }
+  public void sortByTreeOption_actionPerformed(ActionEvent e)
+  {
+    viewport.sortByTree = sortByTree.isSelected();
+  }
 
-
+  @Override
+  protected void listenToViewSelections_actionPerformed(ActionEvent e)
+  {
+    viewport.followSelection = listenToViewSelections.isSelected();
+  }
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2968,8 +3482,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2978,8 +3493,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2988,8 +3504,9 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2998,10 +3515,13 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param type DOCUMENT ME!
-   * @param pwType DOCUMENT ME!
-   * @param title DOCUMENT ME!
+   * 
+   * @param type
+   *          DOCUMENT ME!
+   * @param pwType
+   *          DOCUMENT ME!
+   * @param title
+   *          DOCUMENT ME!
    */
   void NewTreePanel(String type, String pwType, String title)
   {
@@ -3011,10 +3531,11 @@ public class AlignFrame
     {
       if (viewport.getSelectionGroup().getSize() < 3)
       {
-        JOptionPane.showMessageDialog(Desktop.desktop,
-                                      "You need to have more than two sequences selected to build a tree!",
-                                      "Not enough sequences",
-                                      JOptionPane.WARNING_MESSAGE);
+        JOptionPane
+                .showMessageDialog(
+                        Desktop.desktop,
+                        "You need to have more than two sequences selected to build a tree!",
+                        "Not enough sequences", JOptionPane.WARNING_MESSAGE);
         return;
       }
 
@@ -3024,15 +3545,17 @@ public class AlignFrame
       /* Decide if the selection is a column region */
       while (s < sg.getSize())
       {
-        if ( ( (SequenceI) sg.getSequences(null).elementAt(s++)).getLength() <
-            sg.getEndRes())
+        if (((SequenceI) sg.getSequences(null).elementAt(s++)).getLength() < sg
+                .getEndRes())
         {
-          JOptionPane.showMessageDialog(Desktop.desktop,
-                                        "The selected region to create a tree may\nonly contain residues or gaps.\n" +
-                                        "Try using the Pad function in the edit menu,\n" +
-                                        "or one of the multiple sequence alignment web services.",
-                                        "Sequences in selection are not aligned",
-                                        JOptionPane.WARNING_MESSAGE);
+          JOptionPane
+                  .showMessageDialog(
+                          Desktop.desktop,
+                          "The selected region to create a tree may\nonly contain residues or gaps.\n"
+                                  + "Try using the Pad function in the edit menu,\n"
+                                  + "or one of the multiple sequence alignment web services.",
+                          "Sequences in selection are not aligned",
+                          JOptionPane.WARNING_MESSAGE);
 
           return;
         }
@@ -3043,20 +3566,22 @@ public class AlignFrame
     }
     else
     {
-      //are the sequences aligned?
-      if (!viewport.alignment.isAligned())
+      // are the visible sequences aligned?
+      if (!viewport.alignment.isAligned(false))
       {
-        JOptionPane.showMessageDialog(Desktop.desktop,
-                                      "The sequences must be aligned before creating a tree.\n" +
-                                      "Try using the Pad function in the edit menu,\n" +
-                                      "or one of the multiple sequence alignment web services.",
-                                      "Sequences not aligned",
-                                      JOptionPane.WARNING_MESSAGE);
+        JOptionPane
+                .showMessageDialog(
+                        Desktop.desktop,
+                        "The sequences must be aligned before creating a tree.\n"
+                                + "Try using the Pad function in the edit menu,\n"
+                                + "or one of the multiple sequence alignment web services.",
+                        "Sequences not aligned",
+                        JOptionPane.WARNING_MESSAGE);
 
         return;
       }
 
-      if(viewport.alignment.getHeight()<2)
+      if (viewport.alignment.getHeight() < 2)
       {
         return;
       }
@@ -3066,9 +3591,9 @@ public class AlignFrame
 
     title += " from ";
 
-    if(viewport.viewName!=null)
+    if (viewport.viewName != null)
     {
-      title+= viewport.viewName+" of ";
+      title += viewport.viewName + " of ";
     }
 
     title += this.title;
@@ -3078,11 +3603,14 @@ public class AlignFrame
 
   /**
    * DOCUMENT ME!
-   *
-   * @param title DOCUMENT ME!
-   * @param order DOCUMENT ME!
+   * 
+   * @param title
+   *          DOCUMENT ME!
+   * @param order
+   *          DOCUMENT ME!
    */
-  public void addSortByOrderMenuItem(String title, final AlignmentOrder order)
+  public void addSortByOrderMenuItem(String title,
+          final AlignmentOrder order)
   {
     final JMenuItem item = new JMenuItem("by " + title);
     sort.add(item);
@@ -3090,24 +3618,31 @@ public class AlignFrame
     {
       public void actionPerformed(ActionEvent e)
       {
-        SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
+        SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
 
-        // TODO: JBPNote - have to map order entries to curent SequenceI pointers
+        // TODO: JBPNote - have to map order entries to curent SequenceI
+        // pointers
         AlignmentSorter.sortBy(viewport.getAlignment(), order);
 
         addHistoryItem(new OrderCommand(order.getName(), oldOrder,
-                                        viewport.alignment));
+                viewport.alignment));
 
         alignPanel.paintAlignment(true);
       }
     });
   }
+
   /**
    * Add a new sort by annotation score menu item
-   * @param sort the menu to add the option to
-   * @param scoreLabel the label used to retrieve scores for each sequence on the alignment
+   * 
+   * @param sort
+   *          the menu to add the option to
+   * @param scoreLabel
+   *          the label used to retrieve scores for each sequence on the
+   *          alignment
    */
-  public void addSortByAnnotScoreMenuItem(JMenu sort, final String scoreLabel)
+  public void addSortByAnnotScoreMenuItem(JMenu sort,
+          final String scoreLabel)
   {
     final JMenuItem item = new JMenuItem(scoreLabel);
     sort.add(item);
@@ -3115,43 +3650,48 @@ public class AlignFrame
     {
       public void actionPerformed(ActionEvent e)
       {
-        SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
-        AlignmentSorter.sortByAnnotationScore(scoreLabel, viewport.getAlignment());
-        addHistoryItem(new OrderCommand("Sort by "+scoreLabel, oldOrder, viewport.alignment));
+        SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
+        AlignmentSorter.sortByAnnotationScore(scoreLabel,
+                viewport.getAlignment());// ,viewport.getSelectionGroup());
+        addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
+                viewport.alignment));
         alignPanel.paintAlignment(true);
       }
     });
   }
+
   /**
-   * last hash for alignment's annotation array - used to minimise cost of rebuild.
+   * last hash for alignment's annotation array - used to minimise cost of
+   * rebuild.
    */
   protected int _annotationScoreVectorHash;
+
   /**
-   * search the alignment and rebuild the sort by annotation score submenu
-   * the last alignment annotation vector hash is stored to minimize
-   * cost of rebuilding in subsequence calls.
-   *
+   * search the alignment and rebuild the sort by annotation score submenu the
+   * last alignment annotation vector hash is stored to minimize cost of
+   * rebuilding in subsequence calls.
+   * 
    */
   public void buildSortByAnnotationScoresMenu()
   {
-    if(viewport.alignment.getAlignmentAnnotation()==null)
+    if (viewport.alignment.getAlignmentAnnotation() == null)
     {
       return;
     }
 
-    if (viewport.alignment.getAlignmentAnnotation().hashCode()!=_annotationScoreVectorHash)
+    if (viewport.alignment.getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
     {
       sortByAnnotScore.removeAll();
       // almost certainly a quicker way to do this - but we keep it simple
-      Hashtable scoreSorts=new Hashtable();
+      Hashtable scoreSorts = new Hashtable();
       AlignmentAnnotation aann[];
       Enumeration sq = viewport.alignment.getSequences().elements();
       while (sq.hasMoreElements())
       {
         aann = ((SequenceI) sq.nextElement()).getAnnotation();
-        for (int i=0;aann!=null && i<aann.length; i++)
+        for (int i = 0; aann != null && i < aann.length; i++)
         {
-          if (aann[i].hasScore() && aann[i].sequenceRef!=null)
+          if (aann[i].hasScore() && aann[i].sequenceRef != null)
           {
             scoreSorts.put(aann[i].label, aann[i].label);
           }
@@ -3160,36 +3700,40 @@ public class AlignFrame
       Enumeration labels = scoreSorts.keys();
       while (labels.hasMoreElements())
       {
-        addSortByAnnotScoreMenuItem(sortByAnnotScore, (String) labels.nextElement());
+        addSortByAnnotScoreMenuItem(sortByAnnotScore,
+                (String) labels.nextElement());
       }
-      sortByAnnotScore.setVisible(scoreSorts.size()>0);
+      sortByAnnotScore.setVisible(scoreSorts.size() > 0);
       scoreSorts.clear();
 
-      _annotationScoreVectorHash =
-          viewport.alignment.getAlignmentAnnotation().hashCode();
+      _annotationScoreVectorHash = viewport.alignment
+              .getAlignmentAnnotation().hashCode();
     }
   }
 
   /**
-   * Maintain the Order by->Displayed Tree menu.
-   * Creates a new menu item for a TreePanel with an appropriate
-   * <code>jalview.analysis.AlignmentSorter</code> call. Listeners are added
-   * to remove the menu item when the treePanel is closed, and adjust
-   * the tree leaf to sequence mapping when the alignment is modified.
-   * @param treePanel Displayed tree window.
-   * @param title SortBy menu item title.
+   * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
+   * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
+   * call. Listeners are added to remove the menu item when the treePanel is
+   * closed, and adjust the tree leaf to sequence mapping when the alignment is
+   * modified.
+   * 
+   * @param treePanel
+   *          Displayed tree window.
+   * @param title
+   *          SortBy menu item title.
    */
   public void buildTreeMenu()
   {
     sortByTreeMenu.removeAll();
 
-    Vector comps = (Vector) PaintRefresher.components.get(viewport.
-        getSequenceSetId());
+    Vector comps = (Vector) PaintRefresher.components.get(viewport
+            .getSequenceSetId());
     Vector treePanels = new Vector();
     int i, iSize = comps.size();
-    for(i=0; i<iSize; i++)
+    for (i = 0; i < iSize; i++)
     {
-      if(comps.elementAt(i) instanceof TreePanel)
+      if (comps.elementAt(i) instanceof TreePanel)
       {
         treePanels.add(comps.elementAt(i));
       }
@@ -3197,7 +3741,7 @@ public class AlignFrame
 
     iSize = treePanels.size();
 
-    if(iSize<1)
+    if (iSize < 1)
     {
       sortByTreeMenu.setVisible(false);
       return;
@@ -3205,23 +3749,18 @@ public class AlignFrame
 
     sortByTreeMenu.setVisible(true);
 
-    for(i=0; i<treePanels.size(); i++)
+    for (i = 0; i < treePanels.size(); i++)
     {
-      TreePanel tp = (TreePanel)treePanels.elementAt(i);
+      final TreePanel tp = (TreePanel) treePanels.elementAt(i);
       final JMenuItem item = new JMenuItem(tp.getTitle());
-      final NJTree tree = ((TreePanel)treePanels.elementAt(i)).getTree();
+      final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree();
       item.addActionListener(new java.awt.event.ActionListener()
       {
         public void actionPerformed(ActionEvent e)
         {
-          SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
-          AlignmentSorter.sortByTree(viewport.getAlignment(), tree);
-
-          addHistoryItem(new OrderCommand("Tree Sort",
-                                          oldOrder,
-                                          viewport.alignment));
-
-          alignPanel.paintAlignment(true);
+          tp.sortByTree_actionPerformed(null);
+          addHistoryItem(tp.sortAlignmentIn(alignPanel));
+          
         }
       });
 
@@ -3229,60 +3768,71 @@ public class AlignFrame
     }
   }
 
+  public boolean sortBy(AlignmentOrder alorder, String undoname)
+  {
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
+    AlignmentSorter.sortBy(viewport.getAlignment(), alorder);
+    if (undoname != null)
+    {
+      addHistoryItem(new OrderCommand(undoname, oldOrder,
+              viewport.alignment));
+    }
+    alignPanel.paintAlignment(true);
+    return true;
+  }
+
   /**
-   * Work out whether the whole set of sequences
-   * or just the selected set will be submitted for multiple alignment.
-   *
+   * Work out whether the whole set of sequences or just the selected set will
+   * be submitted for multiple alignment.
+   * 
    */
-  private jalview.datamodel.AlignmentView gatherSequencesForAlignment()
+  public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
   {
     // Now, check we have enough sequences
     AlignmentView msa = null;
 
-    if ( (viewport.getSelectionGroup() != null) &&
-        (viewport.getSelectionGroup().getSize() > 1))
-    {
-      // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to some common interface!
-      /*SequenceGroup seqs = viewport.getSelectionGroup();
-      int sz;
-      msa = new SequenceI[sz = seqs.getSize(false)];
-
-      for (int i = 0; i < sz; i++)
-      {
-        msa[i] = (SequenceI) seqs.getSequenceAt(i);
-      } */
+    if ((viewport.getSelectionGroup() != null)
+            && (viewport.getSelectionGroup().getSize() > 1))
+    {
+      // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
+      // some common interface!
+      /*
+       * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
+       * SequenceI[sz = seqs.getSize(false)];
+       * 
+       * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
+       * seqs.getSequenceAt(i); }
+       */
       msa = viewport.getAlignmentView(true);
     }
     else
     {
-      /*Vector seqs = viewport.getAlignment().getSequences();
-
-      if (seqs.size() > 1)
-      {
-        msa = new SequenceI[seqs.size()];
-
-        for (int i = 0; i < seqs.size(); i++)
-        {
-          msa[i] = (SequenceI) seqs.elementAt(i);
-        }
-      }*/
+      /*
+       * Vector seqs = viewport.getAlignment().getSequences();
+       * 
+       * if (seqs.size() > 1) { msa = new SequenceI[seqs.size()];
+       * 
+       * for (int i = 0; i < seqs.size(); i++) { msa[i] = (SequenceI)
+       * seqs.elementAt(i); } }
+       */
       msa = viewport.getAlignmentView(false);
     }
     return msa;
   }
 
   /**
-   * Decides what is submitted to a secondary structure prediction service,
-   * the currently selected sequence, or the currently selected alignment
-   * (where the first sequence in the set is the one that the prediction
-   * will be for).
+   * Decides what is submitted to a secondary structure prediction service: the
+   * first sequence in the alignment, or in the current selection, or, if the
+   * alignment is 'aligned' (ie padded with gaps), then the currently selected
+   * region or the whole alignment. (where the first sequence in the set is the
+   * one that the prediction will be for).
    */
-  AlignmentView gatherSeqOrMsaForSecStrPrediction()
+  public AlignmentView gatherSeqOrMsaForSecStrPrediction()
   {
-   AlignmentView seqs = null;
+    AlignmentView seqs = null;
 
-    if ( (viewport.getSelectionGroup() != null) &&
-        (viewport.getSelectionGroup().getSize() > 0))
+    if ((viewport.getSelectionGroup() != null)
+            && (viewport.getSelectionGroup().getSize() > 0))
     {
       seqs = viewport.getAlignmentView(true);
     }
@@ -3290,26 +3840,32 @@ public class AlignFrame
     {
       seqs = viewport.getAlignmentView(false);
     }
-    // limit sequences - JBPNote in future - could spawn multiple prediction jobs
-    // TODO: viewport.alignment.isAligned is a global state - the local selection may well be aligned - we preserve 2.0.8 behaviour for moment.
-    if (!viewport.alignment.isAligned())
+    // limit sequences - JBPNote in future - could spawn multiple prediction
+    // jobs
+    // TODO: viewport.alignment.isAligned is a global state - the local
+    // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
+    if (!viewport.alignment.isAligned(false))
     {
       seqs.setSequences(new SeqCigar[]
-                        {seqs.getSequences()[0]});
+      { seqs.getSequences()[0] });
+      // TODO: if seqs.getSequences().length>1 then should really have warned
+      // user!
+
     }
     return seqs;
   }
+
   /**
    * DOCUMENT ME!
-   *
-   * @param e DOCUMENT ME!
+   * 
+   * @param e
+   *          DOCUMENT ME!
    */
   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
   {
     // Pick the tree file
-    JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
-        getProperty(
-            "LAST_DIRECTORY"));
+    JalviewFileChooser chooser = new JalviewFileChooser(
+            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle("Select a newick-like tree file");
     chooser.setToolTipText("Load a tree file");
@@ -3320,56 +3876,65 @@ public class AlignFrame
     {
       String choice = chooser.getSelectedFile().getPath();
       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
-
+      jalview.io.NewickFile fin = null;
       try
       {
-        jalview.io.NewickFile fin = new jalview.io.NewickFile(choice,
-            "File");
+        fin = new jalview.io.NewickFile(choice, "File");
         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
+      } catch (Exception ex)
+      {
+        JOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
+                "Problem reading tree file", JOptionPane.WARNING_MESSAGE);
+        ex.printStackTrace();
       }
-      catch (Exception ex)
+      if (fin != null && fin.hasWarningMessage())
       {
         JOptionPane.showMessageDialog(Desktop.desktop,
-                                      "Problem reading tree file",
-                                      ex.getMessage(),
-                                      JOptionPane.WARNING_MESSAGE);
-        ex.printStackTrace();
+                fin.getWarningMessage(), "Possible problem with tree file",
+                JOptionPane.WARNING_MESSAGE);
       }
     }
   }
 
-
   public TreePanel ShowNewickTree(NewickFile nf, String title)
   {
-    return ShowNewickTree(nf,title,600,500,4,5);
+    return ShowNewickTree(nf, title, 600, 500, 4, 5);
   }
 
   public TreePanel ShowNewickTree(NewickFile nf, String title,
-                                  AlignmentView input)
+          AlignmentView input)
   {
-    return ShowNewickTree(nf,title, input, 600,500,4,5);
+    return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
   }
 
-  public TreePanel ShowNewickTree(NewickFile nf, String title, int w, int h,
-                                  int x, int y)
+  public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
+          int h, int x, int y)
   {
     return ShowNewickTree(nf, title, null, w, h, x, y);
   }
+
   /**
-   * Add a treeviewer for the tree extracted from a newick file object to the current alignment view
-   *
-   * @param nf the tree
-   * @param title tree viewer title
-   * @param input Associated alignment input data (or null)
-   * @param w width
-   * @param h height
-   * @param x position
-   * @param y position
+   * Add a treeviewer for the tree extracted from a newick file object to the
+   * current alignment view
+   * 
+   * @param nf
+   *          the tree
+   * @param title
+   *          tree viewer title
+   * @param input
+   *          Associated alignment input data (or null)
+   * @param w
+   *          width
+   * @param h
+   *          height
+   * @param x
+   *          position
+   * @param y
+   *          position
    * @return TreePanel handle
    */
   public TreePanel ShowNewickTree(NewickFile nf, String title,
-                                  AlignmentView input, int w, int h, int x,
-                                  int y)
+          AlignmentView input, int w, int h, int x, int y)
   {
     TreePanel tp = null;
 
@@ -3379,23 +3944,18 @@ public class AlignFrame
 
       if (nf.getTree() != null)
       {
-        tp = new TreePanel(alignPanel,
-                           "FromFile",
-                           title,
-                           nf, input);
+        tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
 
-        tp.setSize(w,h);
+        tp.setSize(w, h);
 
-        if(x>0 && y>0)
+        if (x > 0 && y > 0)
         {
-          tp.setLocation(x,y);
+          tp.setLocation(x, y);
         }
 
-
         Desktop.addInternalFrame(tp, title, w, h);
       }
-    }
-    catch (Exception ex)
+    } catch (Exception ex)
     {
       ex.printStackTrace();
     }
@@ -3403,206 +3963,277 @@ public class AlignFrame
     return tp;
   }
 
+  private boolean buildingMenu = false;
 
   /**
    * Generates menu items and listener event actions for web service clients
-   *
+   * 
    */
   public void BuildWebServiceMenu()
   {
-    if ( (Discoverer.services != null)
-        && (Discoverer.services.size() > 0))
+    while (buildingMenu)
+    {
+      try
+      {
+        System.err.println("Waiting for building menu to finish.");
+        Thread.sleep(10);
+      } catch (Exception e)
+      {
+      }
+      ;
+    }
+    final AlignFrame me = this;
+    buildingMenu = true;
+    new Thread(new Runnable()
     {
-      Vector msaws = (Vector) Discoverer.services.get("MsaWS");
-      Vector secstrpr = (Vector) Discoverer.services.get("SecStrPred");
-      Vector wsmenu = new Vector();
-      final AlignFrame af = this;
-      if (msaws != null)
+      public void run()
       {
-        // Add any Multiple Sequence Alignment Services
-        final JMenu msawsmenu = new JMenu("Alignment");
-        for (int i = 0, j = msaws.size(); i < j; i++)
+        try
         {
-          final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws.
-              get(i);
-          final JMenuItem method = new JMenuItem(sh.getName());
-          method.addActionListener(new ActionListener()
+          System.err.println("Building ws menu again "
+                  + Thread.currentThread());
+          // TODO: add support for context dependent disabling of services based
+          // on
+          // alignment and current selection
+          // TODO: add additional serviceHandle parameter to specify abstract
+          // handler
+          // class independently of AbstractName
+          // TODO: add in rediscovery GUI function to restart discoverer
+          // TODO: group services by location as well as function and/or
+          // introduce
+          // object broker mechanism.
+          final Vector wsmenu = new Vector();
+          final IProgressIndicator af = me;
+          final JMenu msawsmenu = new JMenu("Alignment");
+          final JMenu secstrmenu = new JMenu(
+                  "Secondary Structure Prediction");
+          final JMenu seqsrchmenu = new JMenu(
+                  "Sequence Database Search");
+          final JMenu analymenu = new JMenu(
+                  "Analysis");
+          // JAL-940 - only show secondary structure prediction services from the legacy server
+          if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
+                  // && 
+                  Discoverer.services != null
+                  && (Discoverer.services.size() > 0))
           {
-            public void actionPerformed(ActionEvent e)
+            // TODO: refactor to allow list of AbstractName/Handler bindings to
+            // be
+            // stored or retrieved from elsewhere
+            Vector msaws = null; // (Vector) Discoverer.services.get("MsaWS");
+            Vector secstrpr = (Vector) Discoverer.services
+                    .get("SecStrPred");
+            Vector seqsrch = null; // (Vector) Discoverer.services.get("SeqSearch");
+            // TODO: move GUI generation code onto service implementation - so a
+            // client instance attaches itself to the GUI with method call like
+            // jalview.ws.MsaWSClient.bind(servicehandle, Desktop.instance,
+            // alignframe)
+            if (msaws != null)
             {
-              AlignmentView msa = gatherSequencesForAlignment();
-              new jalview.ws.MsaWSClient(sh, title, msa,
-                                         false, true,
-                                         viewport.getAlignment().getDataset(),
-                                         af);
+              // Add any Multiple Sequence Alignment Services
+              for (int i = 0, j = msaws.size(); i < j; i++)
+              {
+                final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws
+                        .get(i);
+                jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
+                        .getServiceClient(sh);
+                impl.attachWSMenuEntry(msawsmenu, me);
 
+              }
             }
-
-          });
-          msawsmenu.add(method);
-          // Deal with services that we know accept partial alignments.
-          if (sh.getName().indexOf("lustal") > -1)
-          {
-            // We know that ClustalWS can accept partial alignments for refinement.
-            final JMenuItem methodR = new JMenuItem(sh.getName()+" Realign");
-            methodR.addActionListener(new ActionListener()
+            if (secstrpr != null)
             {
-              public void actionPerformed(ActionEvent e)
+              // Add any secondary structure prediction services
+              for (int i = 0, j = secstrpr.size(); i < j; i++)
               {
-                AlignmentView msa = gatherSequencesForAlignment();
-                new jalview.ws.MsaWSClient(sh, title, msa,
-                                           true, true,
-                                           viewport.getAlignment().getDataset(),
-                                           af);
-
+                final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
+                        .get(i);
+                jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
+                        .getServiceClient(sh);
+                impl.attachWSMenuEntry(secstrmenu, me);
               }
-
-            });
-            msawsmenu.add(methodR);
-
+            }
+            if (seqsrch != null)
+            {
+              // Add any sequence search services
+              for (int i = 0, j = seqsrch.size(); i < j; i++)
+              {
+                final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) seqsrch
+                        .elementAt(i);
+                jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
+                        .getServiceClient(sh);
+                impl.attachWSMenuEntry(seqsrchmenu, me);
+              }
+            }
           }
-        }
-        wsmenu.add(msawsmenu);
-      }
-      if (secstrpr != null)
-      {
-        // Add any secondary structure prediction services
-        final JMenu secstrmenu = new JMenu("Secondary Structure Prediction");
-        for (int i = 0, j = secstrpr.size(); i < j; i++)
-        {
-          final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle)
-              secstrpr.get(i);
-          final JMenuItem method = new JMenuItem(sh.getName());
-          method.addActionListener(new ActionListener()
+
+          // TODO: move into separate menu builder class.
+          if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
           {
-            public void actionPerformed(ActionEvent e)
+            Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
+            if (jws2servs != null)
             {
-              AlignmentView msa = gatherSeqOrMsaForSecStrPrediction();
-              if (msa.getSequences().length == 1)
+              if (jws2servs.hasServices())
               {
-                // Single Sequence prediction
-                new jalview.ws.JPredClient(sh, title, false, msa, af, true);
+                jws2servs.attachWSMenuEntry(msawsmenu, me);
               }
-              else
+            }
+          }
+          // Add all submenus in the order they should appear on the web services menu
+          wsmenu.add(msawsmenu);
+          wsmenu.add(secstrmenu);
+          wsmenu.add(analymenu);
+          // No search services yet
+          // wsmenu.add(seqsrchmenu);
+
+          javax.swing.SwingUtilities.invokeLater(new Runnable()
+          {
+            public void run()
+            {
+              try
               {
-                if (msa.getSequences().length > 1)
+                webService.removeAll();
+                // first, add discovered services onto the webservices menu
+                if (wsmenu.size() > 0)
                 {
-                  // Sequence profile based prediction
-                  new jalview.ws.JPredClient(sh,
-                      title, true, msa, af, true);
+                  for (int i = 0, j = wsmenu.size(); i < j; i++)
+                  {
+                    webService.add((JMenu) wsmenu.get(i));
+                  }
+                }
+                else
+                {
+                  webService.add(me.webServiceNoServices);
                 }
+                build_urlServiceMenu(me.webService);
+                build_fetchdbmenu(webService);
+              } catch (Exception e)
+              {
               }
+              ;
             }
           });
-          secstrmenu.add(method);
+        } catch (Exception e)
+        {
         }
-        wsmenu.add(secstrmenu);
-      }
-      resetWebServiceMenu();
-      for (int i = 0, j = wsmenu.size(); i < j; i++)
-      {
-        webService.add( (JMenu) wsmenu.get(i));
+        ;
+
+        buildingMenu = false;
       }
-    }
-    else
-    {
-      resetWebServiceMenu();
-      this.webService.add(this.webServiceNoServices);
-    }
-    // TODO: add in rediscovery function
-    // TODO: reduce code redundancy.
-    // TODO: group services by location as well as function.
+    }).start();
+
   }
 
 
   /**
-   * empty the web service menu and add any ad-hoc functions
-   * not dynamically discovered.
-   *
+   * construct any groupURL type service menu entries.
+   * 
+   * @param webService
    */
-  private void resetWebServiceMenu()
+  private void build_urlServiceMenu(JMenu webService)
   {
-    webService.removeAll();
-    // Temporary hack - DBRef Fetcher always top level ws entry.
-    JMenuItem rfetch = new JMenuItem("Fetch DB References");
-    rfetch.setToolTipText("Retrieve and parse uniprot records for the alignment or the currently selected sequences");
-    webService.add(rfetch);
-    rfetch.addActionListener(new ActionListener() {
-
-      public void actionPerformed(ActionEvent e)
-      {
-        new jalview.ws.DBRefFetcher(
-                alignPanel.av.getSequenceSelection(),
-                alignPanel.alignFrame).fetchDBRefs(false);
-      }
-
-    });
-  }
-
- /* public void vamsasStore_actionPerformed(ActionEvent e)
-  {
-    JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
-        getProperty("LAST_DIRECTORY"));
-
-    chooser.setFileView(new JalviewFileView());
-    chooser.setDialogTitle("Export to Vamsas file");
-    chooser.setToolTipText("Export");
-
-    int value = chooser.showSaveDialog(this);
+    // TODO: remove this code when 2.7 is released
+    // DEBUG - alignmentView
+    /*
+     * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
+     * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
+     * 
+     * @Override public void actionPerformed(ActionEvent e) {
+     * jalview.datamodel.AlignmentView.testSelectionViews(af.viewport.alignment,
+     * af.viewport.colSel, af.viewport.selectionGroup); }
+     * 
+     * }); webService.add(testAlView);
+     */
+    // TODO: refactor to RestClient discoverer and merge menu entries for
+    // rest-style services with other types of analysis/calculation service
+    // SHmmr test client - still being implemented.
+    // DEBUG - alignmentView
+    
+    for (jalview.ws.rest.RestClient client: jalview.ws.rest.RestClient.getRestClients()) {
+      client.attachWSMenuEntry(JvSwingUtils.findOrCreateMenu(webService, client.getAction()), this);
+    }
 
-    if (value == JalviewFileChooser.APPROVE_OPTION)
+    if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
     {
-      jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
-      //vs.store(chooser.getSelectedFile().getAbsolutePath()   );
-      vs.storeJalview( chooser.getSelectedFile().getAbsolutePath(), this);
+      jalview.ws.EnfinEnvision2OneWay.getInstance().attachWSMenuEntry(
+              webService, this);
     }
-  }*/
+  }
+
+  /*
+   * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
+   * chooser = new JalviewFileChooser(jalview.bin.Cache.
+   * getProperty("LAST_DIRECTORY"));
+   * 
+   * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
+   * to Vamsas file"); chooser.setToolTipText("Export");
+   * 
+   * int value = chooser.showSaveDialog(this);
+   * 
+   * if (value == JalviewFileChooser.APPROVE_OPTION) {
+   * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
+   * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
+   * chooser.getSelectedFile().getAbsolutePath(), this); } }
+   */
   /**
    * prototype of an automatically enabled/disabled analysis function
-   *
+   * 
    */
   protected void setShowProductsEnabled()
   {
-    SequenceI [] selection = viewport.getSequenceSelection();
-    if (canShowProducts(selection, viewport.getSelectionGroup()!=null, viewport.getAlignment().getDataset()))
+    SequenceI[] selection = viewport.getSequenceSelection();
+    if (canShowProducts(selection, viewport.getSelectionGroup() != null,
+            viewport.getAlignment().getDataset()))
     {
       showProducts.setEnabled(true);
-      
-    } else {
+
+    }
+    else
+    {
       showProducts.setEnabled(false);
     }
   }
+
   /**
-   * search selection for sequence xRef products and build the
-   * show products menu.
+   * search selection for sequence xRef products and build the show products
+   * menu.
+   * 
    * @param selection
    * @param dataset
    * @return true if showProducts menu should be enabled.
    */
-  public boolean canShowProducts(SequenceI[] selection, boolean isRegionSelection, Alignment dataset)
+  public boolean canShowProducts(SequenceI[] selection,
+          boolean isRegionSelection, Alignment dataset)
   {
-    boolean showp=false;
-    try {
+    boolean showp = false;
+    try
+    {
       showProducts.removeAll();
       final boolean dna = viewport.getAlignment().isNucleotide();
       final Alignment ds = dataset;
-      String[] ptypes = CrossRef.findSequenceXrefTypes(dna, selection, dataset);
-      //Object[] prods = CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(), selection, dataset, true);
+      String[] ptypes = (selection == null || selection.length == 0) ? null
+              : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
+      // Object[] prods =
+      // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
+      // selection, dataset, true);
       final SequenceI[] sel = selection;
-      for (int t=0; ptypes!=null && t<ptypes.length; t++)
+      for (int t = 0; ptypes != null && t < ptypes.length; t++)
       {
-        showp=true;
+        showp = true;
         final boolean isRegSel = isRegionSelection;
         final AlignFrame af = this;
         final String source = ptypes[t];
         JMenuItem xtype = new JMenuItem(ptypes[t]);
-        xtype.addActionListener(new ActionListener() {
+        xtype.addActionListener(new ActionListener()
+        {
 
           public void actionPerformed(ActionEvent e)
           {
-            af.showProductsFor(sel, ds, isRegSel, dna, source);
+            // TODO: new thread for this call with vis-delay
+            af.showProductsFor(af.viewport.getSequenceSelection(), ds,
+                    isRegSel, dna, source);
           }
-          
+
         });
         showProducts.add(xtype);
       }
@@ -3610,171 +4241,251 @@ public class AlignFrame
       showProducts.setEnabled(showp);
     } catch (Exception e)
     {
-      jalview.bin.Cache.log.warn("canTranslate threw an exception - please report to help@jalview.org",e);
-     return false;
+      jalview.bin.Cache.log
+              .warn("canTranslate threw an exception - please report to help@jalview.org",
+                      e);
+      return false;
     }
     return showp;
   }
-protected void showProductsFor(SequenceI[] sel, Alignment ds, boolean isRegSel, boolean dna, String source)
+
+  protected void showProductsFor(SequenceI[] sel, Alignment ds,
+          boolean isRegSel, boolean dna, String source)
   {
-  ds = this.getViewport().alignment.getDataset(); // update our local dataset reference
-  Alignment prods = CrossRef.findXrefSequences(sel, dna, source, ds);
-  if (prods!=null)
+    final boolean fisRegSel = isRegSel;
+    final boolean fdna = dna;
+    final String fsrc = source;
+    final AlignFrame ths = this;
+    final SequenceI[] fsel = sel;
+    Runnable foo = new Runnable()
+    {
+
+      public void run()
+      {
+        final long sttime = System.currentTimeMillis();
+        ths.setProgressBar("Searching for sequences from " + fsrc, sttime);
+        try
+        {
+          Alignment ds = ths.getViewport().alignment.getDataset(); // update
+          // our local
+          // dataset
+          // reference
+          Alignment prods = CrossRef
+                  .findXrefSequences(fsel, fdna, fsrc, ds);
+          if (prods != null)
+          {
+            SequenceI[] sprods = new SequenceI[prods.getHeight()];
+            for (int s = 0; s < sprods.length; s++)
+            {
+              sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
+              if (ds.getSequences() == null
+                      || !ds.getSequences().contains(
+                              sprods[s].getDatasetSequence()))
+                ds.addSequence(sprods[s].getDatasetSequence());
+              sprods[s].updatePDBIds();
+            }
+            Alignment al = new Alignment(sprods);
+            AlignedCodonFrame[] cf = prods.getCodonFrames();
+            al.setDataset(ds);
+            for (int s = 0; cf != null && s < cf.length; s++)
+            {
+              al.addCodonFrame(cf[s]);
+              cf[s] = null;
+            }
+            AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
+                    DEFAULT_HEIGHT);
+            String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
+                    + " for " + ((fisRegSel) ? "selected region of " : "")
+                    + getTitle();
+            Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
+                    DEFAULT_HEIGHT);
+          }
+          else
+          {
+            System.err.println("No Sequences generated for xRef type "
+                    + fsrc);
+          }
+        } catch (Exception e)
+        {
+          jalview.bin.Cache.log.error(
+                  "Exception when finding crossreferences", e);
+        } catch (OutOfMemoryError e)
+        {
+          new OOMWarning("whilst fetching crossreferences", e);
+        } catch (Error e)
+        {
+          jalview.bin.Cache.log.error("Error when finding crossreferences",
+                  e);
+        }
+        ths.setProgressBar("Finished searching for sequences from " + fsrc,
+                sttime);
+      }
+
+    };
+    Thread frunner = new Thread(foo);
+    frunner.start();
+  }
+
+  public boolean canShowTranslationProducts(SequenceI[] selection,
+          AlignmentI alignment)
   {
-    SequenceI[] sprods = new SequenceI[prods.getHeight()];
-    for (int s=0; s<sprods.length;s++)
+    // old way
+    try
     {
-      sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
-      if (ds.getSequences()==null || !ds.getSequences().contains(sprods[s].getDatasetSequence()))
-        ds.addSequence(sprods[s].getDatasetSequence());
-    }
-    Alignment al = new Alignment(sprods);
-    AlignedCodonFrame[] cf = prods.getCodonFrames();
-    for (int s=0; cf!=null && s<cf.length; s++)
+      return (jalview.analysis.Dna.canTranslate(selection,
+              viewport.getViewAsVisibleContigs(true)));
+    } catch (Exception e)
     {
-      al.addCodonFrame(cf[s]);
-      cf[s] = null;
+      jalview.bin.Cache.log
+              .warn("canTranslate threw an exception - please report to help@jalview.org",
+                      e);
+      return false;
     }
-    al.setDataset(ds);
-    AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-    String newtitle =""+((dna) ? "Proteins " : "Nucleotides ") + " for "+((isRegSel) ? "selected region of " : "")
-            + getTitle();
-    Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
-            DEFAULT_HEIGHT);
-  } else {
-    System.err.println("No Sequences generated for xRef type "+source);
-  }
   }
 
-
-public boolean canShowTranslationProducts(SequenceI[] selection, AlignmentI alignment)
-{
-  // old way
-  try {
-      return (jalview.analysis.Dna.canTranslate(selection, viewport.getViewAsVisibleContigs(true)));
-  } catch (Exception e)
+  public void showProducts_actionPerformed(ActionEvent e)
   {
-    jalview.bin.Cache.log.warn("canTranslate threw an exception - please report to help@jalview.org",e);
-   return false;
-  }
-}
+    // /////////////////////////////
+    // Collect Data to be translated/transferred
 
-public void showProducts_actionPerformed(ActionEvent e)
-{
-  ///////////////////////////////
-  // Collect Data to be translated/transferred
-  
-  SequenceI [] selection = viewport.getSequenceSelection();
-  AlignmentI al  = null;
-  try {
-      al = jalview.analysis.Dna.CdnaTranslate(selection, viewport.getViewAsVisibleContigs(true),
-          viewport.getGapCharacter(), viewport.getAlignment().getDataset());
-    } catch (Exception ex) {
+    SequenceI[] selection = viewport.getSequenceSelection();
+    AlignmentI al = null;
+    try
+    {
+      al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
+              .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
+              viewport.getAlignment().getDataset());
+    } catch (Exception ex)
+    {
       al = null;
-      jalview.bin.Cache.log.debug("Exception during translation.",ex);
+      jalview.bin.Cache.log.debug("Exception during translation.", ex);
     }
-    if (al==null)
+    if (al == null)
+    {
+      JOptionPane
+              .showMessageDialog(
+                      Desktop.desktop,
+                      "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
+                      "Translation Failed", JOptionPane.WARNING_MESSAGE);
+    }
+    else
     {
-      JOptionPane.showMessageDialog(Desktop.desktop,
-          "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
-          "Translation Failed",
-          JOptionPane.WARNING_MESSAGE);
-    } else {
       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-      Desktop.addInternalFrame(af, "Translation of "+this.getTitle(),
-                               DEFAULT_WIDTH,
-                               DEFAULT_HEIGHT);
+      Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
+              DEFAULT_WIDTH, DEFAULT_HEIGHT);
     }
   }
 
-public void showTranslation_actionPerformed(ActionEvent e)
-{
-  ///////////////////////////////
-  // Collect Data to be translated/transferred
-
-  SequenceI [] selection = viewport.getSequenceSelection();
-  String [] seqstring = viewport.getViewAsString(true);
-  AlignmentI al  = null;
-  try {
-    al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring, viewport.getViewAsVisibleContigs(true),
-        viewport.getGapCharacter(), viewport.alignment.getAlignmentAnnotation(),
-        viewport.alignment.getWidth(), viewport.getAlignment().getDataset());
-  } catch (Exception ex) {
-    al = null;
-    jalview.bin.Cache.log.debug("Exception during translation.",ex);
-  }
-  if (al==null)
-  {
-    JOptionPane.showMessageDialog(Desktop.desktop,
-        "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
-        "Translation Failed",
-        JOptionPane.WARNING_MESSAGE);
-  } else {
-    AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-    Desktop.addInternalFrame(af, "Translation of "+this.getTitle(),
-                             DEFAULT_WIDTH,
-                             DEFAULT_HEIGHT);
+  public void showTranslation_actionPerformed(ActionEvent e)
+  {
+    // /////////////////////////////
+    // Collect Data to be translated/transferred
+
+    SequenceI[] selection = viewport.getSequenceSelection();
+    String[] seqstring = viewport.getViewAsString(true);
+    AlignmentI al = null;
+    try
+    {
+      al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
+              viewport.getViewAsVisibleContigs(true), viewport
+                      .getGapCharacter(), viewport.alignment
+                      .getAlignmentAnnotation(), viewport.alignment
+                      .getWidth(), viewport.getAlignment().getDataset());
+    } catch (Exception ex)
+    {
+      al = null;
+      jalview.bin.Cache.log.debug("Exception during translation.", ex);
+    }
+    if (al == null)
+    {
+      JOptionPane
+              .showMessageDialog(
+                      Desktop.desktop,
+                      "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
+                      "Translation Failed", JOptionPane.WARNING_MESSAGE);
+    }
+    else
+    {
+      AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+      Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
+              DEFAULT_WIDTH, DEFAULT_HEIGHT);
+    }
   }
-}
 
-/**
- * DOCUMENT ME!
- *
- * @param String DOCUMENT ME!
- */
-public boolean parseFeaturesFile(String file, String type)
-{
+  /**
+   * Try to load a features file onto the alignment.
+   * 
+   * @param file
+   *          contents or path to retrieve file
+   * @param type
+   *          access mode of file (see jalview.io.AlignFile)
+   * @return true if features file was parsed corectly.
+   */
+  public boolean parseFeaturesFile(String file, String type)
+  {
     boolean featuresFile = false;
     try
     {
-      featuresFile = new FeaturesFile(file,
-          type).parse(viewport.alignment.getDataset(),
-                                         alignPanel.seqPanel.seqCanvas.
-                                         getFeatureRenderer().featureColours,
-                                         false);
-    }
-    catch(Exception ex)
+      featuresFile = new FeaturesFile(file, type)
+              .parse(viewport.alignment.getDataset(),
+                      alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureColours,
+                      false, jalview.bin.Cache.getDefault(
+                              "RELAXEDSEQIDMATCHING", false));
+    } catch (Exception ex)
     {
       ex.printStackTrace();
     }
 
-    if(featuresFile)
+    if (featuresFile)
     {
       viewport.showSequenceFeatures = true;
       showSeqFeatures.setSelected(true);
+      if (alignPanel.seqPanel.seqCanvas.fr != null)
+      {
+        // update the min/max ranges where necessary
+        alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
+      }
+      if (featureSettings != null)
+      {
+        featureSettings.setTableData();
+      }
       alignPanel.paintAlignment(true);
     }
 
     return featuresFile;
-}
+  }
 
-public void dragEnter(DropTargetDragEvent evt)
-{}
+  public void dragEnter(DropTargetDragEvent evt)
+  {
+  }
 
-public void dragExit(DropTargetEvent evt)
-{}
+  public void dragExit(DropTargetEvent evt)
+  {
+  }
 
-public void dragOver(DropTargetDragEvent evt)
-{}
+  public void dragOver(DropTargetDragEvent evt)
+  {
+  }
 
-public void dropActionChanged(DropTargetDragEvent evt)
-{}
+  public void dropActionChanged(DropTargetDragEvent evt)
+  {
+  }
 
-public void drop(DropTargetDropEvent evt)
-{
+  public void drop(DropTargetDropEvent evt)
+  {
     Transferable t = evt.getTransferable();
     java.util.List files = null;
 
     try
     {
       DataFlavor uriListFlavor = new DataFlavor(
-          "text/uri-list;class=java.lang.String");
+              "text/uri-list;class=java.lang.String");
       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
       {
-        //Works on Windows and MacOSX
+        // Works on Windows and MacOSX
         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        files = (java.util.List) t.getTransferData(DataFlavor.
-            javaFileListFlavor);
+        files = (java.util.List) t
+                .getTransferData(DataFlavor.javaFileListFlavor);
       }
       else if (t.isDataFlavorSupported(uriListFlavor))
       {
@@ -3783,9 +4494,7 @@ public void drop(DropTargetDropEvent evt)
         String data = (String) t.getTransferData(uriListFlavor);
         files = new java.util.ArrayList(1);
         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
-            data,
-            "\r\n");
-             st.hasMoreTokens(); )
+                data, "\r\n"); st.hasMoreTokens();)
         {
           String s = st.nextToken();
           if (s.startsWith("#"))
@@ -3795,12 +4504,20 @@ public void drop(DropTargetDropEvent evt)
           }
 
           java.net.URI uri = new java.net.URI(s);
-          java.io.File file = new java.io.File(uri);
-          files.add(file);
+          // check to see if we can handle this kind of URI
+          if (uri.getScheme().toLowerCase().startsWith("http"))
+          {
+            files.add(uri.toString());
+          }
+          else
+          {
+            // otherwise preserve old behaviour: catch all for file objects
+            java.io.File file = new java.io.File(uri);
+            files.add(file.toString());
+          }
         }
       }
-    }
-    catch (Exception e)
+    } catch (Exception e)
     {
       e.printStackTrace();
     }
@@ -3808,69 +4525,223 @@ public void drop(DropTargetDropEvent evt)
     {
       try
       {
-
+        // check to see if any of these files have names matching sequences in
+        // the alignment
+        SequenceIdMatcher idm = new SequenceIdMatcher(viewport
+                .getAlignment().getSequencesArray());
+        /**
+         * Object[] { String,SequenceI}
+         */
+        ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
+        ArrayList<String> filesnotmatched = new ArrayList<String>();
         for (int i = 0; i < files.size(); i++)
         {
-          loadJalviewDataFile(files.get(i).toString());
+          String file = files.get(i).toString();
+          String pdbfn = "";
+          String protocol = FormatAdapter.checkProtocol(file);
+          if (protocol == jalview.io.FormatAdapter.FILE)
+          {
+            File fl = new File(file);
+            pdbfn = fl.getName();
+          }
+          else if (protocol == jalview.io.FormatAdapter.URL)
+          {
+            URL url = new URL(file);
+            pdbfn = url.getFile();
+          }
+          if (pdbfn.length() > 0)
+          {
+            // 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)
+            {
+              String type = null;
+              try
+              {
+                type = new IdentifyFile().Identify(file, protocol);
+              } catch (Exception ex)
+              {
+                type = null;
+              }
+              if (type != null)
+              {
+                if (type.equalsIgnoreCase("PDB"))
+                {
+                  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);
+          }
         }
-      }
-      catch (Exception ex)
+        int assocfiles = 0;
+        if (filesmatched.size() > 0)
+        {
+          if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
+                  || JOptionPane
+                          .showConfirmDialog(
+                                  this,
+                                  "Do you want to automatically associate the "
+                                          + filesmatched.size()
+                                          + " PDB files with sequences in the alignment that have the same name ?",
+                                  "Automatically Associate PDB files by name",
+                                  JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
+
+          {
+            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], (String) fm[1],
+                              toassoc, false);
+              if (pe != null)
+              {
+                System.err
+                        .println("Associated file : " + ((String) fm[0])
+                                + " with "
+                                + toassoc.getDisplayId(true));
+                assocfiles++;
+              }
+              }
+              alignPanel.paintAlignment(true);
+            }
+          }
+        }
+        if (filesnotmatched.size() > 0)
+        {
+          if (assocfiles > 0
+                  && (Cache.getDefault(
+                          "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
+                          .showConfirmDialog(
+                                  this,
+                                  "<html>Do you want to <em>ignore</em> the "
+                                          + filesnotmatched.size()
+                                          + " files whose names did not match any sequence IDs ?</html>",
+                                  "Ignore unmatched dropped files ?",
+                                  JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
+          {
+            return;
+          }
+          for (String fn : filesnotmatched)
+          {
+            loadJalviewDataFile(fn, null, null, null);
+          }
+
+        }
+      } catch (Exception ex)
       {
         ex.printStackTrace();
       }
     }
-}
+  }
 
-  // This method will attempt to load a "dropped" file first by testing
-  // whether its and Annotation file, then features file. If both are
-  // false then the user may have dropped an alignment file onto this
-  // AlignFrame
-   public void loadJalviewDataFile(String file)
+  /**
+   * Attempt to load a "dropped" file or URL string: First by testing whether
+   * it's and Annotation file, then a JNet file, and finally a features file. If
+   * all are false then the user may have dropped an alignment file onto this
+   * AlignFrame.
+   * 
+   * @param file
+   *          either a filename or a URL string.
+   */
+  public void loadJalviewDataFile(String file, String protocol,
+          String format, SequenceI assocSeq)
   {
     try
     {
-      String protocol = "File";
-
-      if (file.indexOf("http:") > -1 || file.indexOf("file:") > -1)
+      if (protocol == null)
       {
-        protocol = "URL";
+        protocol = jalview.io.FormatAdapter.checkProtocol(file);
       }
-
-      boolean isAnnotation = new AnnotationFile().readAnnotationFile(viewport.
-          alignment, file, protocol);
+      // if the file isn't identified, or not positively identified as some
+      // other filetype (PFAM is default unidentified alignment file type) then
+      // try to parse as annotation.
+      boolean isAnnotation = (format == null || format
+              .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
+              .readAnnotationFile(viewport.alignment, file, protocol)
+              : false;
 
       if (!isAnnotation)
       {
-        boolean isGroupsFile = parseFeaturesFile(file,protocol);
-        if (!isGroupsFile)
+        // try to see if its a JNet 'concise' style annotation file *before* we
+        // try to parse it as a features file
+        if (format == null)
         {
-          String format = new IdentifyFile().Identify(file, protocol);
-
-          if(format.equalsIgnoreCase("JnetFile"))
+          format = new IdentifyFile().Identify(file, protocol);
+        }
+        if (format.equalsIgnoreCase("JnetFile"))
+        {
+          jalview.io.JPredFile predictions = new jalview.io.JPredFile(file,
+                  protocol);
+          new JnetAnnotationMaker().add_annotation(predictions,
+                  viewport.getAlignment(), 0, false);
+          isAnnotation = true;
+        }
+        else
+        {
+          /*
+           * if (format.equalsIgnoreCase("PDB")) {
+           * 
+           * String pdbfn = ""; // try to match up filename with sequence id try
+           * { if (protocol == jalview.io.FormatAdapter.FILE) { File fl = new
+           * File(file); pdbfn = fl.getName(); } else if (protocol ==
+           * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
+           * url.getFile(); } } catch (Exception e) { } ; if (assocSeq == null)
+           * { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
+           * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) { //
+           * attempt to find a match in the alignment SequenceI mtch =
+           * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
+           * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) > l)
+           * { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch =
+           * idm.findIdMatch(pdbfn); } if (mtch != null) { // try and associate
+           * // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
+           * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null) {
+           * System.err.println("Associated file : " + file + " with " +
+           * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
+           * TODO: maybe need to load as normal otherwise return; } }
+           */
+          // try to parse it as a features file
+          boolean isGroupsFile = parseFeaturesFile(file, protocol);
+          // if it wasn't a features file then we just treat it as a general
+          // alignment file to load into the current view.
+          if (!isGroupsFile)
           {
-            jalview.io.JPredFile predictions = new jalview.io.JPredFile(
-                file, protocol);
-            new JnetAnnotationMaker().add_annotation(predictions,
-                viewport.getAlignment(),
-                0, false);
-            alignPanel.adjustAnnotationHeight();
-            alignPanel.paintAlignment(true);
+            new FileLoader().LoadFile(viewport, file, protocol, format);
           }
           else
           {
-            new FileLoader().LoadFile(viewport, file, protocol, format);
+            alignPanel.paintAlignment(true);
+          }
         }
       }
-      }
-      else
+      if (isAnnotation)
       {
-        // (isAnnotation)
+
         alignPanel.adjustAnnotationHeight();
+        viewport.updateSequenceIdColours();
         buildSortByAnnotationScoresMenu();
+        alignPanel.paintAlignment(true);
       }
-
-    }
-    catch (Exception ex)
+    } catch (Exception ex)
     {
       ex.printStackTrace();
     }
@@ -3888,50 +4759,365 @@ public void drop(DropTargetDropEvent evt)
 
   public void tabbedPane_mousePressed(MouseEvent e)
   {
-    if(SwingUtilities.isRightMouseButton(e))
+    if (SwingUtilities.isRightMouseButton(e))
     {
       String reply = JOptionPane.showInternalInputDialog(this,
-          "Enter View Name",
-          "Edit View Name",
-          JOptionPane.QUESTION_MESSAGE);
+              "Enter View Name", "Edit View Name",
+              JOptionPane.QUESTION_MESSAGE);
 
       if (reply != null)
       {
         viewport.viewName = reply;
-        tabbedPane.setTitleAt( tabbedPane.getSelectedIndex() ,reply);
+        tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
       }
     }
   }
 
-
   public AlignViewport getCurrentView()
   {
     return viewport;
   }
 
-
   /**
    * Open the dialog for regex description parsing.
    */
   protected void extractScores_actionPerformed(ActionEvent e)
   {
-    ParseProperties pp = new jalview.analysis.ParseProperties(viewport.alignment);
-    if (pp.getScoresFromDescription("col", "score column ", "\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)")>0)
+    ParseProperties pp = new jalview.analysis.ParseProperties(
+            viewport.alignment);
+    // TODO: verify regex and introduce GUI dialog for version 2.5
+    // if (pp.getScoresFromDescription("col", "score column ",
+    // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
+    // true)>0)
+    if (pp.getScoresFromDescription("description column",
+            "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
     {
       buildSortByAnnotationScoresMenu();
     }
   }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
+   * )
+   */
+  protected void showDbRefs_actionPerformed(ActionEvent e)
+  {
+    viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
+   * ActionEvent)
+   */
+  protected void showNpFeats_actionPerformed(ActionEvent e)
+  {
+    viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
+  }
+
+  /**
+   * find the viewport amongst the tabs in this alignment frame and close that
+   * tab
+   * 
+   * @param av
+   */
+  public boolean closeView(AlignViewport av)
+  {
+    if (viewport == av)
+    {
+      this.closeMenuItem_actionPerformed(false);
+      return true;
+    }
+    Component[] comp = tabbedPane.getComponents();
+    for (int i = 0; comp != null && i < comp.length; i++)
+    {
+      if (comp[i] instanceof AlignmentPanel)
+      {
+        if (((AlignmentPanel) comp[i]).av == av)
+        {
+          // close the view.
+          closeView((AlignmentPanel) comp[i]);
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  protected void build_fetchdbmenu(JMenu webService)
+  {
+    // Temporary hack - DBRef Fetcher always top level ws entry.
+    // TODO We probably want to store a sequence database checklist in
+    // preferences and have checkboxes.. rather than individual sources selected
+    // here
+    final JMenu rfetch = new JMenu("Fetch DB References");
+    rfetch.setToolTipText("Retrieve and parse sequence database records for the alignment or the currently selected sequences");
+    webService.add(rfetch);
+
+    JMenuItem fetchr = new JMenuItem("Standard Databases");
+    fetchr.setToolTipText("Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources");
+    fetchr.addActionListener(new ActionListener()
+    {
+
+      public void actionPerformed(ActionEvent e)
+      {
+        new Thread(new Runnable()
+        {
+
+          public void run()
+          {
+            new jalview.ws.DBRefFetcher(alignPanel.av
+                    .getSequenceSelection(), alignPanel.alignFrame)
+                    .fetchDBRefs(false);
+          }
+        }).start();
+
+      }
+
+    });
+    rfetch.add(fetchr);
+    final AlignFrame me = this;
+    new Thread(new Runnable()
+    {
+      public void run()
+      {
+        final jalview.ws.SequenceFetcher sf = SequenceFetcher
+                .getSequenceFetcherSingleton(me);
+        final String[] otherdb = sf.getOrderedSupportedSources();
+        // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
+        // jalview.util.QuickSort.sort(otherdb, otherdb);
+        javax.swing.SwingUtilities.invokeLater(new Runnable()
+        {
+          public void run()
+          {
+
+            JMenu dfetch = new JMenu();
+            JMenuItem fetchr;
+            rfetch.add(dfetch);
+            int comp = 0, mcomp = 15;
+            String mname = null;
+            if (otherdb != null && otherdb.length > 0)
+            {
+              for (int i = 0; i < otherdb.length; i++)
+              {
+                String dbname = sf.getSourceProxy(otherdb[i]).getDbName();
+                if (mname == null)
+                {
+                  mname = "from '" + dbname + "'";
+                }
+                fetchr = new JMenuItem(otherdb[i]);
+                final String[] dassource = new String[]
+                { otherdb[i] };
+                fetchr.addActionListener(new ActionListener()
+                {
+
+                  public void actionPerformed(ActionEvent e)
+                  {
+                    new Thread(new Runnable()
+                    {
+
+                      public void run()
+                      {
+                        new jalview.ws.DBRefFetcher(alignPanel.av
+                                .getSequenceSelection(),
+                                alignPanel.alignFrame, dassource)
+                                .fetchDBRefs(false);
+                      }
+                    }).start();
+                  }
+
+                });
+                fetchr.setToolTipText("Retrieve from " + dbname);
+                dfetch.add(fetchr);
+                if (comp++ == mcomp || i == (otherdb.length - 1))
+                {
+                  dfetch.setText(mname + " to '" + dbname + "'");
+                  rfetch.add(dfetch);
+                  dfetch = new JMenu();
+                  mname = null;
+                  comp = 0;
+                }
+              }
+            }
+          }
+        });
+      }
+    }).start();
+
+  }
+
+  /**
+   * Left justify the whole alignment.
+   */
+  protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
+  {
+    AlignmentI al = viewport.getAlignment();
+    al.justify(false);
+    viewport.firePropertyChange("alignment", null, al);
+  }
+
+  /**
+   * Right justify the whole alignment.
+   */
+  protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
+  {
+    AlignmentI al = viewport.getAlignment();
+    al.justify(true);
+    viewport.firePropertyChange("alignment", null, al);
+  }
+
+  public void setShowSeqFeatures(boolean b)
+  {
+    showSeqFeatures.setSelected(true);
+    viewport.setShowSequenceFeatures(true);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
+   * awt.event.ActionEvent)
+   */
+  protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
+  {
+    viewport.setShowUnconserved(showNonconservedMenuItem.getState());
+    alignPanel.paintAlignment(true);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
+   * .ActionEvent)
+   */
+  protected void showGroupConsensus_actionPerformed(ActionEvent e)
+  {
+    viewport.setShowGroupConsensus(showGroupConsensus.getState());
+    alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
+
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
+   * .event.ActionEvent)
+   */
+  protected void showGroupConservation_actionPerformed(ActionEvent e)
+  {
+    viewport.setShowGroupConservation(showGroupConservation.getState());
+    alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
+   * .event.ActionEvent)
+   */
+  protected void showConsensusHistogram_actionPerformed(ActionEvent e)
+  {
+    viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
+    alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
+   * .event.ActionEvent)
+   */
+  protected void showSequenceLogo_actionPerformed(ActionEvent e)
+  {
+    viewport.setShowSequenceLogo(showSequenceLogo.getState());
+    alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
+  }
+
+  protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
+  {
+    alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
+   * .event.ActionEvent)
+   */
+  protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
+  {
+    if (viewport.getSelectionGroup() != null)
+    {
+      SequenceGroup[] gps = jalview.analysis.Grouping.makeGroupsFrom(
+              viewport.getSequenceSelection(),
+              viewport.getAlignmentView(true).getSequenceStrings(
+                      viewport.getGapCharacter()),
+              viewport.alignment.getGroups());
+      viewport.alignment.deleteAllGroups();
+      viewport.sequenceColours = null;
+      viewport.setSelectionGroup(null);
+      // set view properties for each group
+      for (int g = 0; g < gps.length; g++)
+      {
+        gps[g].setShowNonconserved(viewport.getShowUnconserved());
+        gps[g].setshowSequenceLogo(viewport.isShowSequenceLogo());
+        viewport.alignment.addGroup(gps[g]);
+        Color col = new Color((int) (Math.random() * 255),
+                (int) (Math.random() * 255), (int) (Math.random() * 255));
+        col = col.brighter();
+        for (Enumeration sq = gps[g].getSequences(null).elements(); sq
+                .hasMoreElements(); viewport.setSequenceColour(
+                (SequenceI) sq.nextElement(), col))
+          ;
+      }
+      PaintRefresher.Refresh(this, viewport.getSequenceSetId());
+      alignPanel.updateAnnotation();
+      alignPanel.paintAlignment(true);
+    }
+  }
+
+  /**
+   * make the given alignmentPanel the currently selected tab
+   * 
+   * @param alignmentPanel
+   */
+  public void setDisplayedView(AlignmentPanel alignmentPanel)
+  {
+    if (!viewport.getSequenceSetId().equals(
+            alignmentPanel.av.getSequenceSetId()))
+    {
+      throw new Error(
+              "Implementation error: cannot show a view from another alignment in an AlignFrame.");
+    }
+    if (tabbedPane != null
+            & alignPanels.indexOf(alignmentPanel) != tabbedPane.getSelectedIndex())
+    {
+      tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
+    }
+  }
 }
 
-class PrintThread
-    extends Thread
+class PrintThread extends Thread
 {
   AlignmentPanel ap;
+
   public PrintThread(AlignmentPanel ap)
   {
-   this.ap = ap;
+    this.ap = ap;
   }
+
   static PageFormat pf;
+
   public void run()
   {
     PrinterJob printJob = PrinterJob.getPrinterJob();
@@ -3950,8 +5136,7 @@ class PrintThread
       try
       {
         printJob.print();
-      }
-      catch (Exception PrintException)
+      } catch (Exception PrintException)
       {
         PrintException.printStackTrace();
       }