Merge branch 'develop' into features/mchmmer
[jalview.git] / src / jalview / gui / PopupMenu.java
index 73d7941..a3a2422 100644 (file)
@@ -33,15 +33,11 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.HiddenMarkovModel;
 import jalview.datamodel.PDBEntry;
-import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.gui.ColourMenuHelper.ColourChangeListener;
-import jalview.hmmer.HMMAlignThread;
-import jalview.hmmer.HMMBuildThread;
 import jalview.io.FileFormatI;
 import jalview.io.FileFormats;
 import jalview.io.FormatAdapter;
@@ -53,6 +49,7 @@ import jalview.schemes.PIDColourScheme;
 import jalview.util.GroupUrlLink;
 import jalview.util.GroupUrlLink.UrlStringTooLongException;
 import jalview.util.MessageManager;
+import jalview.util.StringUtils;
 import jalview.util.UrlLink;
 
 import java.awt.Color;
@@ -60,6 +57,7 @@ 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;
@@ -160,7 +158,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
   JMenuItem sequenceFeature = new JMenuItem();
 
-  JMenuItem hmmBuildGroup = new JMenuItem();
 
   JMenuItem textColour = new JMenuItem();
 
@@ -180,25 +177,31 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
    * Creates a new PopupMenu object.
    * 
    * @param ap
-   *          DOCUMENT ME!
    * @param seq
-   *          DOCUMENT ME!
+   * @param features
+   *          non-positional features (for seq not null), or positional features
+   *          at residue (for seq equal to null)
    */
-  public PopupMenu(final AlignmentPanel ap, Sequence seq,
-          List<String> links)
+  public PopupMenu(final AlignmentPanel ap, SequenceI seq,
+          List<SequenceFeature> features)
   {
-    this(ap, seq, links, null);
+    this(ap, seq, features, null);
   }
 
   /**
+   * Constructor
    * 
-   * @param ap
+   * @param alignPanel
    * @param seq
-   * @param links
+   *          the sequence under the cursor if in the Id panel, null if in the
+   *          sequence panel
+   * @param features
+   *          non-positional features if in the Id panel, features at the
+   *          clicked residue if in the sequence panel
    * @param groupLinks
    */
