JAL-3725 restrict mapped virtual feature location to mapped region
[jalview.git] / src / jalview / gui / PopupMenu.java
index 3bc7c5c..abe9835 100644 (file)
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
- * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * 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.
+ * 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/>.
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.gui;
 
-import java.util.*;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import javax.swing.*;
-
-import jalview.analysis.*;
-import jalview.commands.*;
-import jalview.datamodel.*;
-import jalview.io.*;
-import jalview.schemes.*;
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.Vector;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JColorChooser;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JRadioButtonMenuItem;
+
+import jalview.analysis.AAFrequency;
+import jalview.analysis.AlignmentAnnotationUtils;
+import jalview.analysis.AlignmentUtils;
+import jalview.analysis.Conservation;
+import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
+import jalview.commands.ChangeCaseCommand;
+import jalview.commands.EditCommand;
+import jalview.commands.EditCommand.Action;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.MappedFeatures;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.ColourMenuHelper.ColourChangeListener;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
+import jalview.io.FormatAdapter;
+import jalview.io.SequenceAnnotationReport;
+import jalview.schemes.Blosum62ColourScheme;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemes;
+import jalview.schemes.PIDColourScheme;
+import jalview.schemes.ResidueColourScheme;
+import jalview.util.Comparison;
 import jalview.util.GroupUrlLink;
 import jalview.util.GroupUrlLink.UrlStringTooLongException;
+import jalview.util.MessageManager;
+import jalview.util.StringUtils;
 import jalview.util.UrlLink;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
 /**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision: 1.118 $
+ * The popup menu that is displayed on right-click on a sequence id, or in the
+ * sequence alignment.
  */
-public class PopupMenu extends JPopupMenu
+public class PopupMenu extends JPopupMenu implements ColourChangeListener
 {
-  JMenu groupMenu = new JMenu();
-
-  JMenuItem groupName = new JMenuItem();
-
-  protected JRadioButtonMenuItem clustalColour = new JRadioButtonMenuItem();
-
-  protected JRadioButtonMenuItem zappoColour = new JRadioButtonMenuItem();
-
-  protected JRadioButtonMenuItem taylorColour = new JRadioButtonMenuItem();
+  /*
+   * maximum length of feature description to include in popup menu item text
+   */
+  private static final int FEATURE_DESC_MAX = 40;
 
-  protected JRadioButtonMenuItem hydrophobicityColour = new JRadioButtonMenuItem();
+  /*
+   * true for ID Panel menu, false for alignment panel menu
+   */
+  private final boolean forIdPanel;
 
-  protected JRadioButtonMenuItem helixColour = new JRadioButtonMenuItem();
+  private final AlignmentPanel ap;
 
-  protected JRadioButtonMenuItem strandColour = new JRadioButtonMenuItem();
+  /*
+   * the sequence under the cursor when clicked
+   * (additional sequences may be selected)
+   */
+  private final SequenceI sequence;
 
-  protected JRadioButtonMenuItem turnColour = new JRadioButtonMenuItem();
+  JMenu groupMenu = new JMenu();
 
-  protected JRadioButtonMenuItem buriedColour = new JRadioButtonMenuItem();
+  JMenuItem groupName = new JMenuItem();
 
   protected JCheckBoxMenuItem abovePIDColour = new JCheckBoxMenuItem();
 
-  protected JRadioButtonMenuItem userDefinedColour = new JRadioButtonMenuItem();
-
-  protected JRadioButtonMenuItem PIDColour = new JRadioButtonMenuItem();
-
-  protected JRadioButtonMenuItem BLOSUM62Colour = new JRadioButtonMenuItem();
-
-  protected JRadioButtonMenuItem purinePyrimidineColour = new JRadioButtonMenuItem();
-
-  // protected JRadioButtonMenuItem covariationColour = new
-  // JRadioButtonMenuItem();
-
-  JRadioButtonMenuItem noColourmenuItem = new JRadioButtonMenuItem();
+  protected JMenuItem modifyPID = new JMenuItem();
 
   protected JCheckBoxMenuItem conservationMenuItem = new JCheckBoxMenuItem();
 
-  AlignmentPanel ap;
-
-  JMenu sequenceMenu = new JMenu();
+  protected JRadioButtonMenuItem annotationColour;
 
-  JMenuItem sequenceName = new JMenuItem();
+  protected JMenuItem modifyConservation = new JMenuItem();
 
-  JMenuItem sequenceDetails = new JMenuItem();
+  JMenu sequenceMenu = new JMenu();
 
-  JMenuItem sequenceSelDetails = new JMenuItem();
+  JMenuItem makeReferenceSeq = new JMenuItem();
 
-  SequenceI sequence;
   JMenuItem createGroupMenuItem = new JMenuItem();
-  JMenuItem unGroupMenuItem = new JMenuItem();
 
-  JMenuItem outline = new JMenuItem();
-
-  JRadioButtonMenuItem nucleotideMenuItem = new JRadioButtonMenuItem();
+  JMenuItem unGroupMenuItem = new JMenuItem();
 
   JMenu colourMenu = new JMenu();
 
@@ -108,99 +139,277 @@ public class PopupMenu extends JPopupMenu
 
   JMenu editMenu = new JMenu();
 
-  JMenuItem cut = new JMenuItem();
-
-  JMenuItem copy = new JMenuItem();
-
   JMenuItem upperCase = new JMenuItem();
 
   JMenuItem lowerCase = new JMenuItem();
 
   JMenuItem toggle = new JMenuItem();
 
-  JMenu pdbMenu = new JMenu();
+  JMenu outputMenu = new JMenu();
 
-  JMenuItem pdbFromFile = new JMenuItem();
+  JMenu seqShowAnnotationsMenu = new JMenu();
 
-  JMenuItem enterPDB = new JMenuItem();
+  JMenu seqHideAnnotationsMenu = new JMenu();
 
-  JMenuItem discoverPDB = new JMenuItem();
+  JMenuItem seqAddReferenceAnnotations = new JMenuItem(
+          MessageManager.getString("label.add_reference_annotations"));
 
-  JMenu outputMenu = new JMenu();
+  JMenu groupShowAnnotationsMenu = new JMenu();
+
+  JMenu groupHideAnnotationsMenu = new JMenu();
 
-  JMenuItem sequenceFeature = new JMenuItem();
+  JMenuItem groupAddReferenceAnnotations = new JMenuItem(
+          MessageManager.getString("label.add_reference_annotations"));
 
   JMenuItem textColour = new JMenuItem();
 
-  JMenu jMenu1 = new JMenu();
+  JMenu editGroupMenu = new JMenu();
+
+  JMenuItem chooseStructure = new JMenuItem();
+
+  JMenu rnaStructureMenu = new JMenu();
+
+  /**
+   * Constructs a menu with sub-menu items for any hyperlinks for the sequence
+   * and/or features provided. Hyperlinks may include a lookup by sequence id,
+   * or database cross-references, depending on which links are enabled in user
+   * preferences.
+   * 
+   * @param seq
+   * @param features
+   * @return
+   */
+  protected static JMenu buildLinkMenu(final SequenceI seq,
+          List<SequenceFeature> features)
+  {
+    JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
+
+    List<String> nlinks = null;
+    if (seq != null)
+    {
+      nlinks = Preferences.sequenceUrlLinks.getLinksForMenu();
+      UrlLink.sort(nlinks);
+    }
+    else
+    {
+      nlinks = new ArrayList<>();
+    }
+
+    if (features != null)
+    {
+      for (SequenceFeature sf : features)
+      {
+        if (sf.links != null)
+        {
+          for (String link : sf.links)
+          {
+            nlinks.add(link);
+          }
+        }
+      }
+    }
+
+    /*
+     * instantiate the hyperlinklink templates from sequence data;
+     * note the order of the templates is preserved in the map
+     */
+    Map<String, List<String>> linkset = new LinkedHashMap<>();
+    for (String link : nlinks)
+    {
+      UrlLink urlLink = null;
+      try
+      {
+        urlLink = new UrlLink(link);
+      } catch (Exception foo)
+      {
+        Cache.log.error("Exception for URLLink '" + link + "'", foo);
+        continue;
+      }
 
-  JMenu structureMenu = new JMenu();
+      if (!urlLink.isValid())
+      {
+        Cache.log.error(urlLink.getInvalidMessage());
+        continue;
+      }
 
-  JMenu viewStructureMenu = new JMenu();
+      urlLink.createLinksFromSeq(seq, linkset);
+    }
 
-  // JMenu colStructureMenu = new JMenu();
-  JMenuItem editSequence = new JMenuItem();
+    /*
+     * construct menu items for the hyperlinks (still preserving
+     * the order of the sorted templates)
+     */
+    addUrlLinks(linkMenu, linkset.values());
 
-  // JMenuItem annotationMenuItem = new JMenuItem();
+    return linkMenu;
+  }
 
-  JMenu groupLinksMenu;
+  /**
+   * A helper method that builds menu items from the given links, with action
+   * handlers to open the link URL, and adds them to the linkMenu. Each provided
+   * link should be a list whose second item is the menu text, and whose fourth
+   * item is the URL to open when the menu item is selected.
+   * 
+   * @param linkMenu
+   * @param linkset
+   */
+  static private void addUrlLinks(JMenu linkMenu,
+          Collection<List<String>> linkset)
+  {
+    for (List<String> linkstrset : linkset)
+    {
+      final String url = linkstrset.get(3);
+      JMenuItem item = new JMenuItem(linkstrset.get(1));
+      item.setToolTipText(MessageManager
+              .formatMessage("label.open_url_param", new Object[]
+              { url }));
+      item.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          new Thread(new Runnable()
+          {
+            @Override
+            public void run()
+            {
+              showLink(url);
+            }
+          }).start();
+        }
+      });
+      linkMenu.add(item);
+    }
+  }
 
   /**
-   * Creates a new PopupMenu object.
+   * Opens the provided url in the default web browser, or shows an error
+   * message if this fails
+   * 
+   * @param url
+   */
+  static void showLink(String url)
+  {
+    try
+    {
+      jalview.util.BrowserLauncher.openURL(url);
+    } catch (Exception ex)
+    {
+      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+              MessageManager.getString("label.web_browser_not_found_unix"),
+              MessageManager.getString("label.web_browser_not_found"),
+              JvOptionPane.WARNING_MESSAGE);
+
+      ex.printStackTrace();
+    }
+  }
+
+  /**
+   * add a late bound groupURL item to the given linkMenu
+   * 
+   * @param linkMenu
+   * @param label
+   *          - menu label string
+   * @param urlgenerator
+   *          GroupURLLink used to generate URL
+   * @param urlstub
+   *          Object array returned from the makeUrlStubs function.
+   */
+  static void addshowLink(JMenu linkMenu, String label,
+          final GroupUrlLink urlgenerator, final Object[] urlstub)
+  {
+    JMenuItem item = new JMenuItem(label);
+    item.setToolTipText(MessageManager
+            .formatMessage("label.open_url_seqs_param", new Object[]
+            { urlgenerator.getUrl_prefix(),
+                urlgenerator.getNumberInvolved(urlstub) }));
+    // TODO: put in info about what is being sent.
+    item.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        new Thread(new Runnable()
+        {
+
+          @Override
+          public void run()
+          {
+            try
+            {
+              showLink(urlgenerator.constructFrom(urlstub));
+            } catch (UrlStringTooLongException e2)
+            {
+            }
+          }
+
+        }).start();
+      }
+    });
+
+    linkMenu.add(item);
+  }
+
+  /**
+   * Constructor for a PopupMenu for a click in the alignment panel (on a residue)
    * 
    * @param ap
-   *          DOCUMENT ME!
+   *              the panel in which the mouse is clicked
    * @param seq
-   *          DOCUMENT ME!
+   *              the sequence under the mouse
+   * @throws NullPointerException
+   *                                if seq is null
    */
-  public PopupMenu(final AlignmentPanel ap, Sequence seq, Vector links)
+  public PopupMenu(final AlignmentPanel ap, SequenceI seq, int column)
   {
-    this(ap, seq, links, null);
+    this(false, ap, seq, column, null);
   }
 
   /**
+   * Constructor for a PopupMenu for a click in the sequence id panel
    * 
-   * @param ap
+   * @param alignPanel
+   *                     the panel in which the mouse is clicked
    * @param seq
-   * @param links
+   *                     the sequence under the mouse click
    * @param groupLinks
+   *                     templates for sequence external links
+   * @throws NullPointerException
+   *                                if seq is null
    */
