JAL-2077 test isPopupTrigger in mouseReleased or mouseClicked (for
[jalview.git] / src / jalview / gui / AlignFrame.java
index f04744d..e8e7da3 100644 (file)
@@ -32,6 +32,7 @@ import jalview.api.AlignViewControllerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureSettingsControllerI;
+import jalview.api.FeatureSettingsModelI;
 import jalview.api.SplitContainerI;
 import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
@@ -53,6 +54,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SeqCigar;
@@ -72,6 +74,7 @@ import jalview.io.JalviewFileView;
 import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
 import jalview.io.TCoffeeScoreFile;
+import jalview.io.gff.SequenceOntologyI;
 import jalview.jbgui.GAlignFrame;
 import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.BuriedColourScheme;
@@ -94,6 +97,9 @@ import jalview.schemes.ZappoColourScheme;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.ws.DBRefFetcher;
+import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
+import jalview.ws.SequenceFetcher;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
@@ -107,13 +113,14 @@ import java.awt.datatransfer.Clipboard;
 import java.awt.datatransfer.DataFlavor;
 import java.awt.datatransfer.StringSelection;
 import java.awt.datatransfer.Transferable;
-import java.awt.dnd.DnDConstants;
 import java.awt.dnd.DropTargetDragEvent;
 import java.awt.dnd.DropTargetDropEvent;
 import java.awt.dnd.DropTargetEvent;
 import java.awt.dnd.DropTargetListener;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.KeyAdapter;
@@ -131,7 +138,6 @@ import java.util.Deque;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
-import java.util.Set;
 import java.util.Vector;
 
 import javax.swing.JCheckBoxMenuItem;
@@ -241,7 +247,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     this(al, hiddenColumns, width, height, null);
   }
 
-
   /**
    * Create alignment frame for al with hiddenColumns, a specific width and
    * height, and specific sequenceId
@@ -286,7 +291,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     alignPanel = new AlignmentPanel(this, viewport);
 
-
     addAlignmentPanel(alignPanel, true);
     init();
   }
@@ -312,7 +316,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     init();
   }
 
-
   /**
    * Make a new AlignFrame from existing alignmentPanels
    * 
@@ -371,10 +374,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       setGUINucleotide(viewport.getAlignment().isNucleotide());
     }
 
+    this.alignPanel.av
+            .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+
     setMenusFromViewport(viewport);
     buildSortByAnnotationScoresMenu();
     buildTreeMenu();
-    
+
     if (viewport.getWrapAlignment())
     {
       wrapMenuItem_actionPerformed(null);
@@ -455,7 +461,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 }
               }
             });
-    formatMenu.add(vsel);
+    if (Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase()
+            .indexOf("devel") > -1
+            || Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase()
+                    .indexOf("test") > -1)
+    {
+      formatMenu.add(vsel);
+    }
+    addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusGained(FocusEvent e)
+      {
+        Jalview.setCurrentAlignFrame(AlignFrame.this);
+      }
+    });
 
   }
 
@@ -552,8 +572,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         case KeyEvent.VK_SPACE:
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().insertGapAtCursor(evt.isControlDown()
-                    || evt.isShiftDown() || evt.isAltDown());
+            alignPanel.getSeqPanel().insertGapAtCursor(
+                    evt.isControlDown() || evt.isShiftDown()
+                            || evt.isAltDown());
           }
           break;
 
@@ -576,8 +597,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.getSeqPanel().deleteGapAtCursor(evt.isControlDown()
-                    || evt.isShiftDown() || evt.isAltDown());
+            alignPanel.getSeqPanel().deleteGapAtCursor(
+                    evt.isControlDown() || evt.isShiftDown()
+                            || evt.isAltDown());
           }
 
           break;
@@ -625,8 +647,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         case KeyEvent.VK_F2:
           viewport.cursorMode = !viewport.cursorMode;
           statusBar.setText(MessageManager.formatMessage(
-                  "label.keyboard_editing_mode", new String[]
-                  { (viewport.cursorMode ? "on" : "off") }));
+                  "label.keyboard_editing_mode",
+                  new String[] { (viewport.cursorMode ? "on" : "off") }));
           if (viewport.cursorMode)
           {
             alignPanel.getSeqPanel().seqCanvas.cursorX = viewport.startRes;
@@ -820,13 +842,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void setGUINucleotide(boolean nucleotide)
   {
     showTranslation.setVisible(nucleotide);
+    showReverse.setVisible(nucleotide);
+    showReverseComplement.setVisible(nucleotide);
     conservationMenuItem.setEnabled(!nucleotide);
     modifyConservation.setEnabled(!nucleotide);
     showGroupConservation.setEnabled(!nucleotide);
     rnahelicesColour.setEnabled(nucleotide);
     purinePyrimidineColour.setEnabled(nucleotide);
-    showComplementMenuItem.setText(MessageManager
-            .getString(nucleotide ? "label.protein" : "label.nucleotide"));
+    showComplementMenuItem.setText(nucleotide ? MessageManager
+            .getString("label.protein") : MessageManager
+            .getString("label.nucleotide"));
     setColourSelected(jalview.bin.Cache.getDefault(
             nucleotide ? Preferences.DEFAULT_COLOUR_NUC
                     : Preferences.DEFAULT_COLOUR_PROT, "None"));
@@ -837,6 +862,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * operation that affects the data in the current view (selection changed,
    * etc) to update the menus to reflect the new state.
    */
+  @Override
   public void setMenusForViewport()
   {
     setMenusFromViewport(viewport);
@@ -894,10 +920,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     rnahelicesColour.setEnabled(av.getAlignment().hasRNAStructure());
     rnahelicesColour
             .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
-    setShowProductsEnabled();
+
+    showProducts.setEnabled(canShowProducts());
+    setGroovyEnabled(Desktop.getGroovyConsole() != null);
+
     updateEditMenuBar();
   }
 