-  public PopupMenu(final AlignmentPanel ap, final SequenceI seq,
-          List<String> links, List<String> groupLinks)
+  public PopupMenu(final AlignmentPanel alignPanel, final SequenceI seq,
+          List<SequenceFeature> features, List<String> groupLinks)
   {
     // /////////////////////////////////////////////////////////
     // If this is activated from the sequence panel, the user may want to
@@ -206,7 +209,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     //
     // If from the IDPanel, we must display the sequence menu
     // ////////////////////////////////////////////////////////
-    this.ap = ap;
+    this.ap = alignPanel;
     sequence = seq;
 
     for (String ff : FileFormats.getInstance().getWritableFormats(true))
@@ -232,6 +235,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
      */
     final List<SequenceI> selectedSequence = (seq == null
             ? Collections.<SequenceI> emptyList() : Arrays.asList(seq));
+
     buildAnnotationTypesMenus(seqShowAnnotationsMenu,
             seqHideAnnotationsMenu, selectedSequence);
     configureReferenceAnnotationsMenu(seqAddReferenceAnnotations,
@@ -240,9 +244,9 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     /*
      * And repeat for the current selection group (if there is one):
      */
-    final List<SequenceI> selectedGroup = (ap.av.getSelectionGroup() == null
+    final List<SequenceI> selectedGroup = (alignPanel.av.getSelectionGroup() == null
             ? Collections.<SequenceI> emptyList()
-            : ap.av.getSelectionGroup().getSequences());
+            : alignPanel.av.getSelectionGroup().getSequences());
     buildAnnotationTypesMenus(groupShowAnnotationsMenu,
             groupHideAnnotationsMenu, selectedGroup);
     configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
@@ -260,7 +264,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     if (seq != null)
     {
       sequenceMenu.setText(sequence.getName());
-      if (seq == ap.av.getAlignment().getSeqrep())
+      if (seq == alignPanel.av.getAlignment().getSeqrep())
       {
         makeReferenceSeq.setText(
                 MessageManager.getString("action.unmark_as_reference"));
@@ -271,7 +275,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
                 MessageManager.getString("action.set_as_reference"));
       }
 
-      if (!ap.av.getAlignment().isNucleotide())
+      if (!alignPanel.av.getAlignment().isNucleotide())
       {
         remove(rnaStructureMenu);
       }
@@ -282,7 +286,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
          * add menu items to 2D-render any alignment or sequence secondary
          * structure annotation
          */
-        AlignmentAnnotation[] aas = ap.av.getAlignment()
+        AlignmentAnnotation[] aas = alignPanel.av.getAlignment()
                 .getAlignmentAnnotation();
         if (aas != null)
         {
@@ -295,15 +299,14 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
                */
               menuItem = new JMenuItem();
               menuItem.setText(MessageManager.formatMessage(
-                      "label.2d_rna_structure_line",
-                      new Object[]
-              { aa.label }));
+                      "label.2d_rna_structure_line", new Object[]
+                      { aa.label }));
               menuItem.addActionListener(new ActionListener()
               {
                 @Override
                 public void actionPerformed(ActionEvent e)
                 {
-                  new AppVarna(seq, aa, ap);
+                  new AppVarna(seq, aa, alignPanel);
                 }
               });
               rnaStructureMenu.add(menuItem);
@@ -324,16 +327,15 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
               // TODO: make rnastrucF a bit more nice
               menuItem = new JMenuItem();
               menuItem.setText(MessageManager.formatMessage(
-                      "label.2d_rna_sequence_name",
-                      new Object[]
-              { seq.getName() }));
+                      "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, aa, ap);
+                  new AppVarna(seq, aa, alignPanel);
                 }
               });
               rnaStructureMenu.add(menuItem);
@@ -358,41 +360,11 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       });
       add(menuItem);
 
-      if (sequence.isHMMConsensusSequence())
+      if (alignPanel.av.getSelectionGroup() != null
+              && alignPanel.av.getSelectionGroup().getSize() > 1)
       {
-        JMenuItem selectHMM = new JCheckBoxMenuItem();
-        selectHMM.setText(MessageManager.getString("label.select_hmm"));
-        selectHMM.addActionListener(new ActionListener()
-        {
-
-          @Override
-          public void actionPerformed(ActionEvent e)
-          {
-            selectHMM_actionPerformed(e);
-          }
-        });
-        add(selectHMM);
-
-        JMenuItem hmmAlign = new JCheckBoxMenuItem();
-        hmmAlign.setText(MessageManager.getString("label.hmmalign"));
-        hmmAlign.addActionListener(new ActionListener()
-        {
-
-          @Override
-          public void actionPerformed(ActionEvent e)
-          {
-            hmmAlign_actionPerformed(e);
-          }
-        });
-        add(hmmAlign);
-      }
-
-      if (ap.av.getSelectionGroup() != null
-              && ap.av.getSelectionGroup().getSize() > 1)
-      {
-        menuItem = new JMenuItem(
-                MessageManager.formatMessage("label.represent_group_with",
-                        new Object[]
+        menuItem = new JMenuItem(MessageManager
+                .formatMessage("label.represent_group_with", new Object[]
                 { seq.getName() }));
         menuItem.addActionListener(new ActionListener()
         {
@@ -405,12 +377,12 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         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(
                   MessageManager.getString("action.reveal_sequences"));
@@ -419,10 +391,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
             @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();
               }
             }
           });
@@ -431,7 +403,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       }
     }
     // for the case when no sequences are even visible