-  public PopupMenu(final AlignmentPanel ap, final SequenceI seq,
-          Vector links, Vector groupLinks)
+  public PopupMenu(final AlignmentPanel alignPanel, final SequenceI seq,
+          List<String> groupLinks)
   {
-    // /////////////////////////////////////////////////////////
-    // If this is activated from the sequence panel, the user may want to
-    // edit or annotate a particular residue. Therefore display the residue menu
-    //
-    // If from the IDPanel, we must display the sequence menu
-    // ////////////////////////////////////////////////////////
-    this.ap = ap;
+    this(true, alignPanel, seq, -1, groupLinks);
+  }
+
+  /**
+   * Private constructor that constructs a popup menu for either sequence ID
+   * Panel, or alignment context
+   * 
+   * @param fromIdPanel
+   * @param alignPanel
+   * @param seq
+   * @param column
+   *                      aligned column position (0...)
+   * @param groupLinks
+   */
+  private PopupMenu(boolean fromIdPanel,
+          final AlignmentPanel alignPanel,
+          final SequenceI seq, final int column, List<String> groupLinks)
+  {
+    Objects.requireNonNull(seq);
+    this.forIdPanel = fromIdPanel;
+    this.ap = alignPanel;
     sequence = seq;
 
-    ButtonGroup colours = new ButtonGroup();
-    colours.add(noColourmenuItem);
-    colours.add(clustalColour);
-    colours.add(zappoColour);
-    colours.add(taylorColour);
-    colours.add(hydrophobicityColour);
-    colours.add(helixColour);
-    colours.add(strandColour);
-    colours.add(turnColour);
-    colours.add(buriedColour);
-    colours.add(abovePIDColour);
-    colours.add(userDefinedColour);
-    colours.add(PIDColour);
-    colours.add(BLOSUM62Colour);
-    colours.add(purinePyrimidineColour);
-    // colours.add(covariationColour);
-
-    for (int i = 0; i < jalview.io.FormatAdapter.WRITEABLE_FORMATS.length; i++)
-    {
-      JMenuItem item = new JMenuItem(
-              jalview.io.FormatAdapter.WRITEABLE_FORMATS[i]);
-
-      item.addActionListener(new java.awt.event.ActionListener()
+    for (String ff : FileFormats.getInstance().getWritableFormats(true))
+    {
+      JMenuItem item = new JMenuItem(ff);
+
+      item.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           outputText_actionPerformed(e);
@@ -210,6 +419,30 @@ public class PopupMenu extends JPopupMenu
       outputMenu.add(item);
     }
 
+    /*
+     * Build menus for annotation types that may be shown or hidden, and for
+     * 'reference annotations' that may be added to the alignment. First for the
+     * currently selected sequence (if there is one):
+     */
+    final List<SequenceI> selectedSequence = (forIdPanel && seq != null
+            ? Arrays.asList(seq)
+            : Collections.<SequenceI> emptyList());
+    buildAnnotationTypesMenus(seqShowAnnotationsMenu,
+            seqHideAnnotationsMenu, selectedSequence);
+    configureReferenceAnnotationsMenu(seqAddReferenceAnnotations,
+            selectedSequence);
+
+    /*
+     * And repeat for the current selection group (if there is one):
+     */
+    final List<SequenceI> selectedGroup = (alignPanel.av.getSelectionGroup() == null
+            ? Collections.<SequenceI> emptyList()
+            : alignPanel.av.getSelectionGroup().getSequences());
+    buildAnnotationTypesMenus(groupShowAnnotationsMenu,
+            groupHideAnnotationsMenu, selectedGroup);
+    configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
+            selectedGroup);
+
     try
     {
       jbInit();
@@ -218,113 +451,99 @@ public class PopupMenu extends JPopupMenu
       e.printStackTrace();
     }
 
-    JMenuItem menuItem;
-    if (seq != null)
+    if (forIdPanel)
     {
+      JMenuItem menuItem;
       sequenceMenu.setText(sequence.getName());
-
-      if (seq.getDatasetSequence().getPDBId() != null
-              && seq.getDatasetSequence().getPDBId().size() > 0)
+      if (seq == alignPanel.av.getAlignment().getSeqrep())
       {
-        java.util.Enumeration e = seq.getDatasetSequence().getPDBId()
-                .elements();
-
-        while (e.hasMoreElements())
-        {
-          final PDBEntry pdb = (PDBEntry) e.nextElement();
-
-          menuItem = new JMenuItem();
-          menuItem.setText(pdb.getId());
-          menuItem.addActionListener(new java.awt.event.ActionListener()
-          {
-            public void actionPerformed(ActionEvent e)
-            {
-              // TODO re JAL-860: optionally open dialog or provide a menu entry
-              // allowing user to open just one structure per sequence
-              new AppJmol(pdb, ap.av.collateForPDB(new PDBEntry[]
-              { pdb })[0], null, ap);
-              // new PDBViewer(pdb, seqs2, null, ap, AppletFormatAdapter.FILE);
-            }
-
-          });
-          viewStructureMenu.add(menuItem);
-
-          /*
-           * menuItem = new JMenuItem(); menuItem.setText(pdb.getId());
-           * menuItem.addActionListener(new java.awt.event.ActionListener() {
-           * public void actionPerformed(ActionEvent e) {
-           * colourByStructure(pdb.getId()); } });
-           * colStructureMenu.add(menuItem);
-           */
-        }
+        makeReferenceSeq.setText(
+                MessageManager.getString("action.unmark_as_reference"));
       }
       else
       {
-        if (ap.av.getAlignment().isNucleotide() == false)
-        {
-          structureMenu.remove(viewStructureMenu);
-        }
-        // structureMenu.remove(colStructureMenu);
+        makeReferenceSeq.setText(
+                MessageManager.getString("action.set_as_reference"));
       }
 
-      if (ap.av.getAlignment().isNucleotide() == true)
+      if (!alignPanel.av.getAlignment().isNucleotide())
+      {
+        remove(rnaStructureMenu);
+      }
+      else
       {
-        AlignmentAnnotation[] aa = ap.av.getAlignment()
+        int origCount = rnaStructureMenu.getItemCount();
+        /*
+         * add menu items to 2D-render any alignment or sequence secondary
+         * structure annotation
+         */
+        AlignmentAnnotation[] aas = alignPanel.av.getAlignment()
                 .getAlignmentAnnotation();
-        for (int i = 0; i < aa.length; i++)
+        if (aas != null)
         {
-          if (aa[i].getRNAStruc() != null)
+          for (final AlignmentAnnotation aa : aas)
           {
-            final String rnastruc = aa[i].getRNAStruc();
-            final String structureLine = aa[i].label;
-            menuItem = new JMenuItem();
-            menuItem.setText("2D RNA " + structureLine);
-            menuItem.addActionListener(new java.awt.event.ActionListener()
+            if (aa.isValidStruc() && aa.sequenceRef == null)
             {
-              public void actionPerformed(ActionEvent e)
+              /*
+               * valid alignment RNA secondary structure annotation
+               */
+              menuItem = new JMenuItem();
+              menuItem.setText(MessageManager.formatMessage(
+                      "label.2d_rna_structure_line", new Object[]
+                      { aa.label }));
+              menuItem.addActionListener(new ActionListener()
               {
-                new AppVarna(structureLine, seq, seq.getSequenceAsString(),
-                        rnastruc, seq.getName(), ap);
-              }
-            });
-            viewStructureMenu.add(menuItem);
+                @Override
+                public void actionPerformed(ActionEvent e)
+                {
+                  new AppVarna(seq, aa, alignPanel);
+                }
+              });
+              rnaStructureMenu.add(menuItem);
+            }
           }
         }
 
-        // SequenceFeatures[] test = seq.getSequenceFeatures();
-
         if (seq.getAnnotation() != null)
         {
-          AlignmentAnnotation seqAnno[] = seq.getAnnotation();
-          for (int i = 0; i < seqAnno.length; i++)
+          AlignmentAnnotation seqAnns[] = seq.getAnnotation();
+          for (final AlignmentAnnotation aa : seqAnns)
           {
-            if (seqAnno[i].getRNAStruc() != null)
+            if (aa.isValidStruc())
             {
-              final String rnastruc = seqAnno[i].getRNAStruc();
-
+              /*
+               * valid sequence RNA secondary structure annotation
+               */
               // TODO: make rnastrucF a bit more nice
               menuItem = new JMenuItem();
-              menuItem.setText("2D RNA - " + seq.getName());
-              menuItem.addActionListener(new java.awt.event.ActionListener()
+              menuItem.setText(MessageManager.formatMessage(
+                      "label.2d_rna_sequence_name", new Object[]
+                      { seq.getName() }));
+              menuItem.addActionListener(new ActionListener()
               {
+                @Override
                 public void actionPerformed(ActionEvent e)
                 {
                   // TODO: VARNA does'nt print gaps in the sequence
-                  new AppVarna(seq.getName() + " structure", seq, seq
-                          .getSequenceAsString(), rnastruc, seq.getName(),
-                          ap);
+                  new AppVarna(seq, aa, alignPanel);
                 }
               });
-              viewStructureMenu.add(menuItem);
+              rnaStructureMenu.add(menuItem);
             }
           }
         }
-
+        if (rnaStructureMenu.getItemCount() == origCount)
+        {
+          remove(rnaStructureMenu);
+        }
       }
 
-      menuItem = new JMenuItem("Hide Sequences");
-      menuItem.addActionListener(new java.awt.event.ActionListener()
+      menuItem = new JMenuItem(
+              MessageManager.getString("action.hide_sequences"));
+      menuItem.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           hideSequences(false);
@@ -332,12 +551,15 @@ public class PopupMenu extends JPopupMenu
       });
       add(menuItem);
 
-      if (ap.av.getSelectionGroup() != null
-              && ap.av.getSelectionGroup().getSize() > 1)
+      if (alignPanel.av.getSelectionGroup() != null
+              && alignPanel.av.getSelectionGroup().getSize() > 1)
       {
-        menuItem = new JMenuItem("Represent Group with " + seq.getName());
-        menuItem.addActionListener(new java.awt.event.ActionListener()
+        menuItem = new JMenuItem(MessageManager
+                .formatMessage("label.represent_group_with", new Object[]
+                { seq.getName() }));
+        menuItem.addActionListener(new ActionListener()
         {
+          @Override
           public void actionPerformed(ActionEvent e)
           {
             hideSequences(true);
@@ -346,22 +568,24 @@ public class PopupMenu extends JPopupMenu
         sequenceMenu.add(menuItem);
       }
 
-      if (ap.av.hasHiddenRows())
+      if (alignPanel.av.hasHiddenRows())
       {
-        final int index = ap.av.getAlignment().findIndex(seq);
+        final int index = alignPanel.av.getAlignment().findIndex(seq);
 
-        if (ap.av.adjustForHiddenSeqs(index)
-                - ap.av.adjustForHiddenSeqs(index - 1) > 1)
+        if (alignPanel.av.adjustForHiddenSeqs(index)
+                - alignPanel.av.adjustForHiddenSeqs(index - 1) > 1)
         {
-          menuItem = new JMenuItem("Reveal Sequences");
+          menuItem = new JMenuItem(
+                  MessageManager.getString("action.reveal_sequences"));
           menuItem.addActionListener(new ActionListener()
           {
+            @Override
             public void actionPerformed(ActionEvent e)
             {
-              ap.av.showSequence(index);
-              if (ap.overviewPanel != null)
+              alignPanel.av.showSequence(index);
+              if (alignPanel.overviewPanel != null)
               {
-                ap.overviewPanel.updateOverviewImage();
+                alignPanel.overviewPanel.updateOverviewImage();
               }
             }
           });
@@ -369,97 +593,66 @@ public class PopupMenu extends JPopupMenu
         }
       }
     }
-    // for the case when no sequences are even visible
-    if (ap.av.hasHiddenRows())
+
+    /*
+     * offer 'Reveal All'
+     * - in the IdPanel (seq not null) if any sequence is hidden
+     * - in the IdPanel or SeqPanel if all sequences are hidden (seq is null)
+     */
+    if (alignPanel.av.hasHiddenRows())
     {
+      boolean addOption = seq != null;
+      if (!addOption && alignPanel.av.getAlignment().getHeight() == 0)
+      {
+        addOption = true;
+      }
+      if (addOption)
       {
-        menuItem = new JMenuItem("Reveal All");
+        JMenuItem menuItem = new JMenuItem(
+                MessageManager.getString("action.reveal_all"));
         menuItem.addActionListener(new ActionListener()
         {
+          @Override
           public void actionPerformed(ActionEvent e)
           {
-            ap.av.showAllHiddenSeqs();
-            if (ap.overviewPanel != null)
+            alignPanel.av.showAllHiddenSeqs();
+            if (alignPanel.overviewPanel != null)
             {
-              ap.overviewPanel.updateOverviewImage();
+              alignPanel.overviewPanel.updateOverviewImage();
             }
           }
         });
-
         add(menuItem);
       }
-
     }
 
-    SequenceGroup sg = ap.av.getSelectionGroup();
-    boolean isDefinedGroup = (sg!=null) ? ap.av.getAlignment().getGroups().contains(sg) : false;
+    SequenceGroup sg = alignPanel.av.getSelectionGroup();
+    boolean isDefinedGroup = (sg != null)
+            ? alignPanel.av.getAlignment().getGroups().contains(sg)
+            : false;
 
     if (sg != null && sg.getSize() > 0)
-    {      
-      groupName.setText("Name: " + sg.getName());
-      groupName.setText("Edit name and description of current group.");
+    {
+      groupName.setText(MessageManager
+              .getString("label.edit_name_and_description_current_group"));
 
-      if (sg.cs instanceof ZappoColourScheme)
-      {
-        zappoColour.setSelected(true);
-      }
-      else if (sg.cs instanceof TaylorColourScheme)
-      {
-        taylorColour.setSelected(true);
-      }
-      else if (sg.cs instanceof PIDColourScheme)
-      {
-        PIDColour.setSelected(true);
-      }
-      else if (sg.cs instanceof Blosum62ColourScheme)
-      {
-        BLOSUM62Colour.setSelected(true);
-      }
-      else if (sg.cs instanceof UserColourScheme)
-      {
-        userDefinedColour.setSelected(true);
-      }
-      else if (sg.cs instanceof HydrophobicColourScheme)
-      {
-        hydrophobicityColour.setSelected(true);
-      }
-      else if (sg.cs instanceof HelixColourScheme)
-      {
-        helixColour.setSelected(true);
-      }
-      else if (sg.cs instanceof StrandColourScheme)
-      {
-        strandColour.setSelected(true);
-      }
-      else if (sg.cs instanceof TurnColourScheme)
-      {
-        turnColour.setSelected(true);
-      }
-      else if (sg.cs instanceof BuriedColourScheme)
-      {
-        buriedColour.setSelected(true);
-      }
-      else if (sg.cs instanceof ClustalxColourScheme)
-      {
-        clustalColour.setSelected(true);
-      }
-      else if (sg.cs instanceof PurinePyrimidineColourScheme)
-      {
-        purinePyrimidineColour.setSelected(true);
-      }
-      /*
-       * else if (sg.cs instanceof CovariationColourScheme) {
-       * covariationColour.setSelected(true); }
-       */
-      else
-      {
-        noColourmenuItem.setSelected(true);
-      }
+      ColourMenuHelper.setColourSelected(colourMenu, sg.getColourScheme());
 
-      if (sg.cs != null && sg.cs.conservationApplied())
+      conservationMenuItem.setEnabled(!sg.isNucleotide());
+
+      if (sg.cs != null)
       {
-        conservationMenuItem.setSelected(true);
+        if (sg.cs.conservationApplied())
+        {
+          conservationMenuItem.setSelected(true);
+        }
+        if (sg.cs.getThreshold() > 0)
+        {
+          abovePIDColour.setSelected(true);
+        }
       }
+      modifyConservation.setEnabled(conservationMenuItem.isSelected());
+      modifyPID.setEnabled(abovePIDColour.isSelected());
       displayNonconserved.setSelected(sg.getShowNonconserved());
       showText.setSelected(sg.getDisplayText());
       showColourText.setSelected(sg.getColourText());
@@ -470,14 +663,15 @@ public class PopupMenu extends JPopupMenu
         buildGroupURLMenu(sg, groupLinks);
       }
       // Add a 'show all structures' for the current selection
-      Hashtable<String, PDBEntry> pdbe = new Hashtable<String, PDBEntry>();
+      Hashtable<String, PDBEntry> pdbe = new Hashtable<>(), reppdb = new Hashtable<>();
+
       SequenceI sqass = null;
-      for (SequenceI sq : ap.av.getSequenceSelection())
+      for (SequenceI sq : alignPanel.av.getSequenceSelection())
       {
-        Vector<PDBEntry> pes = (Vector<PDBEntry>) sq.getDatasetSequence()
-                .getPDBId();
-        if (pes != null)
+        Vector<PDBEntry> pes = sq.getDatasetSequence().getAllPDBEntries();
+        if (pes != null && pes.size() > 0)
         {
+          reppdb.put(pes.get(0).getId(), pes.get(0));
           for (PDBEntry pe : pes)
           {
             pdbe.put(pe.getId(), pe);
@@ -490,29 +684,10 @@ public class PopupMenu extends JPopupMenu
       }
       if (pdbe.size() > 0)
       {
-        final PDBEntry[] pe = pdbe.values().toArray(
-                new PDBEntry[pdbe.size()]);
-        final JMenuItem gpdbview;
-        if (pdbe.size() == 1)
-        {
-          structureMenu.add(gpdbview = new JMenuItem("View structure for "
-                  + sqass.getDisplayId(false)));
-        }
-        else
-        {
-          structureMenu.add(gpdbview = new JMenuItem("View all "
-                  + pdbe.size() + " structures."));
-        }
-        gpdbview.setToolTipText("Open a new Jmol view with all structures associated with the current selection and superimpose them using the alignment.");
-        gpdbview.addActionListener(new ActionListener()
-        {
-
-          @Override
-          public void actionPerformed(ActionEvent e)
-          {
-            new AppJmol(ap, pe, ap.av.collateForPDB(pe));
-          }
-        });
+        final PDBEntry[] pe = pdbe.values()
+                .toArray(new PDBEntry[pdbe.size()]),
+                pr = reppdb.values().toArray(new PDBEntry[reppdb.size()]);
+        final JMenuItem gpdbview, rpdbview;
       }
     }
     else
@@ -525,162 +700,397 @@ public class PopupMenu extends JPopupMenu
     {
       createGroupMenuItem.setVisible(true);
       unGroupMenuItem.setVisible(false);
-      jMenu1.setText("Edit New Group");
-    } else {
+      editGroupMenu.setText(MessageManager.getString("action.edit_new_group"));
+    }
+    else
+    {
       createGroupMenuItem.setVisible(false);
       unGroupMenuItem.setVisible(true);
-      jMenu1.setText("Edit Group");
+      editGroupMenu.setText(MessageManager.getString("action.edit_group"));
     }
 
-    if (seq == null)
+    if (!forIdPanel)
     {
       sequenceMenu.setVisible(false);
-      structureMenu.setVisible(false);
+      chooseStructure.setVisible(false);
+      rnaStructureMenu.setVisible(false);
+    }
+
+    addLinksAndFeatures(seq, column);
+  }
+
+  /**
+   * Adds
+   * <ul>
+   * <li>configured sequence database links (ID panel popup menu)</li>
+   * <li>non-positional feature links (ID panel popup menu)</li>
+   * <li>positional feature links (alignment panel popup menu)</li>
+   * <li>feature details links (alignment panel popup menu)</li>
+   * </ul>
+   * If this panel is also showed complementary (CDS/protein) features, then links
+   * to their feature details are also added.
+   * 
+   * @param seq
+   * @param column
+   */
+  void addLinksAndFeatures(final SequenceI seq, final int column)
+  {
+    List<SequenceFeature> features = null;
+    if (forIdPanel)
+    {
+      features = sequence.getFeatures().getNonPositionalFeatures();
+    }
+    else
+    {
+      features = ap.getFeatureRenderer().findFeaturesAtColumn(sequence,
+              column + 1);
     }
 
-    if (links != null && links.size() > 0)
+    addLinks(seq, features);
+
+    if (!forIdPanel)
     {
+      addFeatureDetails(features, seq, column);
+    }
+  }
 
-      JMenu linkMenu = new JMenu("Link");
-      Vector linkset = new Vector();
-      for (int i = 0; i < links.size(); i++)
+  /**
+   * Add a menu item to show feature details for each sequence feature. Any
+   * linked 'virtual' features (CDS/protein) are also optionally found and
+   * included.
+   * 
+   * @param features
+   * @param seq
+   * @param column
+   */
+  protected void addFeatureDetails(List<SequenceFeature> features,
+          final SequenceI seq, final int column)
+  {
+    /*
+     * add features in CDS/protein complement at the corresponding
+     * position if configured to do so
+     */
+    MappedFeatures mf = null;
+    if (ap.av.isShowComplementFeatures())
+    {
+      if (!Comparison.isGap(sequence.getCharAt(column)))
       {
-        String link = links.elementAt(i).toString();
-        UrlLink urlLink = null;
-        try
-        {
-          urlLink = new UrlLink(link);
-        } catch (Exception foo)
-        {
-          jalview.bin.Cache.log.error("Exception for URLLink '" + link
-                  + "'", foo);
-          continue;
-        }
-        ;
-        if (!urlLink.isValid())
-        {
-          jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
-          continue;
-        }
-        final String label = urlLink.getLabel();
-        if (seq != null && urlLink.isDynamic())
-        {
+        AlignViewportI complement = ap.getAlignViewport()
+                .getCodingComplement();
+        AlignFrame af = Desktop.getAlignFrameFor(complement);
+        FeatureRendererModel fr2 = af.getFeatureRenderer();
+        int seqPos = sequence.findPosition(column);
+        mf = fr2.findComplementFeaturesAtResidue(sequence, seqPos);
+      }
+    }
 
-          // collect matching db-refs
-          DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(
-                  seq.getDBRef(), new String[]
-                  { urlLink.getTarget() });
-          // collect id string too
-          String id = seq.getName();
-          String descr = seq.getDescription();
-          if (descr != null && descr.length() < 1)
-          {
-            descr = null;
-          }
+    if (features.isEmpty() && mf == null)
+    {
+      /*
+       * no features to show at this position
+       */
+      return;
+    }
 
-          if (dbr != null)
-          {
-            for (int r = 0; r < dbr.length; r++)
-            {
-              if (id != null && dbr[r].getAccessionId().equals(id))
-              {
-                // suppress duplicate link creation for the bare sequence ID
-                // string with this link
-                id = null;
-              }
-              // create Bare ID link for this RUL
-              String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(),
-                      true);
-              if (urls != null)
-              {
-                for (int u = 0; u < urls.length; u += 2)
-                {
-                  if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
-                  {
-                    linkset.addElement(urls[u] + "|" + urls[u + 1]);
-                    addshowLink(linkMenu, label + "|" + urls[u],
-                            urls[u + 1]);
-                  }
-                }
-              }
-            }
-          }
-          if (id != null)
-          {
-            // create Bare ID link for this RUL
-            String[] urls = urlLink.makeUrls(id, true);
-            if (urls != null)
-            {
-              for (int u = 0; u < urls.length; u += 2)
-              {
-                if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
-                {
-                  linkset.addElement(urls[u] + "|" + urls[u + 1]);
-                  addshowLink(linkMenu, label, urls[u + 1]);
-                }
-              }
-            }
-          }
-          // Create urls from description but only for URL links which are regex
-          // links
-          if (descr != null && urlLink.getRegexReplace() != null)
-          {
-            // create link for this URL from description where regex matches
-            String[] urls = urlLink.makeUrls(descr, true);
-            if (urls != null)
-            {
-              for (int u = 0; u < urls.length; u += 2)
-              {
-                if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
-                {
-                  linkset.addElement(urls[u] + "|" + urls[u + 1]);
-                  addshowLink(linkMenu, label, urls[u + 1]);
-                }
-              }
-            }
-          }
-        }
-        else
-        {
-          if (!linkset.contains(label + "|" + urlLink.getUrl_prefix()))
-          {
-            linkset.addElement(label + "|" + urlLink.getUrl_prefix());
-            // Add a non-dynamic link
-            addshowLink(linkMenu, label, urlLink.getUrl_prefix());
-          }
-        }
+    JMenu details = new JMenu(
+            MessageManager.getString("label.feature_details"));
+    add(details);
+
+    String name = seq.getName();
+    for (final SequenceFeature sf : features)
+    {
+      addFeatureDetailsMenuItem(details, name, sf, null);
+    }
+
+    if (mf != null)
+    {
+      for (final SequenceFeature sf : mf.features)
+      {
+        addFeatureDetailsMenuItem(details, name, sf, mf);
       }
-      if (sequence != null)
+    }
+  }
+
+  /**
+   * A helper method to add one menu item whose action is to show details for
+   * one feature. The menu text includes feature description, but this may be
+   * truncated.
+   * 
+   * @param details
+   * @param seqName
+   * @param sf
+   * @param mf
+   */
+  void addFeatureDetailsMenuItem(JMenu details, final String seqName,
+          final SequenceFeature sf, MappedFeatures mf)
+  {
+    int start = sf.getBegin();
+    int end = sf.getEnd();
+    if (mf != null)
+    {
+      /*
+       * show local rather than linked feature coordinates
+       */
+      int[] beginRange = mf.getMappedPositions(start, start);
+      int[] endRange = mf.getMappedPositions(end, end);
+      if (beginRange == null || endRange == null)
+      {
+        // e.g. variant extending to stop codon so not mappable
+        return;
+      }
+      start = beginRange[0];
+      end = endRange[endRange.length - 1];
+      int[] localRange = mf.getMappedPositions(start, end);
+      if (localRange == null)
+      {
+        return;
+      }
+      start = localRange[0];
+      end = localRange[localRange.length - 1];
+    }
+    StringBuilder desc = new StringBuilder();
+    desc.append(sf.getType()).append(" ").append(String.valueOf(start));
+    if (start != end)
+    {
+      desc.append(sf.isContactFeature() ? ":" : "-");
+      desc.append(String.valueOf(end));
+    }
+    String description = sf.getDescription();
+    if (description != null)
+    {
+      desc.append(" ");
+      description = StringUtils.stripHtmlTags(description);
+
+      /*
+       * truncate overlong descriptions unless they contain an href
+       * (as truncation could leave corrupted html)
+       */
+      boolean hasLink = description.indexOf("a href") > -1;
+      if (description.length() > FEATURE_DESC_MAX && !hasLink)
+      {
+        description = description.substring(0, FEATURE_DESC_MAX) + "...";
+      }
+      desc.append(description);
+    }
+    String featureGroup = sf.getFeatureGroup();
+    if (featureGroup != null)
+    {
+      desc.append(" (").append(featureGroup).append(")");
+    }
+    String htmlText = JvSwingUtils.wrapTooltip(true, desc.toString());
+    JMenuItem item = new JMenuItem(htmlText);
+    item.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        showFeatureDetails(sf, seqName, mf);
+      }
+    });
+    details.add(item);
+  }
+
+  /**
+   * Opens a panel showing a text report of feature details
+   * 
+   * @param sf
+   * @param seqName
+   * @param mf
+   */
+  protected void showFeatureDetails(SequenceFeature sf, String seqName,
+          MappedFeatures mf)
+  {
+    CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
+    // it appears Java's CSS does not support border-collapse :-(
+    cap.addStylesheetRule("table { border-collapse: collapse;}");
+    cap.addStylesheetRule("table, td, th {border: 1px solid black;}");
+    cap.setText(sf.getDetailsReport(seqName, mf));
+
+    Desktop.addInternalFrame(cap,
+            MessageManager.getString("label.feature_details"), 500, 500);
+  }
+
+  /**
+   * Adds a 'Link' menu item with a sub-menu item for each hyperlink provided.
+   * When seq is not null, these are links for the sequence id, which may be to
+   * external web sites for the sequence accession, and/or links embedded in
+   * non-positional features. When seq is null, only links embedded in the
+   * provided features are added. If no links are found, the menu is not added.
+   * 
+   * @param seq
+   * @param features
+   */
+  void addLinks(final SequenceI seq, List<SequenceFeature> features)
+  {
+    JMenu linkMenu = buildLinkMenu(forIdPanel ? seq : null, features);
+
+    // only add link menu if it has entries
+    if (linkMenu.getItemCount() > 0)
+    {
+      if (forIdPanel)
       {
         sequenceMenu.add(linkMenu);
       }
       else
       {
-        add(linkMenu);
+        add(linkMenu);
+      }
+    }
+  }
+
+  /**
+   * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
+   * "All" is added first, followed by a separator. Then add any annotation
+   * types associated with the current selection. Separate menus are built for
+   * the selected sequence group (if any), and the selected sequence.
+   * <p>
+   * Some annotation rows are always rendered together - these can be identified
+   * by a common graphGroup property > -1. Only one of each group will be marked
+   * as visible (to avoid duplication of the display). For such groups we add a
+   * composite type name, e.g.
+   * <p>
+   * IUPredWS (Long), IUPredWS (Short)
+   * 
+   * @param seq
+   */
+  protected void buildAnnotationTypesMenus(JMenu showMenu, JMenu hideMenu,
+          List<SequenceI> forSequences)
+  {
+    showMenu.removeAll();
+    hideMenu.removeAll();
+
+    final List<String> all = Arrays
+            .asList(new String[]
+            { MessageManager.getString("label.all") });
+    addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true,
+            true);
+    addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
+            false);
+    showMenu.addSeparator();
+    hideMenu.addSeparator();
+
+    final AlignmentAnnotation[] annotations = ap.getAlignment()
+            .getAlignmentAnnotation();
+
+    /*
+     * Find shown/hidden annotations types, distinguished by source (calcId),
+     * and grouped by graphGroup. Using LinkedHashMap means we will retrieve in
+     * the insertion order, which is the order of the annotations on the
+     * alignment.
+     */
+    Map<String, List<List<String>>> shownTypes = new LinkedHashMap<>();
+    Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<>();
+    AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
+            AlignmentAnnotationUtils.asList(annotations), forSequences);
+
+    for (String calcId : hiddenTypes.keySet())
+    {
+      for (List<String> type : hiddenTypes.get(calcId))
+      {
+        addAnnotationTypeToShowHide(showMenu, forSequences, calcId, type,
+                false, true);
+      }
+    }
+    // grey out 'show annotations' if none are hidden
+    showMenu.setEnabled(!hiddenTypes.isEmpty());
+
+    for (String calcId : shownTypes.keySet())
+    {
+      for (List<String> type : shownTypes.get(calcId))
+      {
+        addAnnotationTypeToShowHide(hideMenu, forSequences, calcId, type,
+                false, false);
+      }
+    }
+    // grey out 'hide annotations' if none are shown
+    hideMenu.setEnabled(!shownTypes.isEmpty());
+  }
+
+  /**
+   * Returns a list of sequences - either the current selection group (if there
+   * is one), else the specified single sequence.
+   * 
+   * @param seq
+   * @return
+   */
+  protected List<SequenceI> getSequenceScope(SequenceI seq)
+  {
+    List<SequenceI> forSequences = null;
+    final SequenceGroup selectionGroup = ap.av.getSelectionGroup();
+    if (selectionGroup != null && selectionGroup.getSize() > 0)
+    {
+      forSequences = selectionGroup.getSequences();
+    }
+    else
+    {
+      forSequences = seq == null ? Collections.<SequenceI> emptyList()
+              : Arrays.asList(seq);
+    }
+    return forSequences;
+  }
+
+  /**
+   * Add one annotation type to the 'Show Annotations' or 'Hide Annotations'
+   * menus.
+   * 
+   * @param showOrHideMenu
+   *          the menu to add to
+   * @param forSequences
+   *          the sequences whose annotations may be shown or hidden
+   * @param calcId
+   * @param types
+   *          the label to add
+   * @param allTypes
+   *          if true this is a special label meaning 'All'
+   * @param actionIsShow
+   *          if true, the select menu item action is to show the annotation
+   *          type, else hide
+   */
+  protected void addAnnotationTypeToShowHide(JMenu showOrHideMenu,
+          final List<SequenceI> forSequences, String calcId,
+          final List<String> types, final boolean allTypes,
+          final boolean actionIsShow)
+  {
+    String label = types.toString(); // [a, b, c]
+    label = label.substring(1, label.length() - 1); // a, b, c
+    final JMenuItem item = new JMenuItem(label);
+    item.setToolTipText(calcId);
+    item.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        AlignmentUtils.showOrHideSequenceAnnotations(ap.getAlignment(),
+                types, forSequences, allTypes, actionIsShow);
+        refresh();
       }