+  /**
+   * Set the enabled state of the 'Run Groovy' option in the Calculate menu
+   * 
+   * @param b
+   */
+  public void setGroovyEnabled(boolean b)
+  {
+    runGroovy.setEnabled(b);
+  }
+
   private IProgressIndicator progressBar;
 
   /*
@@ -950,7 +989,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void fetchSequence_actionPerformed(ActionEvent e)
   {
-    new SequenceFetcher(this);
+    new jalview.gui.SequenceFetcher(this);
   }
 
   @Override
@@ -1071,7 +1110,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             currentFileFormat, false);
 
     chooser.setFileView(new JalviewFileView());
-    chooser.setDialogTitle(MessageManager.getString("label.save_alignment_to_file"));
+    chooser.setDialogTitle(MessageManager
+            .getString("label.save_alignment_to_file"));
     chooser.setToolTipText(MessageManager.getString("action.save"));
 
     int value = chooser.showSaveDialog(this);
@@ -1129,8 +1169,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       success = new Jalview2XML().saveAlignment(this, file, shortName);
 
       statusBar.setText(MessageManager.formatMessage(
-              "label.successfully_saved_to_file_in_format", new Object[]
-              { fileName, format }));
+              "label.successfully_saved_to_file_in_format", new Object[] {
+                  fileName, format }));
 
     }
     else
@@ -1154,7 +1194,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       }
       FormatAdapter f = new FormatAdapter(alignPanel,
               exportData.getSettings());
-      String output = f.formatSequences(format,
+      String output = f.formatSequences(
+              format,
               exportData.getAlignment(), // class cast exceptions will
               // occur in the distant future
               exportData.getOmitHidden(), exportData.getStartEndPostions(),
@@ -1177,8 +1218,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           this.setTitle(file);
           statusBar.setText(MessageManager.formatMessage(
                   "label.successfully_saved_to_file_in_format",
-                  new Object[]
-                  { fileName, format }));
+                  new Object[] { fileName, format }));
         } catch (Exception ex)
         {
           success = false;
@@ -1190,8 +1230,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (!success)
     {
       JOptionPane.showInternalMessageDialog(this, MessageManager
-              .formatMessage("label.couldnt_save_file", new Object[]
-              { fileName }), MessageManager
+              .formatMessage("label.couldnt_save_file",
+                      new Object[] { fileName }), MessageManager
               .getString("label.error_saving_file"),
               JOptionPane.WARNING_MESSAGE);
     }
@@ -1199,7 +1239,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return success;
   }
 
-
   private void warningMessage(String warning, String title)
   {
     if (new jalview.util.Platform().isHeadless())
@@ -1236,14 +1275,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     try
     {
       cap.setText(new FormatAdapter(alignPanel, exportData.getSettings())
-              .formatSequences(
-              e.getActionCommand(),
- exportData.getAlignment(),
-              exportData.getOmitHidden(), exportData.getStartEndPostions(),
-              viewport.getColumnSelection()));
+              .formatSequences(e.getActionCommand(),
+                      exportData.getAlignment(),
+                      exportData.getOmitHidden(),
+                      exportData.getStartEndPostions(),
+                      viewport.getColumnSelection()));
       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
-              "label.alignment_output_command", new Object[]
-              { e.getActionCommand() }), 600, 500);
+              "label.alignment_output_command",
+              new Object[] { e.getActionCommand() }), 600, 500);
     } catch (OutOfMemoryError oom)
     {
       new OOMWarning("Outputting alignment as " + e.getActionCommand(), oom);
@@ -1252,36 +1291,34 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   }
 
-  public static AlignmentExportData getAlignmentForExport(String exportFormat,
- AlignViewportI viewport,
+  public static AlignmentExportData getAlignmentForExport(
+          String exportFormat, AlignViewportI viewport,
           AlignExportSettingI exportSettings)
   {
     AlignmentI alignmentToExport = null;
     AlignExportSettingI settings = exportSettings;
     String[] omitHidden = null;
-    int[] alignmentStartEnd = new int[2];
 
     HiddenSequences hiddenSeqs = viewport.getAlignment()
             .getHiddenSequences();
 
-
     alignmentToExport = viewport.getAlignment();
-    alignmentStartEnd = new int[]
-    { 0, alignmentToExport.getWidth() - 1 };
 
     boolean hasHiddenSeqs = hiddenSeqs.getSize() > 0;
     if (settings == null)
     {
       settings = new AlignExportSettings(hasHiddenSeqs,
-            viewport.hasHiddenColumns(), exportFormat);
+              viewport.hasHiddenColumns(), exportFormat);
     }
     // settings.isExportAnnotations();
 
     if (viewport.hasHiddenColumns() && !settings.isExportHiddenColumns())
     {
-      omitHidden = viewport.getViewAsString(false);
+      omitHidden = viewport.getViewAsString(false,
+              settings.isExportHiddenSequences());
     }
 
+    int[] alignmentStartEnd = new int[2];
     if (hasHiddenSeqs && settings.isExportHiddenSequences())
     {
       alignmentToExport = hiddenSeqs.getFullAlignment();
@@ -1289,74 +1326,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     else
     {
       alignmentToExport = viewport.getAlignment();
-      alignmentStartEnd = getStartEnd(alignmentStartEnd, viewport
-              .getColumnSelection().getHiddenColumns());
     }
-    AlignmentExportData ed = new AlignmentExportData(alignmentToExport, omitHidden, alignmentStartEnd,
-            settings);
+    alignmentStartEnd = alignmentToExport
+            .getVisibleStartAndEndIndex(viewport.getColumnSelection()
+                    .getHiddenColumns());
+    AlignmentExportData ed = new AlignmentExportData(alignmentToExport,
+            omitHidden, alignmentStartEnd, settings);
     return ed;
   }
 
-  public static int[] getStartEnd(int[] aligmentStartEnd,
-          List<int[]> hiddenCols)
-  {
-    int startPos = aligmentStartEnd[0];
-    int endPos = aligmentStartEnd[1];
-
-    int[] lowestRange = new int[2];
-    int[] higestRange = new int[2];
-
-    for (int[] hiddenCol : hiddenCols)
-    {
-      // System.out.println("comparing : " + hiddenCol[0] + "-" + hiddenCol[1]);
-      lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
-      higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
-    }
-    // System.out.println("min : " + lowestRange[0] + "-" + lowestRange[1]);
-    // System.out.println("max : " + higestRange[0] + "-" + higestRange[1]);
-
-    if (lowestRange[0] == 0 && lowestRange[1] == 0)
-    {
-      startPos = aligmentStartEnd[0];
-    }
-    else
-    {
-      startPos = lowestRange[1] + 1;
-    }
-
-    if (higestRange[0] == 0 && higestRange[1] == 0)
-    {
-      endPos = aligmentStartEnd[1];
-    }
-    else
-    {
-      endPos = higestRange[0];
-    }
-
-    // System.out.println("Export range : " + minPos + " - " + maxPos);
-    return new int[]
-    { startPos, endPos };
-  }
-
-  public static void main(String[] args)
-  {
-    ArrayList<int[]> hiddenCols = new ArrayList<int[]>();
-    hiddenCols.add(new int[]
-    { 0, 4 });
-    hiddenCols.add(new int[]
-    { 6, 9 });
-    hiddenCols.add(new int[]
-    { 11, 12 });
-    hiddenCols.add(new int[]
-    { 33, 33 });
-    hiddenCols.add(new int[]
-    { 45, 50 });
-
-    int[] x = getStartEnd(new int[]
-    { 0, 50 }, hiddenCols);
-    // System.out.println("Export range : " + x[0] + " - " + x[1]);
-  }
-
   /**
    * DOCUMENT ME!
    * 
@@ -1372,9 +1350,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void bioJSMenuItem_actionPerformed(ActionEvent e)
   {
-    BioJsHTMLOutput bjs = new BioJsHTMLOutput(alignPanel);
+    BioJsHTMLOutput bjs = new BioJsHTMLOutput(alignPanel, this);
     bjs.exportJalviewAlignmentAsBioJsHtmlFile();
   }
+
   public void createImageMap(File file, String image)
   {
     alignPanel.makePNGImageMap(file, image);
@@ -1404,10 +1383,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     alignPanel.makeEPS(f);
   }
 
+  @Override
   public void createSVG(File f)
   {
     alignPanel.makeSVG(f);
   }
+
   @Override
   public void pageSetup_actionPerformed(ActionEvent e)
   {
@@ -1551,8 +1532,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       undoMenuItem.setEnabled(true);
       CommandI command = viewport.getHistoryList().peek();
       undoMenuItem.setText(MessageManager.formatMessage(
-              "label.undo_command", new Object[]
-              { command.getDescription() }));
+              "label.undo_command",
+              new Object[] { command.getDescription() }));
     }
     else
     {
@@ -1566,8 +1547,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       CommandI command = viewport.getRedoList().peek();
       redoMenuItem.setText(MessageManager.formatMessage(
-              "label.redo_command", new Object[]
-              { command.getDescription() }));
+              "label.redo_command",
+              new Object[] { command.getDescription() }));
     }
     else
     {
@@ -1576,6 +1557,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
+  @Override
   public void addHistoryItem(CommandI command)
   {
     if (command.getSize() > 0)
@@ -1609,8 +1591,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     if (viewport != null)
     {
-      return new AlignmentI[]
-      { viewport.getAlignment() };
+      return new AlignmentI[] { viewport.getAlignment() };
     }
     return null;
   }
@@ -1844,8 +1825,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     boolean appendHistoryItem = false;
     Deque<CommandI> historyList = viewport.getHistoryList();
     boolean inSplitFrame = getSplitViewContainer() != null;
-    if (!inSplitFrame && historyList != null
-            && historyList.size() > 0
+    if (!inSplitFrame && historyList != null && historyList.size() > 0
             && historyList.peek() instanceof SlideSequencesCommand)
     {
       appendHistoryItem = ssc
@@ -1916,17 +1896,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
         {
-          hiddenColumns.add(new int[]
-          { region[0] - hiddenOffset, region[1] - hiddenOffset });
+          hiddenColumns.add(new int[] { region[0] - hiddenOffset,
+              region[1] - hiddenOffset });
         }
       }
     }
 
-    Desktop.jalviewClipboard = new Object[]
-    { seqs, viewport.getAlignment().getDataset(), hiddenColumns };
+    Desktop.jalviewClipboard = new Object[] { seqs,
+        viewport.getAlignment().getDataset(), hiddenColumns };
     statusBar.setText(MessageManager.formatMessage(
-            "label.copied_sequences_to_clipboard", new Object[]
-            { Integer.valueOf(seqs.length).toString() }));
+            "label.copied_sequences_to_clipboard", new Object[] { Integer
+                    .valueOf(seqs.length).toString() }));
   }
 
   /**
@@ -1981,7 +1961,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           return;
         }
 
-        format = new IdentifyFile().Identify(str, "Paste");
+        format = new IdentifyFile().identify(str, "Paste");
 
       } catch (OutOfMemoryError er)
       {
@@ -2135,8 +2115,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         //
         addHistoryItem(new EditCommand(
                 MessageManager.getString("label.add_sequences"),
-                Action.PASTE,
-                sequences, 0, alignment.getWidth(), alignment));
+                Action.PASTE, sequences, 0, alignment.getWidth(), alignment));
       }
       // Add any annotations attached to sequences
       for (int i = 0; i < sequences.length; i++)
@@ -2254,7 +2233,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         // found!!<<<
         af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
                 .transferSettings(
-                        alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer());
+                        alignPanel.getSeqPanel().seqCanvas
+                                .getFeatureRenderer());
 
         // TODO: maintain provenance of an alignment, rather than just make the
         // title a concatenation of operations.
@@ -2313,7 +2293,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // found!!<<<
       af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
               .transferSettings(
-                      alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer());
+                      alignPanel.getSeqPanel().seqCanvas
+                              .getFeatureRenderer());
 
       // TODO: maintain provenance of an alignment, rather than just make the
       // title a concatenation of operations.
@@ -2375,20 +2356,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
      */
     if (sg.getSize() == viewport.getAlignment().getHeight())
     {
-      int confirm = JOptionPane.showConfirmDialog(this,
-              MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
-              MessageManager.getString("label.delete_all"), // $NON-NLS-1$
-              JOptionPane.OK_CANCEL_OPTION);
-
-      if (confirm == JOptionPane.CANCEL_OPTION
-              || confirm == JOptionPane.CLOSED_OPTION)
+      boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes()) + 1) == viewport
+              .getAlignment().getWidth()) ? true : false;
+      if (isEntireAlignWidth)
       {
-        return;
+        int confirm = JOptionPane.showConfirmDialog(this,
+                MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
+                MessageManager.getString("label.delete_all"), // $NON-NLS-1$
+                JOptionPane.OK_CANCEL_OPTION);
+
+        if (confirm == JOptionPane.CANCEL_OPTION
+                || confirm == JOptionPane.CLOSED_OPTION)
+        {
+          return;
+        }
       }
       viewport.getColumnSelection().removeElements(sg.getStartRes(),
               sg.getEndRes() + 1);
     }