-    if (ap.av.hasHiddenRows())
+    if (alignPanel.av.hasHiddenRows())
     {
       {
         menuItem = new JMenuItem(
@@ -441,10 +413,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
           @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();
             }
           }
         });
@@ -453,9 +425,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       }
     }
 
-    SequenceGroup sg = ap.av.getSelectionGroup();
+    SequenceGroup sg = alignPanel.av.getSelectionGroup();
     boolean isDefinedGroup = (sg != null)
-            ? ap.av.getAlignment().getGroups().contains(sg) : false;
+            ? alignPanel.av.getAlignment().getGroups().contains(sg)
+            : false;
 
     if (sg != null && sg.getSize() > 0)
     {
@@ -489,10 +462,11 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         buildGroupURLMenu(sg, groupLinks);
       }
       // Add a 'show all structures' for the current selection
-      Hashtable<String, PDBEntry> pdbe = new Hashtable<>(),
-              reppdb = new Hashtable<>();
+      Hashtable<String, PDBEntry> pdbe = new Hashtable<>();
+      Hashtable<String, PDBEntry> reppdb = new Hashtable<>();
+
       SequenceI sqass = null;
-      for (SequenceI sq : ap.av.getSequenceSelection())
+      for (SequenceI sq : alignPanel.av.getSequenceSelection())
       {
         Vector<PDBEntry> pes = sq.getDatasetSequence().getAllPDBEntries();
         if (pes != null && pes.size() > 0)
@@ -542,24 +516,133 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       rnaStructureMenu.setVisible(false);
     }
 