-    }
+    });
+    showOrHideMenu.add(item);
   }
 
-  private void buildGroupURLMenu(SequenceGroup sg, Vector groupLinks)
+  private void buildGroupURLMenu(SequenceGroup sg, List<String> groupLinks)
   {
 
     // TODO: usability: thread off the generation of group url content so root
     // menu appears asap
     // sequence only URLs
     // ID/regex match URLs
-    groupLinksMenu = new JMenu("Group Link");
-    JMenu[] linkMenus = new JMenu[]
-    { null, new JMenu("IDS"), new JMenu("Sequences"),
-        new JMenu("IDS and Sequences") }; // three types of url that might be
-                                          // created.
+    JMenu groupLinksMenu = new JMenu(
+            MessageManager.getString("action.group_link"));
+    // three types of url that might be created.
+    JMenu[] linkMenus = new JMenu[] { null,
+        new JMenu(MessageManager.getString("action.ids")),
+        new JMenu(MessageManager.getString("action.sequences")),
+        new JMenu(MessageManager.getString("action.ids_sequences")) };
+
     SequenceI[] seqs = ap.av.getSelectionAsNewSequence();
     String[][] idandseqs = GroupUrlLink.formStrings(seqs);
-    Hashtable commonDbrefs = new Hashtable();
+    Hashtable<String, Object[]> commonDbrefs = new Hashtable<>();
     for (int sq = 0; sq < seqs.length; sq++)
     {
 
-      int start = seqs[sq].findPosition(sg.getStartRes()), end = seqs[sq]
-              .findPosition(sg.getEndRes());
+      int start = seqs[sq].findPosition(sg.getStartRes()),
+              end = seqs[sq].findPosition(sg.getEndRes());
       // just collect ids from dataset sequence
       // TODO: check if IDs collected from selecton group intersects with the
       // current selection, too
@@ -689,18 +1099,17 @@ public class PopupMenu extends JPopupMenu
       {
         sqi = sqi.getDatasetSequence();
       }
-      DBRefEntry[] dbr = sqi.getDBRef();
+      DBRefEntry[] dbr = sqi.getDBRefs();
       if (dbr != null && dbr.length > 0)
       {
         for (int d = 0; d < dbr.length; d++)
         {
           String src = dbr[d].getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase();
-          Object[] sarray = (Object[]) commonDbrefs.get(src);
+          Object[] sarray = commonDbrefs.get(src);
           if (sarray == null)
           {
             sarray = new Object[2];
-            sarray[0] = new int[]
-            { 0 };
+            sarray[0] = new int[] { 0 };
             sarray[1] = new String[seqs.length];
 
             commonDbrefs.put(src, sarray);
@@ -708,8 +1117,8 @@ public class PopupMenu extends JPopupMenu
 
           if (((String[]) sarray[1])[sq] == null)
           {
-            if (!dbr[d].hasMap()
-                    || (dbr[d].getMap().locateMappedRange(start, end) != null))
+            if (!dbr[d].hasMap() || (dbr[d].getMap()
+                    .locateMappedRange(start, end) != null))
             {
               ((String[]) sarray[1])[sq] = dbr[d].getAccessionId();
               ((int[]) sarray[0])[0]++;
@@ -721,30 +1130,28 @@ public class PopupMenu extends JPopupMenu
     // now create group links for all distinct ID/sequence sets.
     boolean addMenu = false; // indicates if there are any group links to give
                              // to user
-    for (int i = 0; i < groupLinks.size(); i++)
+    for (String link : groupLinks)
     {
-      String link = groupLinks.elementAt(i).toString();
       GroupUrlLink urlLink = null;
       try
       {
         urlLink = new GroupUrlLink(link);
       } catch (Exception foo)
       {
-        jalview.bin.Cache.log.error("Exception for GroupURLLink '" + link
-                + "'", foo);
+        Cache.log.error("Exception for GroupURLLink '" + link + "'", foo);
         continue;
       }
       ;
       if (!urlLink.isValid())
       {
-        jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
+        Cache.log.error(urlLink.getInvalidMessage());
         continue;
       }
       final String label = urlLink.getLabel();
       boolean usingNames = false;
       // Now see which parts of the group apply for this URL
       String ltarget = urlLink.getTarget(); // jalview.util.DBRefUtils.getCanonicalName(urlLink.getTarget());
-      Object[] idset = (Object[]) commonDbrefs.get(ltarget.toUpperCase());
+      Object[] idset = commonDbrefs.get(ltarget.toUpperCase());
       String[] seqstr, ids; // input to makeUrl
       if (idset != null)
       {
@@ -781,20 +1188,20 @@ public class PopupMenu extends JPopupMenu
       if (urlset != null)
       {
         int type = urlLink.getGroupURLType() & 3;
-        // System.out.println(urlLink.getGroupURLType()
-        // +" "+((String[])urlset[3])[0]);
         // first two bits ofurlLink type bitfield are sequenceids and sequences
         // TODO: FUTURE: ensure the groupURL menu structure can be generalised
-        addshowLink(linkMenus[type], label
-                + (((type & 1) == 1) ? ("("
-                        + (usingNames ? "Names" : ltarget) + ")") : ""),
+        addshowLink(linkMenus[type],
+                label + (((type & 1) == 1)
+                        ? ("(" + (usingNames ? "Names" : ltarget) + ")")
+                        : ""),
                 urlLink, urlset);
         addMenu = true;
       }
     }
     if (addMenu)
     {
-      groupLinksMenu = new JMenu("Group Links");
+      groupLinksMenu = new JMenu(
+              MessageManager.getString("action.group_link"));
       for (int m = 0; m < linkMenus.length; m++)
       {
         if (linkMenus[m] != null
@@ -809,676 +1216,612 @@ public class PopupMenu extends JPopupMenu
   }
 
   /**
-   * add a show URL menu item to the given linkMenu
+   * DOCUMENT ME!
    * 
-   * @param linkMenu
-   * @param label
-   *          - menu label string
-   * @param url
-   *          - url to open
+   * @throws Exception
+   *           DOCUMENT ME!
    */
-  private void addshowLink(JMenu linkMenu, String label, final String url)
+  private void jbInit() throws Exception
   {
-    JMenuItem item = new JMenuItem(label);
-    item.setToolTipText("open URL: " + url);
-    item.addActionListener(new java.awt.event.ActionListener()
+    groupMenu.setText(MessageManager.getString("label.selection"));
+    groupName.setText(MessageManager.getString("label.name"));
+    groupName.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        new Thread(new Runnable()
-        {
-
-          public void run()
-          {
-            showLink(url);
-          }
-
-        }).start();
+        groupName_actionPerformed();
       }
     });
+    sequenceMenu.setText(MessageManager.getString("label.sequence"));
 
-    linkMenu.add(item);
-  }
-
-  /**
-   * add a late bound groupURL item to the given linkMenu
-   * 
-   * @param linkMenu
-   * @param label
-   *          - menu label string
-   * @param urlgenerator
-   *          GroupURLLink used to generate URL
-   * @param urlstub
-   *          Object array returned from the makeUrlStubs function.
-   */
-  private void addshowLink(JMenu linkMenu, String label,
-          final GroupUrlLink urlgenerator, final Object[] urlstub)
-  {
-    JMenuItem item = new JMenuItem(label);
-    item.setToolTipText("open URL (" + urlgenerator.getUrl_prefix()
-            + "..) (" + urlgenerator.getNumberInvolved(urlstub) + " seqs)"); // TODO:
-                                                                             // put
-                                                                             // in
-                                                                             // info
-                                                                             // about
-                                                                             // what
-                                                                             // is
-                                                                             // being
-                                                                             // sent.
-    item.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem sequenceName = new JMenuItem(
+            MessageManager.getString("label.edit_name_description"));
+    sequenceName.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        new Thread(new Runnable()
-        {
-
-          public void run()
-          {
-            try
-            {
-              showLink(urlgenerator.constructFrom(urlstub));
-            } catch (UrlStringTooLongException e)
-            {
-            }
-          }
-
-        }).start();
+        sequenceName_actionPerformed();
       }
     });
-
-    linkMenu.add(item);
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @throws Exception
-   *           DOCUMENT ME!
-   */
-  private void jbInit() throws Exception
-  {
-    groupMenu.setText("Group");
-    groupMenu.setText("Selection");
-    groupName.setText("Name");
-    groupName.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem chooseAnnotations = new JMenuItem(
+            MessageManager.getString("action.choose_annotations"));
+    chooseAnnotations.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        groupName_actionPerformed();
+        chooseAnnotations_actionPerformed(e);
       }
     });
-    sequenceMenu.setText("Sequence");
-    sequenceName.setText("Edit Name/Description");
-    sequenceName.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem sequenceDetails = new JMenuItem(
+            MessageManager.getString("label.sequence_details"));
+    sequenceDetails.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        sequenceName_actionPerformed();
+        createSequenceDetailsReport(new SequenceI[] { sequence });
       }
     });
-    sequenceDetails.setText("Sequence Details ...");
-    sequenceDetails.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem sequenceSelDetails = new JMenuItem(
+            MessageManager.getString("label.sequence_details"));
+    sequenceSelDetails.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        sequenceDetails_actionPerformed();
+        createSequenceDetailsReport(ap.av.getSequenceSelection());
       }
     });