-
     SequenceI[] cut = sg.getSequences()
             .toArray(new SequenceI[sg.getSize()]);
 
@@ -2450,7 +2435,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
     viewport.setSelectionGroup(sg);
     viewport.sendSelection();
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
   }
 
@@ -2473,7 +2461,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.setSelectionGroup(null);
     alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(null);
     alignPanel.getIdPanel().getIdCanvas().searchResults = null;
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2500,6 +2491,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
     }
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
 
     alignPanel.paintAlignment(true);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
@@ -2543,7 +2537,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     ColumnSelection colSel = viewport.getColumnSelection();
     int column;
 
-    if (colSel.size() > 0)
+    if (!colSel.isEmpty())
     {
       if (trimLeft)
       {
@@ -2568,23 +2562,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       TrimRegionCommand trimRegion;
       if (trimLeft)
       {
-        trimRegion = new TrimRegionCommand("Remove Left",
-                TrimRegionCommand.TRIM_LEFT, seqs, column,
-                viewport.getAlignment(), viewport.getColumnSelection(),
-                viewport.getSelectionGroup());
+        trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
+                column, viewport.getAlignment());
         viewport.setStartRes(0);
       }
       else
       {
-        trimRegion = new TrimRegionCommand("Remove Right",
-                TrimRegionCommand.TRIM_RIGHT, seqs, column,
-                viewport.getAlignment(), viewport.getColumnSelection(),
-                viewport.getSelectionGroup());
+        trimRegion = new TrimRegionCommand("Remove Right", false, seqs,
+                column, viewport.getAlignment());
       }
 
       statusBar.setText(MessageManager.formatMessage(
-              "label.removed_columns", new String[]
-              { Integer.valueOf(trimRegion.getSize()).toString() }));
+              "label.removed_columns",
+              new String[] { Integer.valueOf(trimRegion.getSize())
+                      .toString() }));
 
       addHistoryItem(trimRegion);
 
@@ -2633,8 +2624,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     addHistoryItem(removeGapCols);
 
     statusBar.setText(MessageManager.formatMessage(
-            "label.removed_empty_columns", new Object[]
-            { Integer.valueOf(removeGapCols.getSize()).toString() }));
+            "label.removed_empty_columns",
+            new Object[] { Integer.valueOf(removeGapCols.getSize())
+                    .toString() }));
 
     // This is to maintain viewport position on first residue
     // of first sequence
@@ -2849,7 +2841,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void expandViews_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.explodeViews(this);
+    Desktop.explodeViews(this);
   }
 
   /**
@@ -2884,8 +2876,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.setShowJVSuffix(seqLimits.isSelected());
 
-    alignPanel.getIdPanel().getIdCanvas().setPreferredSize(alignPanel
-            .calculateIdWidth());
+    alignPanel.getIdPanel().getIdCanvas()
+            .setPreferredSize(alignPanel.calculateIdWidth());
     alignPanel.paintAlignment(true);
   }
 
@@ -2964,13 +2956,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.showAllHiddenColumns();
     repaint();
+    viewport.sendSelection();
   }
 
   @Override
   public void hideSelSequences_actionPerformed(ActionEvent e)
   {
     viewport.hideAllSelectedSeqs();
-//    alignPanel.paintAlignment(true);
+    // alignPanel.paintAlignment(true);
   }
 
   /**
@@ -3054,6 +3047,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void hideAllButSelection_actionPerformed(ActionEvent e)
   {
     toggleHiddenRegions(false, false);
+    viewport.sendSelection();
   }
 
   /*
@@ -3071,6 +3065,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.hideAllSelectedSeqs();
     viewport.hideSelectedColumns();
     alignPanel.paintAlignment(true);
+    viewport.sendSelection();
   }
 
   /*
@@ -3086,6 +3081,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.showAllHiddenColumns();
     viewport.showAllHiddenSeqs();
     alignPanel.paintAlignment(true);
+    viewport.sendSelection();
   }
 
   @Override
@@ -3093,6 +3089,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.hideSelectedColumns();
     alignPanel.paintAlignment(true);
+    viewport.sendSelection();
   }
 
   @Override
@@ -3223,30 +3220,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * Set or clear 'Show Sequence Features'
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
-  {
-    viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
-            .isSelected());
-    if (viewport.isShowSequenceFeaturesHeight())
-    {
-      // ensure we're actually displaying features
-      viewport.setShowSequenceFeatures(true);
-      showSeqFeatures.setSelected(true);
-    }
-    alignPanel.paintAlignment(true);
-    if (alignPanel.getOverviewPanel() != null)
-    {
-      alignPanel.getOverviewPanel().updateOverviewImage();
-    }
-  }
-
-  /**
    * Action on toggle of the 'Show annotations' menu item. This shows or hides
    * the annotations panel as a whole.
    * 
@@ -3275,14 +3248,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
             .formatAsHtml();
     editPane.setText(MessageManager.formatMessage("label.html_content",
-            new Object[]
-            { contents.toString() }));
+            new Object[] { contents.toString() }));
     JInternalFrame frame = new JInternalFrame();
     frame.getContentPane().add(new JScrollPane(editPane));
 
     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
-            "label.alignment_properties", new Object[]
-            { getTitle() }), 500, 400);
+            "label.alignment_properties", new Object[] { getTitle() }),
+            500, 400);
   }
 
   /**
@@ -3303,8 +3275,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     OverviewPanel overview = new OverviewPanel(alignPanel);
     frame.setContentPane(overview);
     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
-            "label.overview_params", new Object[]
-            { this.getTitle() }), frame.getWidth(), frame.getHeight());
+            "label.overview_params", new Object[] { this.getTitle() }),
+            frame.getWidth(), frame.getHeight());
     frame.pack();
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
@@ -3495,6 +3467,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param cs
    *          DOCUMENT ME!
    */