-    if (links != null && links.size() > 0)
+    addLinks(seq, features);
+
+    if (seq == null)
+    {
+      addFeatureDetails(features);
+    }
+  }
+
+  /**
+   * Add a link to show feature details for each sequence feature
+   * 
+   * @param features
+   */
+  protected void addFeatureDetails(List<SequenceFeature> features)
+  {
+    if (features == null || features.isEmpty())
+    {
+      return;
+    }
+    JMenu details = new JMenu(
+            MessageManager.getString("label.feature_details"));
+    add(details);
+
+    for (final SequenceFeature sf : features)
     {
-      addFeatureLinks(seq, links);
+      int start = sf.getBegin();
+      int end = sf.getEnd();
+      String desc = null;
+      if (start == end)
+      {
+        desc = String.format("%s %d", sf.getType(), start);
+      }
+      else
+      {
+        desc = String.format("%s %d-%d", sf.getType(), start, end);
+      }
+      String tooltip = desc;
+      String description = sf.getDescription();
+      if (description != null)
+      {
+        description = StringUtils.stripHtmlTags(description);
+        if (description.length() > 12)
+        {
+          desc = desc + " " + description.substring(0, 12) + "..";
+        }
+        else
+        {
+          desc = desc + " " + description;
+        }
+        tooltip = tooltip + " " + description;
+      }
+      if (sf.getFeatureGroup() != null)
+      {
+        tooltip = tooltip + (" (" + sf.getFeatureGroup() + ")");
+      }
+      JMenuItem item = new JMenuItem(desc);
+      item.setToolTipText(tooltip);
+      item.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          showFeatureDetails(sf);
+        }
+      });
+      details.add(item);
     }
   }
 
   /**
+   * Opens a panel showing a text report of feature dteails
+   * 
+   * @param sf
+   */
+  protected void showFeatureDetails(SequenceFeature sf)
+  {
+    CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
+    // it appears Java's CSS does not support border-collaps :-(
+    cap.addStylesheetRule("table { border-collapse: collapse;}");
+    cap.addStylesheetRule("table, td, th {border: 1px solid black;}");
+    cap.setText(sf.getDetailsReport());
+
+    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.
    * 
    * @param seq
-   * @param links
+   * @param features
    */
-  void addFeatureLinks(final SequenceI seq, List<String> links)
+  void addLinks(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();
+    }
+    else
+    {
+      nlinks = new ArrayList<>();
+    }
+
+    if (features != null)
+    {
+      for (SequenceFeature sf : features)
+      {
+        if (sf.links != null)
+        {
+          for (String link : sf.links)
+          {
+            nlinks.add(link);
+          }
+        }
+      }
+    }
+
     Map<String, List<String>> linkset = new LinkedHashMap<>();
 
-    for (String link : links)
+    for (String link : nlinks)
     {
       UrlLink urlLink = null;
       try
@@ -582,25 +665,18 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
     addshowLinks(linkMenu, linkset.values());
 
-    // disable link menu if there are no valid entries
+    // only add link menu if it has entries
     if (linkMenu.getItemCount() > 0)
     {
-      linkMenu.setEnabled(true);
-    }
-    else
-    {
-      linkMenu.setEnabled(false);
-    }
-
-    if (sequence != null)
-    {
-      sequenceMenu.add(linkMenu);
-    }
-    else
-    {
-      add(linkMenu);
+      if (sequence != null)
+      {
+        sequenceMenu.add(linkMenu);
+      }
+      else
+      {
+        add(linkMenu);
+      }
     }
-
   }
 
   /**
@@ -626,7 +702,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
     final List<String> all = Arrays
             .asList(new String[]
-    { MessageManager.getString("label.all") });
+            { MessageManager.getString("label.all") });
     addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true,
             true);
     addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
@@ -855,8 +931,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         int type = urlLink.getGroupURLType() & 3;
         // 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;
       }
@@ -902,7 +980,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     JMenuItem item = new JMenuItem(label);
     item.setToolTipText(MessageManager.formatMessage("label.open_url_param",
             new Object[]
-    { url }));
+            { url }));
     item.addActionListener(new ActionListener()
     {
       @Override
@@ -939,11 +1017,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
           final GroupUrlLink urlgenerator, final Object[] urlstub)
   {
     JMenuItem item = new JMenuItem(label);
-    item.setToolTipText(
-            MessageManager.formatMessage("label.open_url_seqs_param",
-                    new Object[]
+    item.setToolTipText(MessageManager
+            .formatMessage("label.open_url_seqs_param", new Object[]
             { urlgenerator.getUrl_prefix(),
-        urlgenerator.getNumberInvolved(urlstub) }));
+                urlgenerator.getNumberInvolved(urlstub) }));
     // TODO: put in info about what is being sent.
     item.addActionListener(new ActionListener()
     {
@@ -1167,15 +1244,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         sequenceFeature_actionPerformed();
       }
     });
-    hmmBuildGroup.setText(MessageManager.getString("label.group_hmmbuild"));
-    hmmBuildGroup.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        hmmBuildGroup_actionPerformed();
-      }
-    });
     jMenu1.setText(MessageManager.getString("label.group"));
     pdbStructureDialog.setText(
             MessageManager.getString("label.show_pdbstruct_dialog"));
@@ -1285,26 +1353,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     jMenu1.add(displayNonconserved);
   }
 
-  protected void hmmBuildGroup_actionPerformed()
-  {
-    new Thread(new HMMBuildThread(ap.alignFrame)).start();
-    ap.repaint();
-
-  }
-
-  protected void selectHMM_actionPerformed(ActionEvent e)
-  {
-    HiddenMarkovModel hmm = ap.av.getSequenceSelection()[0].getHMM();
-    ap.alignFrame.setSelectedHMM(hmm);
-  }
-
-  protected void hmmAlign_actionPerformed(ActionEvent e)
-  {
-    ap.alignFrame.setSelectedHMM(this.sequence.getHMM());
-    new Thread(new HMMAlignThread(ap.alignFrame, true)).start();
-    ap.repaint();
-  }
-
   /**
    * Constructs the entries for the colour menu
    */
@@ -1515,23 +1563,37 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
   protected void hideInsertions_actionPerformed(ActionEvent actionEvent)
   {
-    if (sequence != null)
+    HiddenColumns hidden = ap.av.getAlignment().getHiddenColumns();
+    BitSet inserts = new BitSet();
+
+    boolean markedPopup = false;
+    // mark inserts in current selection
+    if (ap.av.getSelectionGroup() != null)
     {
-      /* ColumnSelection cs = ap.av.getColumnSelection();
-       if (cs == null)
-       {
-         cs = new ColumnSelection();
-       }
-       cs.hideInsertionsFor(sequence);
-       ap.av.setColumnSelection(cs);*/
+      // 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?
 
-      HiddenColumns hidden = ap.av.getAlignment().getHiddenColumns();
-      if (hidden == null)
+      // now clear columns without gaps
+      for (SequenceI sq : ap.av.getSelectionGroup().getSequences())
       {
-        hidden = new HiddenColumns();
+        if (sq == sequence)
+        {
+          markedPopup = true;
+        }
+        inserts.and(sq.getInsertionsAsBits());
       }
-      hidden.hideInsertionsFor(sequence);
-      ap.av.getAlignment().setHiddenColumns(hidden);
+      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();
   }
@@ -1552,28 +1614,24 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     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(null)
-              .createSequenceAnnotationReport(contents, seq, true, true,
-                      (ap.getSeqPanel().seqCanvas.fr != null)
-                              ? ap.getSeqPanel().seqCanvas.fr.getMinMax()
-                              : null);
+      contents.append("<p><h2>" + MessageManager.formatMessage(
+              "label.create_sequence_details_report_annotation_for",
+              new Object[]
+              { seq.getDisplayId(true) }) + "</h2></p><p>");
+      new SequenceAnnotationReport(null).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[]
+    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);
+                            : new Object[]
+                            { MessageManager
+                                    .getString("label.selection") })),
+            500, 400);
 
   }
 