-    sequenceSelDetails.setText("Sequence Details ...");
-    sequenceSelDetails
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              public void actionPerformed(ActionEvent e)
-              {
-                sequenceSelectionDetails_actionPerformed();
-              }
-            });
-    PIDColour.setFocusPainted(false);
-    unGroupMenuItem.setText("Remove Group");
-    unGroupMenuItem.addActionListener(new java.awt.event.ActionListener()
+
+    unGroupMenuItem
+            .setText(MessageManager.getString("action.remove_group"));
+    unGroupMenuItem.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         unGroupMenuItem_actionPerformed();
       }
     });
-    createGroupMenuItem.setText("Create Group");
-    createGroupMenuItem.addActionListener(new java.awt.event.ActionListener()
+    createGroupMenuItem
+            .setText(MessageManager.getString("action.create_group"));
+    createGroupMenuItem.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         createGroupMenuItem_actionPerformed();
       }
     });
 
-    outline.setText("Border colour");
-    outline.addActionListener(new java.awt.event.ActionListener()
+    JMenuItem outline = new JMenuItem(
+            MessageManager.getString("action.border_colour"));
+    outline.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         outline_actionPerformed();
       }
     });
-    nucleotideMenuItem.setText("Nucleotide");
-    nucleotideMenuItem.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        nucleotideMenuItem_actionPerformed();
-      }
-    });
-    colourMenu.setText("Group Colour");
-    showBoxes.setText("Boxes");
+    showBoxes.setText(MessageManager.getString("action.boxes"));
     showBoxes.setState(true);
     showBoxes.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         showBoxes_actionPerformed();
       }
     });