+  @Override
   public void changeColour(ColourSchemeI cs)
   {
     // TODO: pull up to controller method
@@ -3514,6 +3487,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
                 cs, "Background"));
       }
+      if (cs instanceof TCoffeeColourScheme)
+      {
+        tcoffeeColour.setEnabled(true);
+        tcoffeeColour.setSelected(true);
+      }
     }
 
     viewport.setGlobalColourScheme(cs);
@@ -3647,35 +3625,51 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           @Override
           public void mousePressed(MouseEvent evt)
           {
-            if (evt.isControlDown()
-                    || SwingUtilities.isRightMouseButton(evt))
+            if (evt.isPopupTrigger()) // Mac
             {
-              radioItem.removeActionListener(radioItem.getActionListeners()[0]);
+              offerRemoval(radioItem);
+            }
+          }
 
-              int option = JOptionPane.showInternalConfirmDialog(
-                      jalview.gui.Desktop.desktop,
-                      MessageManager
-                              .getString("label.remove_from_default_list"),
-                      MessageManager
-                              .getString("label.remove_user_defined_colour"),
-                      JOptionPane.YES_NO_OPTION);
-              if (option == JOptionPane.YES_OPTION)
-              {
-                jalview.gui.UserDefinedColours
-                        .removeColourFromDefaults(radioItem.getText());
-                colourMenu.remove(radioItem);
-              }
-              else
+          @Override
+          public void mouseReleased(MouseEvent evt)
+          {
+            if (evt.isPopupTrigger()) // Windows
+            {
+              offerRemoval(radioItem);
+            }
+          }
+
+          /**
+           * @param radioItem
+           */
+          void offerRemoval(final JRadioButtonMenuItem radioItem)
+          {
+            radioItem.removeActionListener(radioItem.getActionListeners()[0]);
+
+            int option = JOptionPane.showInternalConfirmDialog(
+                    jalview.gui.Desktop.desktop,
+                    MessageManager
+                            .getString("label.remove_from_default_list"),
+                    MessageManager
+                            .getString("label.remove_user_defined_colour"),
+                    JOptionPane.YES_NO_OPTION);
+            if (option == JOptionPane.YES_OPTION)
+            {
+              jalview.gui.UserDefinedColours
+                      .removeColourFromDefaults(radioItem.getText());
+              colourMenu.remove(radioItem);
+            }
+            else
+            {
+              radioItem.addActionListener(new ActionListener()
               {
-                radioItem.addActionListener(new ActionListener()
+                @Override
+                public void actionPerformed(ActionEvent evt)
                 {
-                  @Override
-                  public void actionPerformed(ActionEvent evt)
-                  {
-                    userDefinedColour_actionPerformed(evt);
-                  }
-                });
-              }
+                  userDefinedColour_actionPerformed(evt);
+                }
+              });
             }
           }
         });