@@ -1589,7 +1647,9 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   void refresh()
   {
     ap.updateAnnotation();
-    ap.paintAlignment(true);
+    // removed paintAlignment(true) here:
+    // updateAnnotation calls paintAlignment already, so don't need to call
+    // again
 
     PaintRefresher.Refresh(this, ap.av.getSequenceSetId());
   }
@@ -1747,8 +1807,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
             "       " + MessageManager.getString("label.sequence_name")
                     + " ",
             MessageManager.getString("label.sequence_description") + " ",
-            MessageManager
-                    .getString("label.edit_sequence_name_description"),
+            MessageManager.getString(
+                    "label.edit_sequence_name_description"),
             ap.alignFrame);
 
     if (!dialog.accept)
@@ -1769,7 +1829,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       }
 
       sequence.setName(dialog.getName().replace(' ', '_'));
-      ap.paintAlignment(false);
+      ap.paintAlignment(false, false);
     }
 
     sequence.setDescription(dialog.getDescription());
@@ -1933,9 +1993,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
     cap.setForInput(null);
-    Desktop.addInternalFrame(cap,
-            MessageManager.formatMessage("label.alignment_output_command",
-                    new Object[]
+    Desktop.addInternalFrame(cap, MessageManager
+            .formatMessage("label.alignment_output_command", new Object[]
             { e.getActionCommand() }), 600, 500);
 
     String[] omitHidden = null;
@@ -1973,16 +2032,22 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       if (start <= end)
       {
         seqs.add(sg.getSequenceAt(i).getDatasetSequence());
-        features.add(
-                new SequenceFeature(null, null, null, start, end, null));
+        features.add(new SequenceFeature(null, null, start, end, null));
       }
     }
 
-    if (ap.getSeqPanel().seqCanvas.getFeatureRenderer().amendFeatures(seqs,
-            features, true, ap))
+    /*
+     * an entirely gapped region will generate empty lists of sequence / features
+     */
+    if (!seqs.isEmpty())
     {
-      ap.alignFrame.setShowSeqFeatures(true);
-      ap.highlightSearchResults(null);
+      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
+      }
     }
   }