-    showText.setText("Text");
+    showText.setText(MessageManager.getString("action.text"));
     showText.setState(true);
     showText.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         showText_actionPerformed();
       }
     });
-    showColourText.setText("Colour Text");
+    showColourText.setText(MessageManager.getString("label.colour_text"));
     showColourText.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         showColourText_actionPerformed();
       }
     });
-    displayNonconserved.setText("Show Nonconserved");
+    displayNonconserved
+            .setText(MessageManager.getString("label.show_non_conserved"));
     displayNonconserved.setState(true);
     displayNonconserved.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         showNonconserved_actionPerformed();
       }
     });
-    editMenu.setText("Edit");
-    cut.setText("Cut");
+    editMenu.setText(MessageManager.getString("action.edit"));
+    JMenuItem cut = new JMenuItem(MessageManager.getString("action.cut"));
     cut.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         cut_actionPerformed();
       }
     });
-    upperCase.setText("To Upper Case");
+    upperCase.setText(MessageManager.getString("label.to_upper_case"));
     upperCase.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         changeCase(e);
       }
     });
-    copy.setText("Copy");
+    JMenuItem copy = new JMenuItem(MessageManager.getString("action.copy"));
     copy.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         copy_actionPerformed();
       }
     });
-    lowerCase.setText("To Lower Case");
+    lowerCase.setText(MessageManager.getString("label.to_lower_case"));
     lowerCase.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         changeCase(e);
       }
     });
-    toggle.setText("Toggle Case");
+    toggle.setText(MessageManager.getString("label.toggle_case"));
     toggle.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         changeCase(e);
       }
     });
-    pdbMenu.setText("Associate Structure with Sequence");
-    pdbFromFile.setText("From File");
-    pdbFromFile.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        pdbFromFile_actionPerformed();
-      }
-    });
-    enterPDB.setText("Enter PDB Id");
-    enterPDB.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        enterPDB_actionPerformed();
-      }
-    });
-    discoverPDB.setText("Discover PDB ids");
-    discoverPDB.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        discoverPDB_actionPerformed();
-      }
-    });
-    outputMenu.setText("Output to Textbox...");
-    sequenceFeature.setText("Create Sequence Feature");
+    outputMenu.setText(
+            MessageManager.getString("label.out_to_textbox") + "...");
+    seqShowAnnotationsMenu
+            .setText(MessageManager.getString("label.show_annotations"));
+    seqHideAnnotationsMenu
+            .setText(MessageManager.getString("label.hide_annotations"));
+    groupShowAnnotationsMenu
+            .setText(MessageManager.getString("label.show_annotations"));
+    groupHideAnnotationsMenu
+            .setText(MessageManager.getString("label.hide_annotations"));
+    JMenuItem sequenceFeature = new JMenuItem(
+            MessageManager.getString("label.create_sequence_feature"));
     sequenceFeature.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         sequenceFeature_actionPerformed();
       }
     });
-    textColour.setText("Text Colour");
-    textColour.addActionListener(new ActionListener()
+    editGroupMenu.setText(MessageManager.getString("label.group"));
+    chooseStructure.setText(
+            MessageManager.getString("label.show_pdbstruct_dialog"));
+    chooseStructure.addActionListener(new ActionListener()
     {
-      public void actionPerformed(ActionEvent e)
+      @Override
+      public void actionPerformed(ActionEvent actionEvent)
       {
-        textColour_actionPerformed();
+        SequenceI[] selectedSeqs = new SequenceI[] { sequence };
+        if (ap.av.getSelectionGroup() != null)
+        {
+          selectedSeqs = ap.av.getSequenceSelection();
+        }
+        new StructureChooser(selectedSeqs, sequence, ap);
       }
     });
-    jMenu1.setText("Group");
-    structureMenu.setText("Structure");
-    viewStructureMenu.setText("View Structure");
+
+    rnaStructureMenu
+            .setText(MessageManager.getString("label.view_rna_structure"));
+
     // colStructureMenu.setText("Colour By Structure");
-    editSequence.setText("Edit Sequence...");
+    JMenuItem editSequence = new JMenuItem(
+            MessageManager.getString("label.edit_sequence") + "...");
     editSequence.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         editSequence_actionPerformed(actionEvent);
       }
     });
+    makeReferenceSeq.setText(
+            MessageManager.getString("label.mark_as_representative"));
+    makeReferenceSeq.addActionListener(new ActionListener()
+    {
+
+      @Override
+      public void actionPerformed(ActionEvent actionEvent)
+      {
+        makeReferenceSeq_actionPerformed(actionEvent);
+
+      }
+    });
 
-    /*
-     * annotationMenuItem.setText("By Annotation");
-     * annotationMenuItem.addActionListener(new ActionListener() { public void
-     * actionPerformed(ActionEvent actionEvent) {
-     * annotationMenuItem_actionPerformed(actionEvent); } });
-     */
     groupMenu.add(sequenceSelDetails);
     add(groupMenu);
     add(sequenceMenu);
-    this.add(structureMenu);
+    add(rnaStructureMenu);
+    add(chooseStructure);
+    if (forIdPanel)
+    {
+      JMenuItem hideInsertions = new JMenuItem(
+              MessageManager.getString("label.hide_insertions"));
+      hideInsertions.addActionListener(new ActionListener()
+      {
+
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          hideInsertions_actionPerformed(e);
+        }
+      });
+      add(hideInsertions);
+    }
+    // annotations configuration panel suppressed for now
+    // groupMenu.add(chooseAnnotations);
+
+    /*
+     * Add show/hide annotations to the Sequence menu, and to the Selection menu
+     * (if a selection group is in force).
+     */
+    sequenceMenu.add(seqShowAnnotationsMenu);
+    sequenceMenu.add(seqHideAnnotationsMenu);
+    sequenceMenu.add(seqAddReferenceAnnotations);
+    groupMenu.add(groupShowAnnotationsMenu);
+    groupMenu.add(groupHideAnnotationsMenu);
+    groupMenu.add(groupAddReferenceAnnotations);
     groupMenu.add(editMenu);
     groupMenu.add(outputMenu);
     groupMenu.add(sequenceFeature);
     groupMenu.add(createGroupMenuItem);
     groupMenu.add(unGroupMenuItem);