@@ -4024,7 +4018,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void addSortByOrderMenuItem(String title,
           final AlignmentOrder order)
   {
-    final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new Object[]{title}));
+    final JMenuItem item = new JMenuItem(MessageManager.formatMessage(
+            "action.by_title_param", new Object[] { title }));
     sort.add(item);
     item.addActionListener(new java.awt.event.ActionListener()
     {
@@ -4143,8 +4138,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     calculateTree.removeAll();
     // build the calculate menu
 
-    for (final String type : new String[]
-    { "NJ", "AV" })
+    for (final String type : new String[] { "NJ", "AV" })
     {
       String treecalcnm = MessageManager.getString("label.tree_calc_"
               + type.toLowerCase());
@@ -4152,7 +4146,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         JMenuItem tm = new JMenuItem();
         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
-        if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
+        if (sm.isDNA() == viewport.getAlignment().isNucleotide()
+                || sm.isProtein() == !viewport.getAlignment()
+                        .isNucleotide())
         {
           String smn = MessageManager.getStringOrReturn(
                   "label.score_model_", sm.getName());
@@ -4293,8 +4289,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
     if (!viewport.getAlignment().isAligned(false))
     {
-      seqs.setSequences(new SeqCigar[]
-      { seqs.getSequences()[0] });
+      seqs.setSequences(new SeqCigar[] { seqs.getSequences()[0] });
       // TODO: if seqs.getSequences().length>1 then should really have warned
       // user!
 
@@ -4466,22 +4461,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           // object broker mechanism.
           final Vector<JMenu> wsmenu = new Vector<JMenu>();
           final IProgressIndicator af = me;
+
+          /*
+           * do not i18n these strings - they are hard-coded in class
+           * compbio.data.msa.Category, Jws2Discoverer.isRecalculable() and
+           * SequenceAnnotationWSClient.initSequenceAnnotationWSClient()
+           */
           final JMenu msawsmenu = new JMenu("Alignment");
           final JMenu secstrmenu = new JMenu(
                   "Secondary Structure Prediction");
           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
           final JMenu analymenu = new JMenu("Analysis");
           final JMenu dismenu = new JMenu("Protein Disorder");
-          // final JMenu msawsmenu = new
-          // JMenu(MessageManager.getString("label.alignment"));
-          // final JMenu secstrmenu = new
-          // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
-          // final JMenu seqsrchmenu = new
-          // JMenu(MessageManager.getString("label.sequence_database_search"));
-          // final JMenu analymenu = new
-          // JMenu(MessageManager.getString("label.analysis"));
-          // final JMenu dismenu = new
-          // JMenu(MessageManager.getString("label.protein_disorder"));
           // JAL-940 - only show secondary structure prediction services from
           // the legacy server
           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
@@ -4640,81 +4631,39 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  /*
-   * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
-   * chooser = new JalviewFileChooser(jalview.bin.Cache.
-   * getProperty("LAST_DIRECTORY"));
-   * 
-   * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
-   * to Vamsas file"); chooser.setToolTipText("Export");
-   * 
-   * int value = chooser.showSaveDialog(this);
-   * 
-   * if (value == JalviewFileChooser.APPROVE_OPTION) {
-   * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
-   * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
-   * chooser.getSelectedFile().getAbsolutePath(), this); } }
-   */
-  /**
-   * prototype of an automatically enabled/disabled analysis function
-   * 
-   */
-  protected void setShowProductsEnabled()
-  {
-    SequenceI[] selection = viewport.getSequenceSelection();
-    if (canShowProducts(selection, viewport.getSelectionGroup() != null,
-            viewport.getAlignment().getDataset()))
-    {
-      showProducts.setEnabled(true);
-
-    }
-    else
-    {
-      showProducts.setEnabled(false);
-    }
-  }
-
   /**
-   * search selection for sequence xRef products and build the show products
-   * menu.
+   * Searches the alignment sequences for xRefs and builds the Show
+   * Cross-References menu (formerly called Show Products), with database
+   * sources for which cross-references are found (protein sources for a
+   * nucleotide alignment and vice versa)
    * 
-   * @param selection
-   * @param dataset
-   * @return true if showProducts menu should be enabled.
+   * @return true if Show Cross-references menu should be enabled
    */
-  public boolean canShowProducts(SequenceI[] selection,
-          boolean isRegionSelection, Alignment dataset)
+  public boolean canShowProducts()
   {
+    SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
+    AlignmentI dataset = viewport.getAlignment().getDataset();
     boolean showp = false;
     try
     {
       showProducts.removeAll();
       final boolean dna = viewport.getAlignment().isNucleotide();
-      final Alignment ds = dataset;
-      String[] ptypes = (selection == null || selection.length == 0) ? null
-              : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
-      // Object[] prods =
-      // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
-      // selection, dataset, true);
-      final SequenceI[] sel = selection;
-      for (int t = 0; ptypes != null && t < ptypes.length; t++)
+      List<String> ptypes = (seqs == null || seqs.length == 0) ? null
+              : new CrossRef(seqs, dataset)
+                      .findXrefSourcesForSequences(dna);
+
+      for (final String source : ptypes)
       {
         showp = true;
-        final boolean isRegSel = isRegionSelection;
         final AlignFrame af = this;
-        final String source = ptypes[t];
-        JMenuItem xtype = new JMenuItem(ptypes[t]);
+        JMenuItem xtype = new JMenuItem(source);
         xtype.addActionListener(new ActionListener()
         {
-
           @Override
           public void actionPerformed(ActionEvent e)
           {
-            // TODO: new thread for this call with vis-delay
-            af.showProductsFor(af.viewport.getSequenceSelection(),
-                    isRegSel, dna, source);
+            showProductsFor(af.viewport.getSequenceSelection(), dna, source);
           }
-
         });
         showProducts.add(xtype);
       }
@@ -4722,16 +4671,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       showProducts.setEnabled(showp);
     } catch (Exception e)
     {
-      jalview.bin.Cache.log
-              .warn("canTranslate threw an exception - please report to help@jalview.org",
+      Cache.log
+              .warn("canShowProducts threw an exception - please report to help@jalview.org",
                       e);
       return false;
     }
     return showp;
   }
 
+  /**
+   * Finds and displays cross-references for the selected sequences (protein
+   * products for nucleotide sequences, dna coding sequences for peptides).
+   * 
+   * @param sel
+   *          the sequences to show cross-references for
+   * @param dna
+   *          true if from a nucleotide alignment (so showing proteins)
+   * @param source
+   *          the database to show cross-references for
+   */
   protected void showProductsFor(final SequenceI[] sel,
-          final boolean isRegSel, final boolean dna, final String source)
+          final boolean _odna, final String source)
   {
     Runnable foo = new Runnable()
     {
@@ -4741,117 +4701,223 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         final long sttime = System.currentTimeMillis();
         AlignFrame.this.setProgressBar(MessageManager.formatMessage(
-                "status.searching_for_sequences_from", new Object[]
-                { source }), sttime);
+                "status.searching_for_sequences_from",
+                new Object[] { source }), sttime);
         try
         {
-          // update our local dataset reference
-          Alignment ds = AlignFrame.this.getViewport().getAlignment()
-                  .getDataset();
-          Alignment prods = CrossRef
-                  .findXrefSequences(sel, dna, source, ds);
-          if (prods != null)
+          AlignmentI alignment = AlignFrame.this.getViewport()
+                  .getAlignment();
+          AlignmentI dataset = alignment.getDataset() == null ? alignment
+                  : alignment.getDataset();
+          boolean dna = alignment.isNucleotide();
+          if (_odna != dna)
           {
-            SequenceI[] sprods = new SequenceI[prods.getHeight()];
-            for (int s = 0; s < sprods.length; s++)
+            System.err
+                    .println("Conflict: showProducts for alignment originally "
+                            + "thought to be "
+                            + (_odna ? "DNA" : "Protein")
+                            + " now searching for "
+                            + (dna ? "DNA" : "Protein") + " Context.");
+          }
+          AlignmentI xrefs = new CrossRef(sel, dataset).findXrefSequences(
+                  source, dna);
+          if (xrefs == null)
+          {
+            return;
+          }
+          /*
+           * get display scheme (if any) to apply to features
+           */
+          FeatureSettingsModelI featureColourScheme = new SequenceFetcher()
+                  .getFeatureColourScheme(source);
+
+          AlignmentI xrefsAlignment = makeCrossReferencesAlignment(dataset,
+                  xrefs);
+          if (!dna)
+          {
+            xrefsAlignment = AlignmentUtils.makeCdsAlignment(
+                    xrefsAlignment.getSequencesArray(), dataset, sel);
+            xrefsAlignment.alignAs(alignment);
+          }
+
+          /*
+           * If we are opening a splitframe, make a copy of this alignment (sharing the same dataset
+           * sequences). If we are DNA, drop introns and update mappings
+           */
+          AlignmentI copyAlignment = null;
+
+          if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
+          {
+            boolean copyAlignmentIsAligned = false;
+            if (dna)
             {
-              sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
-              if (ds.getSequences() == null
-                      || !ds.getSequences().contains(
-                              sprods[s].getDatasetSequence()))
+              copyAlignment = AlignmentUtils.makeCdsAlignment(sel, dataset,
+                      xrefsAlignment.getSequencesArray());
+              if (copyAlignment.getHeight() == 0)
+              {
+                JOptionPane.showMessageDialog(AlignFrame.this,
+                        MessageManager.getString("label.cant_map_cds"),
+                        MessageManager.getString("label.operation_failed"),
+                        JOptionPane.OK_OPTION);
+                System.err.println("Failed to make CDS alignment");
+              }
+
+              /*
+               * pending getting Embl transcripts to 'align', 
+               * we are only doing this for Ensembl
+               */
+              // TODO proper criteria for 'can align as cdna'
+              if (DBRefSource.ENSEMBL.equalsIgnoreCase(source)
+                      || AlignmentUtils.looksLikeEnsembl(alignment))
               {
-                ds.addSequence(sprods[s].getDatasetSequence());
+                copyAlignment.alignAs(alignment);
+                copyAlignmentIsAligned = true;
               }
-              sprods[s].updatePDBIds();
             }
-            Alignment al = new Alignment(sprods);
-            al.setDataset(ds);
+            else
+            {
+              copyAlignment = AlignmentUtils.makeCopyAlignment(sel,
+                      xrefs.getSequencesArray(), dataset);
+            }
+            copyAlignment.setGapCharacter(AlignFrame.this.viewport
+                    .getGapCharacter());
+
+            StructureSelectionManager ssm = StructureSelectionManager
+                    .getStructureSelectionManager(Desktop.instance);
 
             /*
-             * Copy dna-to-protein mappings to new alignment
+             * register any new mappings for sequence mouseover etc
+             * (will not duplicate any previously registered mappings)
              */
-            // TODO 1: no mappings are set up for EMBL product
-            // TODO 2: if they were, should add them to protein alignment, not
-            // dna
-            Set<AlignedCodonFrame> cf = prods.getCodonFrames();
-            for (AlignedCodonFrame acf : cf)
+            ssm.registerMappings(dataset.getCodonFrames());
+
+            if (copyAlignment.getHeight() <= 0)
             {
-              al.addCodonFrame(acf);
+              System.err.println("No Sequences generated for xRef type "
+                      + source);
+              return;
             }
-            AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
-                    DEFAULT_HEIGHT);
-            String newtitle = "" + ((dna) ? "Proteins" : "Nucleotides")
-                    + " for " + ((isRegSel) ? "selected region of " : "")
-                    + getTitle();
-            naf.setTitle(newtitle);
-
-            // temporary flag until SplitFrame is released
-            boolean asSplitFrame = Cache.getDefault(
-                    Preferences.ENABLE_SPLIT_FRAME, true);
-            if (asSplitFrame)
+            /*
+             * align protein to dna
+             */
+            if (dna && copyAlignmentIsAligned)
+            {
+              xrefsAlignment.alignAs(copyAlignment);
+            }
+            else
             {
               /*
-               * Make a copy of this alignment (sharing the same dataset
-               * sequences). If we are DNA, drop introns and update mappings
+               * align cdna to protein - currently only if 
+               * fetching and aligning Ensembl transcripts!
                */
-              AlignmentI copyAlignment = null;
-              final SequenceI[] sequenceSelection = AlignFrame.this.viewport
-                      .getSequenceSelection();
-              if (dna)
+              // TODO: generalise for other sources of locus/transcript/cds data
+              if (dna && DBRefSource.ENSEMBL.equalsIgnoreCase(source))
               {
-                copyAlignment = AlignmentUtils.makeExonAlignment(
-                        sequenceSelection, cf);
-                al.getCodonFrames().clear();
-                al.getCodonFrames().addAll(cf);
-                final StructureSelectionManager ssm = StructureSelectionManager
-                        .getStructureSelectionManager(Desktop.instance);
-                ssm.registerMappings(cf);
+                copyAlignment.alignAs(xrefsAlignment);
               }
-              else
-              {
-                copyAlignment = new Alignment(new Alignment(
-                        sequenceSelection));
-              }
-              AlignFrame copyThis = new AlignFrame(copyAlignment,
-                      AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
-              copyThis.setTitle(AlignFrame.this.getTitle());
-              // SplitFrame with dna above, protein below
-              SplitFrame sf = new SplitFrame(dna ? copyThis : naf,
-                      dna ? naf : copyThis);
-              naf.setVisible(true);
-              copyThis.setVisible(true);
-              String linkedTitle = MessageManager
-                      .getString("label.linked_view_title");
-              Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
-            }
-            else
-            {
-              Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
-                      DEFAULT_HEIGHT);
             }
           }
-          else
+          /*
+           * build AlignFrame(s) according to available alignment data
+           */
+          AlignFrame newFrame = new AlignFrame(xrefsAlignment,
+                  DEFAULT_WIDTH, DEFAULT_HEIGHT);
+          if (Cache.getDefault("HIDE_INTRONS", true))
           {
-            System.err.println("No Sequences generated for xRef type "
-                    + source);
+            newFrame.hideFeatureColumns(SequenceOntologyI.EXON, false);
           }
-        } catch (Exception e)
-        {
-          jalview.bin.Cache.log.error(
-                  "Exception when finding crossreferences", e);
+          String newtitle = String.format("%s %s %s",
+                  dna ? MessageManager.getString("label.proteins")
+                          : MessageManager.getString("label.nucleotides"),
+                  MessageManager.getString("label.for"), getTitle());
+          newFrame.setTitle(newtitle);
+
+          if (copyAlignment == null)
+          {
+            /*
+             * split frame display is turned off in preferences file
+             */
+            Desktop.addInternalFrame(newFrame, newtitle, DEFAULT_WIDTH,
+                    DEFAULT_HEIGHT);
+            return; // via finally clause
+          }
+          AlignFrame copyThis = new AlignFrame(copyAlignment,
+                  AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+          copyThis.setTitle(AlignFrame.this.getTitle());
+
+          boolean showSequenceFeatures = viewport.isShowSequenceFeatures();
+          newFrame.setShowSeqFeatures(showSequenceFeatures);
+          copyThis.setShowSeqFeatures(showSequenceFeatures);
+          FeatureRenderer myFeatureStyling = alignPanel.getSeqPanel().seqCanvas
+                  .getFeatureRenderer();
+
+          /*
+           * copy feature rendering settings to split frame
+           */
+          newFrame.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+                  .transferSettings(myFeatureStyling);
+          copyThis.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+                  .transferSettings(myFeatureStyling);
+
+          /*
+           * apply 'database source' feature configuration
+           * if any was found
+           */
+          // TODO is this the feature colouring for the original
+          // alignment or the fetched xrefs? either could be Ensembl
+          newFrame.getViewport().applyFeaturesStyle(featureColourScheme);
+          copyThis.getViewport().applyFeaturesStyle(featureColourScheme);
+
+          SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame,
+                  dna ? newFrame : copyThis);
+          newFrame.setVisible(true);
+          copyThis.setVisible(true);
+          String linkedTitle = MessageManager
+                  .getString("label.linked_view_title");
+          Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
+          sf.adjustDivider();
         } catch (OutOfMemoryError e)
         {
           new OOMWarning("whilst fetching crossreferences", e);
-        } catch (Error e)
+        } catch (Throwable e)
         {
-          jalview.bin.Cache.log.error("Error when finding crossreferences",
-                  e);
+          Cache.log.error("Error when finding crossreferences", e);
+        } finally
+        {
+          AlignFrame.this.setProgressBar(MessageManager.formatMessage(
+                  "status.finished_searching_for_sequences_from",
+                  new Object[] { source }), sttime);
         }
-        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
-                "status.finished_searching_for_sequences_from",
-                new Object[]
-                { source }),
-                sttime);
+      }
+
+      /**
+       * Makes an alignment containing the given sequences, and adds them to the
+       * given dataset, which is also set as the dataset for the new alignment
+       * 
+       * TODO: refactor to DatasetI method
+       * 
+       * @param dataset
+       * @param seqs
+       * @return
+       */
+      protected AlignmentI makeCrossReferencesAlignment(AlignmentI dataset,
+              AlignmentI seqs)
+      {
+        SequenceI[] sprods = new SequenceI[seqs.getHeight()];
+        for (int s = 0; s < sprods.length; s++)
+        {
+          sprods[s] = (seqs.getSequenceAt(s)).deriveSequence();
+          if (dataset.getSequences() == null
+                  || !dataset.getSequences().contains(
+                          sprods[s].getDatasetSequence()))
+          {
+            dataset.addSequence(sprods[s].getDatasetSequence());
+          }
+          sprods[s].updatePDBIds();
+        }
+        Alignment al = new Alignment(sprods);
+        al.setDataset(dataset);
+        return al;
       }
 
     };
@@ -4859,23 +4925,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     frunner.start();
   }
 
-  public boolean canShowTranslationProducts(SequenceI[] selection,
-          AlignmentI alignment)
-  {
-    // old way
-    try
-    {
-      return (jalview.analysis.Dna.canTranslate(selection,
-              viewport.getViewAsVisibleContigs(true)));
-    } catch (Exception e)
-    {
-      jalview.bin.Cache.log
-              .warn("canTranslate threw an exception - please report to help@jalview.org",
-                      e);
-      return false;
-    }
-  }
-
   /**
    * Construct and display a new frame containing the translation of this
    * frame's DNA sequences to their aligned protein (amino acid) equivalents.
@@ -4897,7 +4946,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               .getString("label.error_when_translating_sequences_submit_bug_report");
       final String errorTitle = MessageManager
               .getString("label.implementation_error")
-              + MessageManager.getString("translation_failed");
+              + MessageManager.getString("label.translation_failed");
       JOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
               JOptionPane.ERROR_MESSAGE);
       return;
@@ -4916,8 +4965,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
       af.setFileFormat(this.currentFileFormat);
       final String newTitle = MessageManager.formatMessage(
-              "label.translation_of_params", new Object[]
-              { this.getTitle() });
+              "label.translation_of_params",
+              new Object[] { this.getTitle() });
       af.setTitle(newTitle);
       if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
       {
@@ -4955,7 +5004,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     return avc.parseFeaturesFile(file, type,
             jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
-    
+
   }
 
   @Override
@@ -4970,8 +5019,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       showSeqFeatures.setSelected(true);
     }
 
-
   }
+
   @Override
   public void dragEnter(DropTargetDragEvent evt)
   {
@@ -4996,49 +5045,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void drop(DropTargetDropEvent evt)
   {
     Transferable t = evt.getTransferable();
-    java.util.List files = null;
+    java.util.List<String> files = new ArrayList<String>(), protocols = new ArrayList<String>();
 
     try
     {
-      DataFlavor uriListFlavor = new DataFlavor(
-              "text/uri-list;class=java.lang.String");
-      if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
-      {
-        // Works on Windows and MacOSX
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        files = (java.util.List) t
-                .getTransferData(DataFlavor.javaFileListFlavor);
-      }
-      else if (t.isDataFlavorSupported(uriListFlavor))
-      {
-        // This is used by Unix drag system
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        String data = (String) t.getTransferData(uriListFlavor);
-        files = new java.util.ArrayList(1);
-        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
-                data, "\r\n"); st.hasMoreTokens();)
-        {
-          String s = st.nextToken();
-          if (s.startsWith("#"))
-          {
-            // the line is a comment (as per the RFC 2483)
-            continue;
-          }
-
-          java.net.URI uri = new java.net.URI(s);
-          // check to see if we can handle this kind of URI
-          if (uri.getScheme().toLowerCase().startsWith("http"))
-          {
-            files.add(uri.toString());
-          }
-          else
-          {
-            // otherwise preserve old behaviour: catch all for file objects
-            java.io.File file = new java.io.File(uri);
-            files.add(file.toString());
-          }
-        }
-      }
+      Desktop.transferFromDropTarget(files, protocols, evt, t);
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -5093,7 +5104,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               String type = null;
               try
               {
-                type = new IdentifyFile().Identify(file, protocol);
+                type = new IdentifyFile().identify(file, protocol);
               } catch (Exception ex)
               {
                 type = null;
@@ -5102,8 +5113,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               {
                 if (type.equalsIgnoreCase("PDB"))
                 {
-                  filesmatched.add(new Object[]
-                  { file, protocol, mtch });
+                  filesmatched.add(new Object[] { file, protocol, mtch });
                   continue;
                 }
               }
@@ -5122,10 +5132,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                                   MessageManager
                                           .formatMessage(
                                                   "label.automatically_associate_pdb_files_with_sequences_same_name",
-                                                  new Object[]
-                                                  { Integer.valueOf(
-                                                          filesmatched
-                                                                  .size())
+                                                  new Object[] { Integer
+                                                          .valueOf(
+                                                                  filesmatched
+                                                                          .size())
                                                           .toString() }),
                                   MessageManager
                                           .getString("label.automatically_associate_pdb_files_by_name"),
@@ -5162,14 +5172,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
                           .showConfirmDialog(
                                   this,
-                                  "<html>"+MessageManager
-                                          .formatMessage(
-                                                  "label.ignore_unmatched_dropped_files_info",
-                                                  new Object[]
-                                                  { Integer.valueOf(
-                                                          filesnotmatched
-                                                                  .size())
-                                                          .toString() })+"</html>",
+                                  "<html>"
+                                          + MessageManager
+                                                  .formatMessage(
+                                                          "label.ignore_unmatched_dropped_files_info",
+                                                          new Object[] { Integer
+                                                                  .valueOf(
+                                                                          filesnotmatched
+                                                                                  .size())
+                                                                  .toString() })
+                                          + "</html>",
                                   MessageManager
                                           .getString("label.ignore_unmatched_dropped_files"),
                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
@@ -5191,7 +5203,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   /**
    * 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
+   * it's an 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.
    * 
@@ -5205,15 +5217,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       if (protocol == null)
       {
-        protocol = jalview.io.FormatAdapter.checkProtocol(file);
+        protocol = FormatAdapter.checkProtocol(file);
       }
       // if the file isn't identified, or not positively identified as some
       // other filetype (PFAM is default unidentified alignment file type) then
       // try to parse as annotation.
       boolean isAnnotation = (format == null || format
               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
-              .annotateAlignmentView(viewport, file, protocol)
-              : false;
+              .annotateAlignmentView(viewport, file, protocol) : false;
 
       if (!isAnnotation)
       {
@@ -5267,7 +5278,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           // try to parse it as a features file
           if (format == null)
           {
-            format = new IdentifyFile().Identify(file, protocol);
+            format = new IdentifyFile().identify(file, protocol);
           }
           if (format.equalsIgnoreCase("JnetFile"))
           {
@@ -5283,42 +5294,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             viewport.setColumnSelection(cs);
             isAnnotation = true;
           }
-          else
+          else if (IdentifyFile.FeaturesFile.equals(format))
           {
-            /*
-             * if (format.equalsIgnoreCase("PDB")) {
-             * 
-             * String pdbfn = ""; // try to match up filename with sequence id
-             * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
-             * new File(file); pdbfn = fl.getName(); } else if (protocol ==
-             * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
-             * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
-             * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
-             * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
-             * // attempt to find a match in the alignment SequenceI mtch =
-             * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
-             * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
-             * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
-             * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
-             * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
-             * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
-             * { System.err.println("Associated file : " + file + " with " +
-             * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
-             * TODO: maybe need to load as normal otherwise return; } }
-             */
-            // try to parse it as a features file
-            boolean isGroupsFile = parseFeaturesFile(file, protocol);
-            // if it wasn't a features file then we just treat it as a general
-            // alignment file to load into the current view.
-            if (!isGroupsFile)
-            {
-              new FileLoader().LoadFile(viewport, file, protocol, format);
-            }
-            else
+            if (parseFeaturesFile(file, protocol))
             {
               alignPanel.paintAlignment(true);
             }
           }