-    groupMenu.add(jMenu1);
+    groupMenu.add(editGroupMenu);
     sequenceMenu.add(sequenceName);
     sequenceMenu.add(sequenceDetails);
-    colourMenu.add(textColour);
-    colourMenu.add(noColourmenuItem);
-    colourMenu.add(clustalColour);
-    colourMenu.add(BLOSUM62Colour);
-    colourMenu.add(PIDColour);
-    colourMenu.add(zappoColour);
-    colourMenu.add(taylorColour);
-    colourMenu.add(hydrophobicityColour);
-    colourMenu.add(helixColour);
-    colourMenu.add(strandColour);
-    colourMenu.add(turnColour);
-    colourMenu.add(buriedColour);
-    colourMenu.add(nucleotideMenuItem);
-    if (ap.getAlignment().isNucleotide())
-    {
-      colourMenu.add(purinePyrimidineColour);
-    }
-    // colourMenu.add(covariationColour);
-    colourMenu.add(userDefinedColour);
-
-    if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
-    {
-      java.util.Enumeration userColours = jalview.gui.UserDefinedColours
-              .getUserColourSchemes().keys();
+    sequenceMenu.add(makeReferenceSeq);
 
-      while (userColours.hasMoreElements())
-      {
-        JMenuItem item = new JMenuItem(userColours.nextElement().toString());
-        item.addActionListener(new ActionListener()
-        {
-          public void actionPerformed(ActionEvent evt)
-          {
-            userDefinedColour_actionPerformed(evt);
-          }
-        });
-        colourMenu.add(item);
-      }
-    }
+    initColourMenu();
+    buildColourMenu();
 
-    colourMenu.addSeparator();
-    colourMenu.add(abovePIDColour);
-    colourMenu.add(conservationMenuItem);
-    // colourMenu.add(annotationMenuItem);
     editMenu.add(copy);
     editMenu.add(cut);
     editMenu.add(editSequence);
     editMenu.add(upperCase);
     editMenu.add(lowerCase);
     editMenu.add(toggle);
-    pdbMenu.add(pdbFromFile);
-    pdbMenu.add(enterPDB);
-    pdbMenu.add(discoverPDB);
-    jMenu1.add(groupName);
-    jMenu1.add(colourMenu);
-    jMenu1.add(showBoxes);
-    jMenu1.add(showText);
-    jMenu1.add(showColourText);
-    jMenu1.add(outline);
-    jMenu1.add(displayNonconserved);
-    structureMenu.add(pdbMenu);
-    structureMenu.add(viewStructureMenu);
-    // structureMenu.add(colStructureMenu);
-    noColourmenuItem.setText("None");
-    noColourmenuItem.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        noColourmenuItem_actionPerformed();
-      }
-    });
+    editGroupMenu.add(groupName);
+    editGroupMenu.add(colourMenu);
+    editGroupMenu.add(showBoxes);
+    editGroupMenu.add(showText);
+    editGroupMenu.add(showColourText);
+    editGroupMenu.add(outline);
+    editGroupMenu.add(displayNonconserved);
+  }
 
-    clustalColour.setText("Clustalx colours");
-    clustalColour.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        clustalColour_actionPerformed();
-      }
-    });
-    zappoColour.setText("Zappo");
-    zappoColour.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        zappoColour_actionPerformed();
-      }
-    });
-    taylorColour.setText("Taylor");
-    taylorColour.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        taylorColour_actionPerformed();
-      }
-    });
-    hydrophobicityColour.setText("Hydrophobicity");
-    hydrophobicityColour
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              public void actionPerformed(ActionEvent e)
-              {
-                hydrophobicityColour_actionPerformed();
-              }
-            });
-    helixColour.setText("Helix propensity");
-    helixColour.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        helixColour_actionPerformed();
-      }
-    });
-    strandColour.setText("Strand propensity");
-    strandColour.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        strandColour_actionPerformed();
-      }
-    });
-    turnColour.setText("Turn propensity");
-    turnColour.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        turnColour_actionPerformed();
-      }
-    });
-    buriedColour.setText("Buried Index");
-    buriedColour.addActionListener(new java.awt.event.ActionListener()
+  /**
+   * Constructs the entries for the colour menu
+   */
+  protected void initColourMenu()
+  {
+    colourMenu.setText(MessageManager.getString("label.group_colour"));
+    textColour.setText(MessageManager.getString("label.text_colour"));
+    textColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        buriedColour_actionPerformed();
+        textColour_actionPerformed();
       }
     });
-    abovePIDColour.setText("Above % Identity");
-    abovePIDColour.addActionListener(new java.awt.event.ActionListener()
+
+    abovePIDColour.setText(
+            MessageManager.getString("label.above_identity_threshold"));
+    abovePIDColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        abovePIDColour_actionPerformed();
+        abovePIDColour_actionPerformed(abovePIDColour.isSelected());
       }
     });
-    userDefinedColour.setText("User Defined...");
-    userDefinedColour.addActionListener(new java.awt.event.ActionListener()
+
+    modifyPID.setText(
+            MessageManager.getString("label.modify_identity_threshold"));
+    modifyPID.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        userDefinedColour_actionPerformed(e);
+        modifyPID_actionPerformed();
       }
     });
-    PIDColour.setText("Percentage Identity");
-    PIDColour.addActionListener(new java.awt.event.ActionListener()
+
+    conservationMenuItem
+            .setText(MessageManager.getString("action.by_conservation"));
+    conservationMenuItem.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        PIDColour_actionPerformed();
+        conservationMenuItem_actionPerformed(
+                conservationMenuItem.isSelected());
       }
     });
-    BLOSUM62Colour.setText("BLOSUM62");
-    BLOSUM62Colour.addActionListener(new java.awt.event.ActionListener()
+
+    annotationColour = new JRadioButtonMenuItem(
+            MessageManager.getString("action.by_annotation"));
+    annotationColour.setName(ResidueColourScheme.ANNOTATION_COLOUR);
+    annotationColour.setEnabled(false);
+    annotationColour.setToolTipText(
+            MessageManager.getString("label.by_annotation_tooltip"));
+
+    modifyConservation.setText(MessageManager
+            .getString("label.modify_conservation_threshold"));
+    modifyConservation.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        BLOSUM62Colour_actionPerformed();
+        modifyConservation_actionPerformed();
       }
     });
-    purinePyrimidineColour.setText("Purine/Pyrimidine");
-    purinePyrimidineColour
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              public void actionPerformed(ActionEvent e)
-              {
-                purinePyrimidineColour_actionPerformed();
-              }
-            });
-    /*
-     * covariationColour.addActionListener(new java.awt.event.ActionListener() {
-     * public void actionPerformed(ActionEvent e) {
-     * covariationColour_actionPerformed(); } });
-     */
-
-    conservationMenuItem.setText("Conservation");
-    conservationMenuItem
-            .addActionListener(new java.awt.event.ActionListener()
-            {
-              public void actionPerformed(ActionEvent e)
-              {
-                conservationMenuItem_actionPerformed();
-              }
-            });
-  }
-
-  protected void sequenceSelectionDetails_actionPerformed()
-  {
-    createSequenceDetailsReport(ap.av.getSequenceSelection());
-  }
-
-  protected void sequenceDetails_actionPerformed()
-  {
-    createSequenceDetailsReport(new SequenceI[]
-    { sequence });
   }
 
-  public void createSequenceDetailsReport(SequenceI[] sequences)
+  /**
+   * Builds the group colour sub-menu, including any user-defined colours which
+   * were loaded at startup or during the Jalview session
+   */
+  protected void buildColourMenu()
   {
-    CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
-    StringBuffer contents = new StringBuffer();
-    for (SequenceI seq : sequences)
+    SequenceGroup sg = ap.av.getSelectionGroup();
+    if (sg == null)
     {
-      contents.append("<p><h2>Annotation for " + seq.getDisplayId(true)
-              + "</h2></p><p>");
-      new SequenceAnnotationReport(null)
-              .createSequenceAnnotationReport(
-                      contents,
-                      seq,
-                      true,
-                      true,
-                      false,
-                      (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr.minmax
-                              : null);
-      contents.append("</p>");
+      /*
+       * popup menu with no sequence group scope
+       */
+      return;
     }
-    cap.setText("<html>" + contents.toString() + "</html>");
-
-    Desktop.instance.addInternalFrame(cap, "Sequence Details for "
-            + (sequences.length == 1 ? sequences[0].getDisplayId(true)
-                    : "Selection"), 500, 400);
-
-  }
-
-  protected void showNonconserved_actionPerformed()
-  {
-    getGroup().setShowNonconserved(displayNonconserved.isSelected());
-    refresh();
-  }
+    colourMenu.removeAll();
+    colourMenu.add(textColour);
+    colourMenu.addSeparator();
 
-  /**
-   * call to refresh view after settings change
-   */
-  void refresh()
-  {
-    ap.updateAnnotation();
-    ap.paintAlignment(true);
+    ButtonGroup bg = ColourMenuHelper.addMenuItems(colourMenu, this, sg,
+            false);
+    bg.add(annotationColour);
+    colourMenu.add(annotationColour);
 
-    PaintRefresher.Refresh(this, ap.av.getSequenceSetId());
+    colourMenu.addSeparator();
+    colourMenu.add(conservationMenuItem);
+    colourMenu.add(modifyConservation);
+    colourMenu.add(abovePIDColour);
+    colourMenu.add(modifyPID);
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  protected void clustalColour_actionPerformed()
+  protected void modifyConservation_actionPerformed()
   {
     SequenceGroup sg = getGroup();
-    sg.cs = new ClustalxColourScheme(sg, ap.av.getHiddenRepSequences());
-    refresh();
+    if (sg.cs != null)
+    {
+      SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
+      SliderPanel.showConservationSlider();
+    }
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  protected void zappoColour_actionPerformed()
+  protected void modifyPID_actionPerformed()
   {
-    getGroup().cs = new ZappoColourScheme();
-    refresh();
+    SequenceGroup sg = getGroup();
+    if (sg.cs != null)
+    {
+      // int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup()
+      // .getName());
+      // sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
+      SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup().getName());
+      SliderPanel.showPIDSlider();
+    }
   }
 
   /**
-   * DOCUMENT ME!
+   * Check for any annotations on the underlying dataset sequences (for the
+   * current selection group) which are not 'on the alignment'.If any are found,
+   * enable the option to add them to the alignment. The criteria for 'on the
+   * alignment' is finding an alignment annotation on the alignment, matched on
+   * calcId, label and sequenceRef.
    * 
-   * @param e
-   *          DOCUMENT ME!
+   * A tooltip is also constructed that displays the source (calcId) and type
+   * (label) of the annotations that can be added.
+   * 
+   * @param menuItem
+   * @param forSequences
    */
-  protected void taylorColour_actionPerformed()
+  protected void configureReferenceAnnotationsMenu(JMenuItem menuItem,
+          List<SequenceI> forSequences)
   {
-    getGroup().cs = new TaylorColourScheme();
-    refresh();
+    menuItem.setEnabled(false);
+
+    /*
+     * Temporary store to hold distinct calcId / type pairs for the tooltip.
+     * Using TreeMap means calcIds are shown in alphabetical order.
+     */
+    SortedMap<String, String> tipEntries = new TreeMap<>();
+    final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<>();
+    AlignmentI al = this.ap.av.getAlignment();
+    AlignmentUtils.findAddableReferenceAnnotations(forSequences, tipEntries,
+            candidates, al);
+    if (!candidates.isEmpty())
+    {
+      StringBuilder tooltip = new StringBuilder(64);
+      tooltip.append(MessageManager.getString("label.add_annotations_for"));
+
+      /*
+       * Found annotations that could be added. Enable the menu item, and
+       * configure its tooltip and action.
+       */
+      menuItem.setEnabled(true);
+      for (String calcId : tipEntries.keySet())
+      {
+        tooltip.append("<br/>" + calcId + "/" + tipEntries.get(calcId));
+      }
+      String tooltipText = JvSwingUtils.wrapTooltip(true,
+              tooltip.toString());
+      menuItem.setToolTipText(tooltipText);
+
+      menuItem.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          addReferenceAnnotations_actionPerformed(candidates);
+        }
+      });
+    }
   }
 
   /**
-   * DOCUMENT ME!
+   * Add annotations to the sequences and to the alignment.
    * 
-   * @param e
-   *          DOCUMENT ME!
+   * @param candidates
+   *          a map whose keys are sequences on the alignment, and values a list
+   *          of annotations to add to each sequence
    */