+          else
+          {
+            new FileLoader().LoadFile(viewport, file, protocol, format);
+          }
         }
       }
       if (isAnnotation)
@@ -5340,7 +5326,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       } catch (Exception x)
       {
       }
-      ;
       new OOMWarning(
               "loading data "
                       + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
@@ -5390,7 +5375,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void tabbedPane_mousePressed(MouseEvent e)
   {
-    if (SwingUtilities.isRightMouseButton(e))
+    if (e.isPopupTrigger())
     {
       String msg = MessageManager.getString("label.enter_view_name");
       String reply = JOptionPane.showInternalInputDialog(this, msg, msg,
@@ -5523,13 +5508,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         new Thread(new Runnable()
         {
-
           @Override
           public void run()
           {
-            new jalview.ws.DBRefFetcher(alignPanel.av
-                    .getSequenceSelection(), alignPanel.alignFrame)
-                    .fetchDBRefs(false);
+            boolean isNucleotide = alignPanel.alignFrame.getViewport()
+                    .getAlignment().isNucleotide();
+            DBRefFetcher dbRefFetcher = new DBRefFetcher(alignPanel.av
+                    .getSequenceSelection(), alignPanel.alignFrame, null,
+                    alignPanel.alignFrame.featureSettings, isNucleotide);
+            dbRefFetcher.addListener(new FetchFinishedListenerI()
+            {
+              @Override
+              public void finished()
+              {
+                AlignFrame.this.setMenusForViewport();
+              }
+            });
+            dbRefFetcher.fetchDBRefs(false);
           }
         }).start();
 
@@ -5543,7 +5538,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       @Override
       public void run()
       {
-        final jalview.ws.SequenceFetcher sf = SequenceFetcher
+        final jalview.ws.SequenceFetcher sf = jalview.gui.SequenceFetcher
                 .getSequenceFetcherSingleton(me);
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
@@ -5597,16 +5592,33 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       @Override
                       public void run()
                       {
-                        new jalview.ws.DBRefFetcher(alignPanel.av
-                                .getSequenceSelection(),
-                                alignPanel.alignFrame, dassource)
-                                .fetchDBRefs(false);
+                        boolean isNucleotide = alignPanel.alignFrame
+                                .getViewport().getAlignment()
+                                .isNucleotide();
+                        DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                alignPanel.av.getSequenceSelection(),
+                                alignPanel.alignFrame, dassource,
+                                alignPanel.alignFrame.featureSettings,
+                                isNucleotide);
+                        dbRefFetcher
+                                .addListener(new FetchFinishedListenerI()
+                                {
+                                  @Override
+                                  public void finished()
+                                  {
+                                    AlignFrame.this.setMenusForViewport();
+                                  }
+                                });
+                        dbRefFetcher.fetchDBRefs(false);
                       }
                     }).start();
                   }
 
                 });
-                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{src.getDbName()})));
+                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true,
+                        MessageManager.formatMessage(
+                                "label.fetch_retrieve_from",
+                                new Object[] { src.getDbName() })));
                 dfetch.add(fetchr);
                 comp++;
               }
@@ -5617,8 +5629,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 // fetch all entry
                 DbSourceProxy src = otherdb.get(0);
                 fetchr = new JMenuItem(MessageManager.formatMessage(
-                        "label.fetch_all_param", new Object[]
-                        { src.getDbSource() }));
+                        "label.fetch_all_param",
+                        new Object[] { src.getDbSource() }));
                 fetchr.addActionListener(new ActionListener()
                 {
                   @Override
@@ -5630,20 +5642,42 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       @Override
                       public void run()
                       {
-                        new jalview.ws.DBRefFetcher(alignPanel.av
-                                .getSequenceSelection(),
-                                alignPanel.alignFrame, dassource)
-                                .fetchDBRefs(false);
+                        boolean isNucleotide = alignPanel.alignFrame
+                                .getViewport().getAlignment()
+                                .isNucleotide();
+                        DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                alignPanel.av.getSequenceSelection(),
+                                alignPanel.alignFrame, dassource,
+                                alignPanel.alignFrame.featureSettings,
+                                isNucleotide);
+                        dbRefFetcher
+                                .addListener(new FetchFinishedListenerI()
+                                {
+                                  @Override
+                                  public void finished()
+                                  {
+                                    AlignFrame.this.setMenusForViewport();
+                                  }
+                                });
+                        dbRefFetcher.fetchDBRefs(false);
                       }
                     }).start();
                   }
                 });
 