-  protected void hydrophobicityColour_actionPerformed()
+  protected void addReferenceAnnotations_actionPerformed(
+          Map<SequenceI, List<AlignmentAnnotation>> candidates)
   {
-    getGroup().cs = new HydrophobicColourScheme();
+    final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
+    final AlignmentI alignment = this.ap.getAlignment();
+    AlignmentUtils.addReferenceAnnotations(candidates, alignment,
+            selectionGroup);
     refresh();
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  protected void helixColour_actionPerformed()
+  protected void makeReferenceSeq_actionPerformed(ActionEvent actionEvent)
   {
-    getGroup().cs = new HelixColourScheme();
+    if (!ap.av.getAlignment().hasSeqrep())
+    {
+      // initialise the display flags so the user sees something happen
+      ap.av.setDisplayReferenceSeq(true);
+      ap.av.setColourByReferenceSeq(true);
+      ap.av.getAlignment().setSeqrep(sequence);
+    }
+    else
+    {
+      if (ap.av.getAlignment().getSeqrep() == sequence)
+      {
+        ap.av.getAlignment().setSeqrep(null);
+      }
+      else
+      {
+        ap.av.getAlignment().setSeqrep(sequence);
+      }
+    }
     refresh();
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  protected void strandColour_actionPerformed()
+  protected void hideInsertions_actionPerformed(ActionEvent actionEvent)
   {
-    getGroup().cs = new StrandColourScheme();
+    HiddenColumns hidden = ap.av.getAlignment().getHiddenColumns();
+    BitSet inserts = new BitSet();
+
+    boolean markedPopup = false;
+    // mark inserts in current selection
+    if (ap.av.getSelectionGroup() != null)
+    {
+      // mark just the columns in the selection group to be hidden
+      inserts.set(ap.av.getSelectionGroup().getStartRes(),
+              ap.av.getSelectionGroup().getEndRes() + 1); // TODO why +1?
+
+      // now clear columns without gaps
+      for (SequenceI sq : ap.av.getSelectionGroup().getSequences())
+      {
+        if (sq == sequence)
+        {
+          markedPopup = true;
+        }
+        inserts.and(sq.getInsertionsAsBits());
+      }
+      hidden.clearAndHideColumns(inserts, ap.av.getSelectionGroup().getStartRes(),
+              ap.av.getSelectionGroup().getEndRes());
+    }
+
+    // now mark for sequence under popup if we haven't already done it
+    else if (!markedPopup && sequence != null)
+    {
+      inserts.or(sequence.getInsertionsAsBits());
+
+      // and set hidden columns accordingly
+      hidden.hideColumns(inserts);
+    }
     refresh();
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  protected void turnColour_actionPerformed()
+  protected void sequenceSelectionDetails_actionPerformed()
   {
-    getGroup().cs = new TurnColourScheme();
-    refresh();
+    createSequenceDetailsReport(ap.av.getSequenceSelection());
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  protected void buriedColour_actionPerformed()
+  public void createSequenceDetailsReport(SequenceI[] sequences)
   {
-    getGroup().cs = new BuriedColourScheme();
-    refresh();
+    CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
+    StringBuilder contents = new StringBuilder(128);
+    for (SequenceI seq : sequences)
+    {
+      contents.append("<p><h2>" + MessageManager.formatMessage(
+              "label.create_sequence_details_report_annotation_for",
+              new Object[]
+              { seq.getDisplayId(true) }) + "</h2></p><p>");
+      new SequenceAnnotationReport(false).createSequenceAnnotationReport(
+              contents, seq, true, true, ap.getSeqPanel().seqCanvas.fr);
+      contents.append("</p>");
+    }
+    cap.setText("<html>" + contents.toString() + "</html>");
+
+    Desktop.addInternalFrame(cap,
+            MessageManager.formatMessage("label.sequence_details_for",
+                    (sequences.length == 1 ? new Object[]
+                    { sequences[0].getDisplayId(true) }
+                            : new Object[]
+                            { MessageManager
+                                    .getString("label.selection") })),
+            500, 400);
+
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  public void nucleotideMenuItem_actionPerformed()
+  protected void showNonconserved_actionPerformed()
   {
-    getGroup().cs = new NucleotideColourScheme();
+    getGroup().setShowNonconserved(displayNonconserved.isSelected());
     refresh();
   }
 
-  protected void purinePyrimidineColour_actionPerformed()
+  /**
+   * call to refresh view after settings change
+   */
+  void refresh()
   {
-    getGroup().cs = new PurinePyrimidineColourScheme();
-    refresh();
+    ap.updateAnnotation();
+    // removed paintAlignment(true) here:
+    // updateAnnotation calls paintAlignment already, so don't need to call
+    // again
+
+    PaintRefresher.Refresh(this, ap.av.getSequenceSetId());
   }
 
   /*
@@ -1488,10 +1831,12 @@ public class PopupMenu extends JPopupMenu
   /**
    * DOCUMENT ME!
    * 
+   * @param selected
+   * 
    * @param e
    *          DOCUMENT ME!
    */
-  protected void abovePIDColour_actionPerformed()
+  public void abovePIDColour_actionPerformed(boolean selected)
   {
     SequenceGroup sg = getGroup();
     if (sg.cs == null)
@@ -1499,97 +1844,40 @@ public class PopupMenu extends JPopupMenu
       return;
     }
 
-    if (abovePIDColour.isSelected())
+    if (selected)
     {
       sg.cs.setConsensus(AAFrequency.calculate(
               sg.getSequences(ap.av.getHiddenRepSequences()),
               sg.getStartRes(), sg.getEndRes() + 1));
 
-      int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup()
-              .getName());
+      int threshold = SliderPanel.setPIDSliderSource(ap,
+              sg.getGroupColourScheme(), getGroup().getName());
 
-      sg.cs.setThreshold(threshold, ap.av.getIgnoreGapsConsensus());
+      sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
 
       SliderPanel.showPIDSlider();
     }
     else
     // remove PIDColouring
     {
-      sg.cs.setThreshold(0, ap.av.getIgnoreGapsConsensus());
-    }
-
-    refresh();
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  protected void userDefinedColour_actionPerformed(ActionEvent e)
-  {
-    SequenceGroup sg = getGroup();
-
-    if (e.getActionCommand().equals("User Defined..."))
-    {
-      new UserDefinedColours(ap, sg);
-    }
-    else
-    {
-      UserColourScheme udc = (UserColourScheme) UserDefinedColours
-              .getUserColourSchemes().get(e.getActionCommand());
-
-      sg.cs = udc;
+      sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
+      SliderPanel.hidePIDSlider();
     }
-    refresh();
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  protected void PIDColour_actionPerformed()
-  {
-    SequenceGroup sg = getGroup();
-    sg.cs = new PIDColourScheme();
-    sg.cs.setConsensus(AAFrequency.calculate(
-            sg.getSequences(ap.av.getHiddenRepSequences()),
-            sg.getStartRes(), sg.getEndRes() + 1));
-    refresh();
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  protected void BLOSUM62Colour_actionPerformed()
-  {
-    SequenceGroup sg = getGroup();
-
-    sg.cs = new Blosum62ColourScheme();
-
-    sg.cs.setConsensus(AAFrequency.calculate(
-            sg.getSequences(ap.av.getHiddenRepSequences()),
-            sg.getStartRes(), sg.getEndRes() + 1));
+    modifyPID.setEnabled(selected);
 
     refresh();
   }
 
   /**
-   * DOCUMENT ME!
+   * Open a panel where the user can choose which types of sequence annotation
+   * to show or hide.
    * 
    * @param e
-   *          DOCUMENT ME!
    */
-  protected void noColourmenuItem_actionPerformed()
+  protected void chooseAnnotations_actionPerformed(ActionEvent e)
   {
-    getGroup().cs = null;
-    refresh();
+    // todo correct way to guard against opening a duplicate panel?
+    new AnnotationChooser(ap);
   }
 
   /**
@@ -1598,7 +1886,7 @@ public class PopupMenu extends JPopupMenu
    * @param e
    *          DOCUMENT ME!
    */
-  protected void conservationMenuItem_actionPerformed()
+  public void conservationMenuItem_actionPerformed(boolean selected)
   {
     SequenceGroup sg = getGroup();
     if (sg.cs == null)
@@ -1606,44 +1894,28 @@ public class PopupMenu extends JPopupMenu
       return;
     }
 
-    if (conservationMenuItem.isSelected())
+    if (selected)
     {
+      // JBPNote: Conservation name shouldn't be i18n translated
       Conservation c = new Conservation("Group",
-              ResidueProperties.propHash, 3, sg.getSequences(ap.av
-                      .getHiddenRepSequences()), sg.getStartRes(),
-              sg.getEndRes() + 1);
+              sg.getSequences(ap.av.getHiddenRepSequences()),
+              sg.getStartRes(), sg.getEndRes() + 1);
 
       c.calculate();
       c.verdict(false, ap.av.getConsPercGaps());
-
       sg.cs.setConservation(c);
 
-      SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
+      SliderPanel.setConservationSlider(ap, sg.getGroupColourScheme(),
+              sg.getName());
       SliderPanel.showConservationSlider();
     }
     else
     // remove ConservationColouring
     {
       sg.cs.setConservation(null);
+      SliderPanel.hideConservationSlider();
     }
-
-    refresh();
-  }
-
-  public void annotationMenuItem_actionPerformed(ActionEvent actionEvent)
-  {
-    SequenceGroup sg = getGroup();
-    if (sg == null)
-    {
-      return;
-    }
-
-    AnnotationColourGradient acg = new AnnotationColourGradient(
-            sequence.getAnnotation()[0], null,
-            AnnotationColourGradient.NO_THRESHOLD);
-
-    acg.setPredefinedColours(true);
-    sg.cs = acg;
+    modifyConservation.setEnabled(selected);
 
     refresh();
   }
@@ -1659,8 +1931,10 @@ public class PopupMenu extends JPopupMenu
 
     SequenceGroup sg = getGroup();
     EditNameDialog dialog = new EditNameDialog(sg.getName(),
-            sg.getDescription(), "       Group Name ",
-            "Group Description ", "Edit Group Name/Description",
+            sg.getDescription(),
+            "       " + MessageManager.getString("label.group_name") + " ",
+            MessageManager.getString("label.group_description") + " ",
+            MessageManager.getString("label.edit_group_name_description"),
             ap.alignFrame);
 
     if (!dialog.accept)
@@ -1691,16 +1965,19 @@ public class PopupMenu extends JPopupMenu
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Shows a dialog where the sequence name and description may be edited. If a
+   * name containing spaces is entered, these are converted to underscores, with a
+   * warning message.
    */
   void sequenceName_actionPerformed()
   {
     EditNameDialog dialog = new EditNameDialog(sequence.getName(),
-            sequence.getDescription(), "       Sequence Name ",
-            "Sequence Description ", "Edit Sequence Name/Description",
+            sequence.getDescription(),
+            "       " + MessageManager.getString("label.sequence_name")
+                    + " ",
+            MessageManager.getString("label.sequence_description") + " ",
+            MessageManager.getString(
+                    "label.edit_sequence_name_description"),
             ap.alignFrame);
 
     if (!dialog.accept)
@@ -1708,24 +1985,28 @@ public class PopupMenu extends JPopupMenu
       return;
     }
 
-    if (dialog.getName() != null)
+    String name = dialog.getName();
+    if (name != null)
     {
-      if (dialog.getName().indexOf(" ") > -1)
+      if (name.indexOf(" ") > -1)
       {
-        JOptionPane.showMessageDialog(ap,
-                "Spaces have been converted to \"_\"",
-                "No spaces allowed in Sequence Name",
-                JOptionPane.WARNING_MESSAGE);
+        JvOptionPane.showMessageDialog(ap,
+                MessageManager
+                        .getString("label.spaces_converted_to_backslashes"),
+                MessageManager
+                        .getString("label.no_spaces_allowed_sequence_name"),
+                JvOptionPane.WARNING_MESSAGE);
+        name = name.replace(' ', '_');
       }
 
-      sequence.setName(dialog.getName().replace(' ', '_'));
-      ap.paintAlignment(false);
+      sequence.setName(name);
+      ap.paintAlignment(false, false);
     }
 
     sequence.setDescription(dialog.getDescription());
 
-    ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
-            .getSequences());
+    ap.av.firePropertyChange("alignment", null,
+            ap.av.getAlignment().getSequences());
 
   }
 
@@ -1742,9 +2023,11 @@ public class PopupMenu extends JPopupMenu
     ap.av.setSelectionGroup(null);
     refresh();
   }
+
   void createGroupMenuItem_actionPerformed()
   {
-    getGroup(); // implicitly creates group - note - should apply defaults / use standard alignment window logic for this
+    getGroup(); // implicitly creates group - note - should apply defaults / use
+                // standard alignment window logic for this
     refresh();
   }
 
@@ -1757,7 +2040,8 @@ public class PopupMenu extends JPopupMenu
   protected void outline_actionPerformed()
   {
     SequenceGroup sg = getGroup();
-    Color col = JColorChooser.showDialog(this, "Select Outline Colour",
+    Color col = JColorChooser.showDialog(this,
+            MessageManager.getString("label.select_outline_colour"),
             Color.BLUE);
 
     if (col != null)
@@ -1804,57 +2088,9 @@ public class PopupMenu extends JPopupMenu
     refresh();
   }
 
-  public void showLink(String url)
-  {
-    try
-    {
-      jalview.util.BrowserLauncher.openURL(url);
-    } catch (Exception ex)
-    {
-      JOptionPane
-              .showInternalMessageDialog(
-                      Desktop.desktop,
-                      "Unixers: Couldn't find default web browser."
-                              + "\nAdd the full path to your browser in Preferences.",
-                      "Web browser not found", JOptionPane.WARNING_MESSAGE);
-
-      ex.printStackTrace();
-    }
-  }
-
   void hideSequences(boolean representGroup)
   {
-    SequenceGroup sg = ap.av.getSelectionGroup();
-    if (sg == null || sg.getSize() < 1)
-    {
-      ap.av.hideSequence(new SequenceI[]
-      { sequence });
-      return;
-    }
-
-    ap.av.setSelectionGroup(null);
-
-    if (representGroup)
-    {
-      ap.av.hideRepSequences(sequence, sg);
-
-      return;
-    }
-
-    int gsize = sg.getSize();
-    SequenceI[] hseqs;
-
-    hseqs = new SequenceI[gsize];
-
-    int index = 0;
-    for (int i = 0; i < gsize; i++)
-    {
-      hseqs[index++] = sg.getSequenceAt(i);
-    }
-
-    ap.av.hideSequence(hseqs);
-    // refresh(); TODO: ? needed ?
-    ap.av.sendSelection();
+    ap.av.hideSequences(sequence, representGroup);
   }
 
   public void copy_actionPerformed()
@@ -1874,25 +2110,25 @@ public class PopupMenu extends JPopupMenu
 
     if (sg != null)
     {
-      int[][] startEnd = ap.av.getVisibleRegionBoundaries(sg.getStartRes(),
-              sg.getEndRes() + 1);
+      List<int[]> startEnd = ap.av.getVisibleRegionBoundaries(
+              sg.getStartRes(), sg.getEndRes() + 1);
 
       String description;
       int caseChange;
 
       if (source == toggle)
       {
-        description = "Toggle Case";
+        description = MessageManager.getString("label.toggle_case");
         caseChange = ChangeCaseCommand.TOGGLE_CASE;
       }
       else if (source == upperCase)
       {
-        description = "To Upper Case";
+        description = MessageManager.getString("label.to_upper_case");
         caseChange = ChangeCaseCommand.TO_UPPER;
       }
       else
       {
-        description = "To Lower Case";
+        description = MessageManager.getString("label.to_lower_case");
         caseChange = ChangeCaseCommand.TO_LOWER;
       }
 
@@ -1902,8 +2138,8 @@ public class PopupMenu extends JPopupMenu
 
       ap.alignFrame.addHistoryItem(caseCommand);
 
-      ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
-              .getSequences());
+      ap.av.firePropertyChange("alignment", null,
+              ap.av.getAlignment().getSequences());
 
     }
   }
@@ -1912,85 +2148,20 @@ public class PopupMenu extends JPopupMenu
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
     cap.setForInput(null);
-    Desktop.addInternalFrame(cap,
-            "Alignment output - " + e.getActionCommand(), 600, 500);
+    Desktop.addInternalFrame(cap, MessageManager
+            .formatMessage("label.alignment_output_command", new Object[]
+            { e.getActionCommand() }), 600, 500);
 
     String[] omitHidden = null;
 
     System.out.println("PROMPT USER HERE"); // TODO: decide if a prompt happens
     // or we simply trust the user wants
     // wysiwig behaviour
-    SequenceGroup sg = ap.av.getSelectionGroup();
-    ColumnSelection csel = new ColumnSelection(ap.av.getColumnSelection());
-    omitHidden = ap.av.getViewAsString(true);
-    Alignment oal = new Alignment(ap.av.getSequenceSelection());
-    AlignmentAnnotation[] nala = ap.av.getAlignment()
-            .getAlignmentAnnotation();
-    if (nala != null)
-    {
-      for (int i = 0; i < nala.length; i++)
-      {
-        AlignmentAnnotation na = nala[i];
-        oal.addAnnotation(na);
-      }
-    }
-    cap.setText(new FormatAdapter().formatSequences(e.getActionCommand(),
-            oal, omitHidden, csel, sg));
-    oal = null;
-  }
-
-  public void pdbFromFile_actionPerformed()
-  {
-    jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
-    chooser.setFileView(new jalview.io.JalviewFileView());
-    chooser.setDialogTitle("Select a PDB file for "
-            + sequence.getDisplayId(false));
-    chooser.setToolTipText("Load a PDB file and associate it with sequence '"
-            + sequence.getDisplayId(false) + "'");
-
-    int value = chooser.showOpenDialog(null);
-
-    if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
-    {
-      String choice = chooser.getSelectedFile().getPath();
-      jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
-      new AssociatePdbFileWithSeq().associatePdbWithSeq(choice,
-              jalview.io.AppletFormatAdapter.FILE, sequence, true);
-    }
-
-  }
 
-  public void enterPDB_actionPerformed()
-  {
-    String id = JOptionPane.showInternalInputDialog(Desktop.desktop,
-            "Enter PDB Id", "Enter PDB Id", JOptionPane.QUESTION_MESSAGE);
-
-    if (id != null && id.length() > 0)
-    {
-      PDBEntry entry = new PDBEntry();
-      entry.setId(id.toUpperCase());
-      sequence.getDatasetSequence().addPDBId(entry);
-    }
-  }
-
-  public void discoverPDB_actionPerformed()
-  {
-
-    final SequenceI[] sequences = ((ap.av.getSelectionGroup() == null) ? new SequenceI[]
-    { sequence }
-            : ap.av.getSequenceSelection());
-    Thread discpdb = new Thread(new Runnable()
-    {
-      public void run()
-      {
-
-        new jalview.ws.DBRefFetcher(sequences, ap.alignFrame)
-                .fetchDBRefs(false);
-      }
-
-    });
-    discpdb.start();
+    FileFormatI fileFormat = FileFormats.getInstance()
+            .forName(e.getActionCommand());
+    cap.setText(
+            new FormatAdapter(ap).formatSequences(fileFormat, ap, true));
   }
 
   public void sequenceFeature_actionPerformed()
@@ -2001,33 +2172,37 @@ public class PopupMenu extends JPopupMenu
       return;
     }
 
-    int rsize = 0, gSize = sg.getSize();
-    SequenceI[] rseqs, seqs = new SequenceI[gSize];
-    SequenceFeature[] tfeatures, features = new SequenceFeature[gSize];
+    List<SequenceI> seqs = new ArrayList<>();
+    List<SequenceFeature> features = new ArrayList<>();
 
+    /*
+     * assemble dataset sequences, and template new sequence features,
+     * for the amend features dialog
+     */
+    int gSize = sg.getSize();
     for (int i = 0; i < gSize; i++)
     {
       int start = sg.getSequenceAt(i).findPosition(sg.getStartRes());
       int end = sg.findEndRes(sg.getSequenceAt(i));
       if (start <= end)
       {
-        seqs[rsize] = sg.getSequenceAt(i).getDatasetSequence();
-        features[rsize] = new SequenceFeature(null, null, null, start, end,
-                "Jalview");
-        rsize++;
+        seqs.add(sg.getSequenceAt(i).getDatasetSequence());
+        features.add(new SequenceFeature(null, null, start, end, null));
       }
     }
-    rseqs = new SequenceI[rsize];
-    tfeatures = new SequenceFeature[rsize];
-    System.arraycopy(seqs, 0, rseqs, 0, rsize);
-    System.arraycopy(features, 0, tfeatures, 0, rsize);
-    features = tfeatures;
-    seqs = rseqs;
-    if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs,
-            features, true, ap))
-    {
-      ap.alignFrame.setShowSeqFeatures(true);
-      ap.highlightSearchResults(null);
+
+    /*
+     * an entirely gapped region will generate empty lists of sequence / features
+     */
+    if (!seqs.isEmpty())
+    {
+      if (ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+              .amendFeatures(seqs, features, true, ap))
+      {
+        ap.alignFrame.setShowSeqFeatures(true);
+        ap.av.setSearchResults(null); // clear highlighting
+        ap.repaint(); // draw new/amended features
+      }
     }
   }
 
@@ -2040,53 +2215,68 @@ public class PopupMenu extends JPopupMenu
     }
   }
 
-  public void colourByStructure(String pdbid)
-  {
-    Annotation[] anots = ap.av.getStructureSelectionManager()
-            .colourSequenceFromStructure(sequence, pdbid);
-
-    AlignmentAnnotation an = new AlignmentAnnotation("Structure",
-            "Coloured by " + pdbid, anots);
-
-    ap.av.getAlignment().addAnnotation(an);
-    an.createSequenceMapping(sequence, 0, true);
-    // an.adjustForAlignment();
-    ap.av.getAlignment().setAnnotationIndex(an, 0);
-
-    ap.adjustAnnotationHeight();
-
-    sequence.addAlignmentAnnotation(an);
-
-  }
-
   public void editSequence_actionPerformed(ActionEvent actionEvent)
   {
     SequenceGroup sg = ap.av.getSelectionGroup();
 
+    SequenceI seq = sequence;
     if (sg != null)
     {
-      if (sequence == null)
-        sequence = (Sequence) sg.getSequenceAt(0);
+      if (seq == null)
+      {
+        seq = sg.getSequenceAt(0);
+      }
 
       EditNameDialog dialog = new EditNameDialog(
-              sequence.getSequenceAsString(sg.getStartRes(),
-                      sg.getEndRes() + 1), null, "Edit Sequence ", null,
-              "Edit Sequence", ap.alignFrame);
+              seq.getSequenceAsString(sg.getStartRes(),
+                      sg.getEndRes() + 1),
+              null, MessageManager.getString("label.edit_sequence"), null,
+              MessageManager.getString("label.edit_sequence"),
+              ap.alignFrame);
 
       if (dialog.accept)
       {
-        EditCommand editCommand = new EditCommand("Edit Sequences",
-                EditCommand.REPLACE, dialog.getName().replace(' ',
-                        ap.av.getGapCharacter()),
+        EditCommand editCommand = new EditCommand(
+                MessageManager.getString("label.edit_sequences"),
+                Action.REPLACE,
+                dialog.getName().replace(' ', ap.av.getGapCharacter()),
                 sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
                 sg.getStartRes(), sg.getEndRes() + 1, ap.av.getAlignment());
 
         ap.alignFrame.addHistoryItem(editCommand);
 
-        ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
-                .getSequences());
+        ap.av.firePropertyChange("alignment", null,
+                ap.av.getAlignment().getSequences());
       }
     }
   }
 
+  /**
+   * Action on user selecting an item from the colour menu (that does not have
+   * its bespoke action handler)
+   * 
+   * @return
+   */
+  @Override
+  public void changeColour_actionPerformed(String colourSchemeName)
+  {
+    SequenceGroup sg = getGroup();
+    /*
+     * switch to the chosen colour scheme (or null for None)
+     */
+    ColourSchemeI colourScheme = ColourSchemes.getInstance()
+            .getColourScheme(colourSchemeName, ap.av, sg,
+                    ap.av.getHiddenRepSequences());
+    sg.setColourScheme(colourScheme);
+    if (colourScheme instanceof Blosum62ColourScheme
+            || colourScheme instanceof PIDColourScheme)
+    {
+      sg.cs.setConsensus(AAFrequency.calculate(
+              sg.getSequences(ap.av.getHiddenRepSequences()),
+              sg.getStartRes(), sg.getEndRes() + 1));
+    }
+
+    refresh();
+  }
+
 }