-                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new Object[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
+                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true,
+                        MessageManager.formatMessage(
+                                "label.fetch_retrieve_from_all_sources",
+                                new Object[] {
+                                    Integer.valueOf(otherdb.size())
+                                            .toString(), src.getDbSource(),
+                                    src.getDbName() })));
                 dfetch.add(fetchr);
                 comp++;
                 // and then build the rest of the individual menus
-                ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new Object[]{src.getDbSource()}));
+                ifetch = new JMenu(MessageManager.formatMessage(
+                        "label.source_from_db_source",
+                        new Object[] { src.getDbSource() }));
                 icomp = 0;
                 String imname = null;
                 int i = 0;
@@ -5656,11 +5690,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                           0, 10) + "..." : dbname;
                   if (imname == null)
                   {
-                    imname = MessageManager.formatMessage("label.from_msname", new Object[]{sname});
+                    imname = MessageManager.formatMessage(
+                            "label.from_msname", new Object[] { sname });
                   }
                   fetchr = new JMenuItem(msname);
-                  final DbSourceProxy[] dassrc =
-                  { sproxy };
+                  final DbSourceProxy[] dassrc = { sproxy };
                   fetchr.addActionListener(new ActionListener()
                   {
 
@@ -5673,17 +5707,33 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         @Override
                         public void run()
                         {
-                          new jalview.ws.DBRefFetcher(alignPanel.av
-                                  .getSequenceSelection(),
-                                  alignPanel.alignFrame, dassrc)
-                                  .fetchDBRefs(false);
+                          boolean isNucleotide = alignPanel.alignFrame
+                                  .getViewport().getAlignment()
+                                  .isNucleotide();
+                          DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                  alignPanel.av.getSequenceSelection(),
+                                  alignPanel.alignFrame, dassrc,
+                                  alignPanel.alignFrame.featureSettings,
+                                  isNucleotide);
+                          dbRefFetcher
+                                  .addListener(new FetchFinishedListenerI()
+                                  {
+                                    @Override
+                                    public void finished()
+                                    {
+                                      AlignFrame.this.setMenusForViewport();
+                                    }
+                                  });
+                          dbRefFetcher.fetchDBRefs(false);
                         }
                       }).start();
                     }
 
                   });
                   fetchr.setToolTipText("<html>"
-                          + MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{dbname}));
+                          + MessageManager.formatMessage(
+                                  "label.fetch_retrieve_from", new Object[]
+                                  { dbname }));
                   ifetch.add(fetchr);
                   ++i;
                   if (++icomp >= mcomp || i == (otherdb.size()))
@@ -5738,6 +5788,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.firePropertyChange("alignment", null, al);
   }
 
+  @Override
   public void setShowSeqFeatures(boolean b)
   {
     showSeqFeatures.setSelected(b);
@@ -5847,10 +5898,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       alignPanel.paintAlignment(true);
     }
   }
+
   public void clearAlignmentSeqRep()
   {
     // TODO refactor alignmentseqrep to controller
-    if (viewport.getAlignment().hasSeqrep()) {
+    if (viewport.getAlignment().hasSeqrep())
+    {
       viewport.getAlignment().setSeqrep(null);
       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
       alignPanel.updateAnnotation();
@@ -5886,7 +5939,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (!viewport.getSequenceSetId().equals(
             alignmentPanel.av.getSequenceSetId()))
     {
-      throw new Error(MessageManager.getString("error.implementation_error_cannot_show_view_alignment_frame"));
+      throw new Error(
+              MessageManager
+                      .getString("error.implementation_error_cannot_show_view_alignment_frame"));
     }
     if (tabbedPane != null
             && tabbedPane.getTabCount() > 0
@@ -5910,8 +5965,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void setAnnotationsVisibility(boolean visible,
           boolean forSequences, boolean forAlignment)
   {
-    for (AlignmentAnnotation aa : alignPanel.getAlignment()
-            .getAlignmentAnnotation())
+    AlignmentAnnotation[] anns = alignPanel.getAlignment()
+            .getAlignmentAnnotation();
+    if (anns == null)
+    {
+      return;
+    }
+    for (AlignmentAnnotation aa : anns)
     {
       /*
        * don't display non-positional annotations on an alignment
@@ -5949,8 +6009,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   public List<? extends AlignmentViewPanel> getAlignPanels()
   {
-    return alignPanels == null ? Arrays.asList(alignPanel)
-            : alignPanels;
+    return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
   }
 
   /**
@@ -5961,14 +6020,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     // TODO no longer a menu action - refactor as required
     final AlignmentI alignment = getViewport().getAlignment();
-    Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
+    List<AlignedCodonFrame> mappings = alignment.getCodonFrames();
     if (mappings == null)
     {
       return;
     }
     List<SequenceI> cdnaSeqs = new ArrayList<SequenceI>();
-    for (SequenceI aaSeq : alignment.getSequences()) {
-      for (AlignedCodonFrame acf : mappings) {
+    for (SequenceI aaSeq : alignment.getSequences())
+    {
+      for (AlignedCodonFrame acf : mappings)
+      {
         SequenceI dnaSeq = acf.getDnaForAaSeq(aaSeq.getDatasetSequence());
         if (dnaSeq != null)
         {
@@ -5996,8 +6057,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     String newtitle = "cDNA " + MessageManager.getString("label.for") + " "
             + this.title;
     Desktop.addInternalFrame(alignFrame, newtitle,
-            AlignFrame.DEFAULT_WIDTH,
-            AlignFrame.DEFAULT_HEIGHT);
+            AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
   }
 
   /**
@@ -6010,10 +6070,92 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void showComplement_actionPerformed(boolean show)
   {
     SplitContainerI sf = getSplitViewContainer();
-    if (sf != null) {
+    if (sf != null)
+    {
       sf.setComplementVisible(this, show);
     }
   }
+
+  /**
+   * Generate the reverse (optionally complemented) of the selected sequences,
+   * and add them to the alignment
+   */
+  @Override
+  protected void showReverse_actionPerformed(boolean complement)
+  {
+    AlignmentI al = null;
+    try
+    {
+      Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
+      al = dna.reverseCdna(complement);
+      viewport.addAlignment(al, "");
+      addHistoryItem(new EditCommand(
+              MessageManager.getString("label.add_sequences"),
+              Action.PASTE, al.getSequencesArray(), 0, al.getWidth(),
+              viewport.getAlignment()));
+    } catch (Exception ex)
+    {
+      System.err.println(ex.getMessage());
+      return;
+    }
+  }
+
+  /**
+   * Try to run a script in the Groovy console, having first ensured that this
+   * AlignFrame is set as currentAlignFrame in Desktop, to allow the script to
+   * be targeted at this alignment.
+   */
+  @Override
+  protected void runGroovy_actionPerformed()
+  {
+    Jalview.setCurrentAlignFrame(this);
+    groovy.ui.Console console = Desktop.getGroovyConsole();
+    if (console != null)
+    {
+      try
+      {
+        console.runScript();
+      } catch (Exception ex)
+      {
+        System.err.println((ex.toString()));
+        JOptionPane
+                .showInternalMessageDialog(Desktop.desktop, MessageManager
+                        .getString("label.couldnt_run_groovy_script"),
+                        MessageManager
+                                .getString("label.groovy_support_failed"),
+                        JOptionPane.ERROR_MESSAGE);
+      }
+    }
+    else
+    {
+      System.err.println("Can't run Groovy script as console not found");
+    }
+  }
+
+  /**
+   * Hides columns containing (or not containing) a specified feature, provided
+   * that would not leave all columns hidden
+   * 
+   * @param featureType
+   * @param columnsContaining
+   * @return
+   */
+  public boolean hideFeatureColumns(String featureType,
+          boolean columnsContaining)
+  {
+    boolean notForHiding = avc.markColumnsContainingFeatures(
+            columnsContaining, false, false, featureType);
+    if (notForHiding)
+    {
+      if (avc.markColumnsContainingFeatures(!columnsContaining, false,
+              false, featureType))
+      {
+        getViewport().hideSelectedColumns();
+        return true;
+      }
+    }
+    return false;
+  }
 }
 
 class PrintThread extends Thread