JAL-1619 refactoring wip
[jalview.git] / src / jalview / gui / AlignFrame.java
index 6346870..5fe97a4 100644 (file)
@@ -25,15 +25,18 @@ import jalview.analysis.AlignmentSorter;
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.Conservation;
 import jalview.analysis.CrossRef;
+import jalview.analysis.Dna;
 import jalview.analysis.NJTree;
 import jalview.analysis.ParseProperties;
 import jalview.analysis.SequenceIdMatcher;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
+import jalview.api.AlignmentViewPanel;
 import jalview.api.analysis.ScoreModelI;
 import jalview.bin.Cache;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
+import jalview.commands.EditCommand.Action;
 import jalview.commands.OrderCommand;
 import jalview.commands.RemoveGapColCommand;
 import jalview.commands.RemoveGapsCommand;
@@ -82,6 +85,7 @@ import jalview.schemes.TaylorColourScheme;
 import jalview.schemes.TurnColourScheme;
 import jalview.schemes.UserColourScheme;
 import jalview.schemes.ZappoColourScheme;
+import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
@@ -114,6 +118,7 @@ import java.beans.PropertyChangeEvent;
 import java.io.File;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
@@ -279,7 +284,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * Make a new AlignFrame from exisiting alignmentPanels
+   * Make a new AlignFrame from existing alignmentPanels
    * 
    * @param ap
    *          AlignmentPanel
@@ -334,6 +339,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     setMenusFromViewport(viewport);
     buildSortByAnnotationScoresMenu();
     buildTreeMenu();
+    
     if (viewport.wrapAlignment)
     {
       wrapMenuItem_actionPerformed(null);
@@ -376,7 +382,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
                         .getKeyCode() <= KeyEvent.VK_NUMPAD9))
                 && Character.isDigit(evt.getKeyChar()))
+        {
           alignPanel.seqPanel.numberPressed(evt.getKeyChar());
+        }
 
         switch (evt.getKeyCode())
         {
@@ -388,32 +396,48 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
         case KeyEvent.VK_DOWN:
           if (evt.isAltDown() || !viewport.cursorMode)
+          {
             moveSelectedSequences(false);
+          }
           if (viewport.cursorMode)
+          {
             alignPanel.seqPanel.moveCursor(0, 1);
+          }
           break;
 
         case KeyEvent.VK_UP:
           if (evt.isAltDown() || !viewport.cursorMode)
+          {
             moveSelectedSequences(true);
+          }
           if (viewport.cursorMode)
+          {
             alignPanel.seqPanel.moveCursor(0, -1);
+          }
 
           break;
 
         case KeyEvent.VK_LEFT:
           if (evt.isAltDown() || !viewport.cursorMode)
+          {
             slideSequences(false, alignPanel.seqPanel.getKeyboardNo1());
+          }
           else
+          {
             alignPanel.seqPanel.moveCursor(-1, 0);
+          }
 
           break;
 
         case KeyEvent.VK_RIGHT:
           if (evt.isAltDown() || !viewport.cursorMode)
+          {
             slideSequences(true, alignPanel.seqPanel.getKeyboardNo1());
+          }
           else
+          {
             alignPanel.seqPanel.moveCursor(1, 0);
+          }
           break;
 
         case KeyEvent.VK_SPACE:
@@ -505,14 +529,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         case KeyEvent.VK_F1:
           try
           {
-            ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
-            java.net.URL url = javax.help.HelpSet.findHelpSet(cl,
-                    "help/help");
-            javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
-
-            javax.help.HelpBroker hb = hs.createHelpBroker();
-            hb.setCurrentID("home");
-            hb.setDisplayed(true);
+            Help.showHelpWindow();
           } catch (Exception ex)
           {
             ex.printStackTrace();
@@ -557,14 +574,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         {
         case KeyEvent.VK_LEFT:
           if (evt.isAltDown() || !viewport.cursorMode)
+          {
             viewport.firePropertyChange("alignment", null, viewport
                     .getAlignment().getSequences());
+          }
           break;
 
         case KeyEvent.VK_RIGHT:
           if (evt.isAltDown() || !viewport.cursorMode)
+          {
             viewport.firePropertyChange("alignment", null, viewport
                     .getAlignment().getSequences());
+          }
           break;
         }
       }
@@ -684,21 +705,110 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void setGUINucleotide(boolean nucleotide)
   {
     showTranslation.setVisible(nucleotide);
+    cdna.setVisible(!nucleotide);
     conservationMenuItem.setEnabled(!nucleotide);
     modifyConservation.setEnabled(!nucleotide);
     showGroupConservation.setEnabled(!nucleotide);
     rnahelicesColour.setEnabled(nucleotide);
     purinePyrimidineColour.setEnabled(nucleotide);
-    // Remember AlignFrame always starts as protein
-    // if (!nucleotide)
-    // {
-    // showTr
-    // calculateMenu.remove(calculateMenu.getItemCount() - 2);
-    // }
   }
 
   /**
-   * set up menus for the currently viewport. This may be called after any
+   * Builds codon mappings from this (protein) alignment to any compatible
+   * nucleotide alignments. Mappings are built between sequences with the same
+   * name and compatible lengths. Also makes the cDNA alignment a
+   * CommandListener for the protein alignment so that edits are mirrored.
+   */
+  @Override
+  protected void linkCdna_actionPerformed()
+  {
+    int linkedCount = 0;
+    int alreadyLinkedCount = 0;
+    final AlignmentI thisAlignment = this.alignPanel.getAlignment();
+
+    for (AlignFrame af : Desktop.getAlignframes())
+    {
+      if (af.alignPanel != null)
+      {
+        final AlignmentI thatAlignment = af.alignPanel.getAlignment();
+        if (thatAlignment.isNucleotide())
+        {
+          // TODO exclude an AlignFrame which is already mapped to this one
+          // temporary version: exclude if already a CommandListener (should
+          // cover most cases but not all)
+          final boolean alreadyMapped = this.viewport
+                  .getStructureSelectionManager().hasCommandListener(
+                          af.viewport);
+          if (alreadyMapped)
+          {
+            alreadyLinkedCount++;
+          }
+          else
+          {
+            boolean mapped = AlignmentUtils.mapProteinToCdna(thisAlignment,
+                    thatAlignment);
+            if (mapped)
+            {
+              final StructureSelectionManager ssm = StructureSelectionManager
+                      .getStructureSelectionManager(Desktop.instance);
+              ssm.addMappings(thisAlignment.getCodonFrames());
+              ssm.addCommandListener(af.getViewport());
+              linkedCount++;
+            }
+          }
+        }
+      }
+    }
+    String msg = "";
+    if (linkedCount == 0 && alreadyLinkedCount == 0)
+    {
+      msg = MessageManager.getString("label.no_cdna");
+    }
+    else if (linkedCount > 0)
+    {
+      msg = MessageManager.formatMessage("label.linked_cdna", linkedCount);
+    }
+    else
+    {
+      msg = MessageManager.formatMessage("label.cdna_all_linked",
+              alreadyLinkedCount);
+    }
+    setStatus(msg);
+  }
+
+  /**
+   * Align any linked cDNA to match the alignment of this (protein) alignment.
+   * Any mapped sequence regions will be realigned, unmapped sequences are not
+   * affected.
+   */
+  @Override
+  protected void alignCdna_actionPerformed()
+  {
+    int seqCount = 0;
+    int alignCount = 0;
+    final AlignmentI thisAlignment = this.alignPanel.getAlignment();
+    for (AlignFrame af : Desktop.getAlignframes())
+    {
+      if (af.alignPanel != null)
+      {
+        final AlignmentI thatAlignment = af.alignPanel.getAlignment();
+        if (thatAlignment.isNucleotide())
+        {
+          int seqsAligned = thatAlignment.alignAs(thisAlignment);
+          seqCount += seqsAligned;
+          if (seqsAligned > 0)
+          {
+            af.alignPanel.alignmentChanged();
+            alignCount++;
+          }
+        }
+      }
+    }
+    setStatus(MessageManager.formatMessage("label.cdna_aligned", seqCount,
+            alignCount));
+  }
+  /**
+   * set up menus for the current viewport. This may be called after any
    * operation that affects the data in the current view (selection changed,
    * etc) to update the menus to reflect the new state.
    */
@@ -729,6 +839,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     scaleLeft.setVisible(av.wrapAlignment);
     scaleRight.setVisible(av.wrapAlignment);
     annotationPanelMenuItem.setState(av.showAnnotation);
+    /*
+     * Show/hide annotations only enabled if annotation panel is shown
+     */
+    showAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
     viewBoxesMenuItem.setSelected(av.showBoxes);
     viewTextMenuItem.setSelected(av.showText);
     showNonconservedMenuItem.setSelected(av.getShowUnconserved());
@@ -1006,7 +1123,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
       currentFileFormat = chooser.getSelectedFormat();
-      if (currentFileFormat == null)
+      while (currentFileFormat == null)
       {
         JOptionPane
                 .showInternalMessageDialog(
@@ -1016,8 +1133,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         MessageManager
                                 .getString("label.file_format_not_specified"),
                         JOptionPane.WARNING_MESSAGE);
+        currentFileFormat = chooser.getSelectedFormat();
         value = chooser.showSaveDialog(this);
-        return;
+        if (value != JalviewFileChooser.APPROVE_OPTION)
+        {
+          return;
+        }
       }
 
       fileName = chooser.getSelectedFile().getPath();
@@ -1237,6 +1358,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     alignPanel.makeEPS(f);
   }
 
+  public void createSVG(File f)
+  {
+    alignPanel.makeSVG(f);
+  }
   @Override
   public void pageSetup_actionPerformed(ActionEvent e)
   {
@@ -1380,10 +1505,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   void updateEditMenuBar()
   {
 
-    if (viewport.historyList.size() > 0)
+    if (viewport.getHistoryList().size() > 0)
     {
       undoMenuItem.setEnabled(true);
-      CommandI command = (CommandI) viewport.historyList.peek();
+      CommandI command = viewport.getHistoryList().peek();
       undoMenuItem.setText(MessageManager.formatMessage(
               "label.undo_command", new String[]
               { command.getDescription() }));
@@ -1394,11 +1519,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       undoMenuItem.setText(MessageManager.getString("action.undo"));
     }
 
-    if (viewport.redoList.size() > 0)
+    if (viewport.getRedoList().size() > 0)
     {
       redoMenuItem.setEnabled(true);
 
-      CommandI command = (CommandI) viewport.redoList.peek();
+      CommandI command = viewport.getRedoList().peek();
       redoMenuItem.setText(MessageManager.formatMessage(
               "label.redo_command", new String[]
               { command.getDescription() }));
@@ -1414,8 +1539,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (command.getSize() > 0)
     {
-      viewport.historyList.push(command);
-      viewport.redoList.clear();
+      viewport.addToHistoryList(command);
+      viewport.clearRedoList();
       updateEditMenuBar();
       viewport.updateHiddenColumns();
       // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
@@ -1458,10 +1583,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void undoMenuItem_actionPerformed(ActionEvent e)
   {
-    if (viewport.historyList.empty())
+    if (viewport.getHistoryList().isEmpty())
+    {
       return;
-    CommandI command = (CommandI) viewport.historyList.pop();
-    viewport.redoList.push(command);
+    }
+    CommandI command = viewport.getHistoryList().pop();
+    viewport.addToRedoList(command);
+    // TODO: execute command before adding to redo list / broadcasting?
     command.undoCommand(getViewAlignments());
 
     AlignViewport originalSource = getOriginatingSource(command);
@@ -1494,13 +1622,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void redoMenuItem_actionPerformed(ActionEvent e)
   {
-    if (viewport.redoList.size() < 1)
+    if (viewport.getRedoList().size() < 1)
     {
       return;
     }
 
-    CommandI command = (CommandI) viewport.redoList.pop();
-    viewport.historyList.push(command);
+    CommandI command = viewport.getRedoList().pop();
+    viewport.addToHistoryList(command);
     command.doCommand(getViewAlignments());
 
     AlignViewport originalSource = getOriginatingSource(command);
@@ -1612,37 +1740,53 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
     {
       if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
+      {
         invertGroup.add(viewport.getAlignment().getSequenceAt(i));
+      }
     }
 
     SequenceI[] seqs1 = sg.toArray(new SequenceI[0]);
 
     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
     for (int i = 0; i < invertGroup.size(); i++)
+    {
       seqs2[i] = (SequenceI) invertGroup.elementAt(i);
+    }
 
     SlideSequencesCommand ssc;
     if (right)
+    {
       ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
               size, viewport.getGapCharacter());
+    }
     else
+    {
       ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
               size, viewport.getGapCharacter());
+    }
 
     int groupAdjustment = 0;
     if (ssc.getGapsInsertedBegin() && right)
     {
       if (viewport.cursorMode)
+      {
         alignPanel.seqPanel.moveCursor(size, 0);
+      }
       else
+      {
         groupAdjustment = size;
+      }
     }
     else if (!ssc.getGapsInsertedBegin() && !right)
     {
       if (viewport.cursorMode)
+      {
         alignPanel.seqPanel.moveCursor(-size, 0);
+      }
       else
+      {
         groupAdjustment = -size;
+      }
     }
 
     if (groupAdjustment != 0)
@@ -1654,16 +1798,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     boolean appendHistoryItem = false;
-    if (viewport.historyList != null && viewport.historyList.size() > 0
-            && viewport.historyList.peek() instanceof SlideSequencesCommand)
+    if (viewport.getHistoryList() != null
+            && viewport.getHistoryList().size() > 0
+            && viewport.getHistoryList().peek() instanceof SlideSequencesCommand)
     {
       appendHistoryItem = ssc
-              .appendSlideCommand((SlideSequencesCommand) viewport.historyList
+              .appendSlideCommand((SlideSequencesCommand) viewport
+                      .getHistoryList()
                       .peek());
     }
 
     if (!appendHistoryItem)
+    {
       addHistoryItem(ssc);
+    }
 
     repaint();
   }
@@ -1943,7 +2091,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         // /////
         // ADD HISTORY ITEM
         //
-        addHistoryItem(new EditCommand(MessageManager.getString("label.add_sequences"), EditCommand.PASTE,
+        addHistoryItem(new EditCommand(
+                MessageManager.getString("label.add_sequences"),
+                Action.PASTE,
                 sequences, 0, alignment.getWidth(), alignment));
       }
       // Add any annotations attached to sequences
@@ -1998,7 +2148,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           {
             AlignmentAnnotation sann[] = sequences[i].getAnnotation();
             if (sann == null)
+            {
               continue;
+            }
             for (int avnum = 0; avnum < alview.length; avnum++)
             {
               if (alview[avnum] != alignment)
@@ -2178,17 +2330,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    Vector seqs = new Vector();
+    List<SequenceI> seqs = new ArrayList<SequenceI>(sg.getSize());
     SequenceI seq;
     for (int i = 0; i < sg.getSize(); i++)
     {
       seq = sg.getSequenceAt(i);
-      seqs.addElement(seq);
+      seqs.add(seq);
     }
 
-    // If the cut affects all sequences, remove highlighted columns
+    // If the cut affects all sequences, warn, remove highlighted columns
     if (sg.getSize() == viewport.getAlignment().getHeight())
     {
+      int confirm = JOptionPane.showConfirmDialog(this,
+              MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
+              MessageManager.getString("label.delete_all"), // $NON-NLS-1$
+              JOptionPane.OK_CANCEL_OPTION);
+
+      if (confirm == JOptionPane.CANCEL_OPTION
+              || confirm == JOptionPane.CLOSED_OPTION)
+      {
+        return;
+      }
       viewport.getColumnSelection().removeElements(sg.getStartRes(),
               sg.getEndRes() + 1);
     }
@@ -2196,14 +2358,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     SequenceI[] cut = new SequenceI[seqs.size()];
     for (int i = 0; i < seqs.size(); i++)
     {
-      cut[i] = (SequenceI) seqs.elementAt(i);
+      cut[i] = seqs.get(i);
     }
 
     /*
      * //ADD HISTORY ITEM
      */
-    addHistoryItem(new EditCommand(MessageManager.getString("label.cut_sequences"), EditCommand.CUT, cut,
-            sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
+    addHistoryItem(new EditCommand(
+            MessageManager.getString("label.cut_sequences"), Action.CUT,
+            cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
             viewport.getAlignment()));
 
     viewport.setSelectionGroup(null);
@@ -2535,6 +2698,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     new Finder();
   }
 
+  /**
+   * Create a new view of the current alignment.
+   */
   @Override
   public void newView_actionPerformed(ActionEvent e)
   {
@@ -2597,8 +2763,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       viewport.viewName = "Original";
     }
 
-    newap.av.historyList = viewport.historyList;
-    newap.av.redoList = viewport.redoList;
+    newap.av.setHistoryList(viewport.getHistoryList());
+    newap.av.setRedoList(viewport.getRedoList());
 
     int index = Desktop.getViewCount(viewport.getSequenceSetId());
     // make sure the new view has a unique name - this is essential for Jalview
@@ -3033,16 +3199,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * DOCUMENT ME!
+   * Action on toggle of the 'Show annotations' menu item. This shows or hides
+   * the annotations panel as a whole.
+   * 
+   * The options to show/hide all annotations should be enabled when the panel
+   * is shown, and disabled when the panel is hidden.
    * 
    * @param e
-   *          DOCUMENT ME!
    */
   @Override
   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
   {
-    viewport.setShowAnnotation(annotationPanelMenuItem.isSelected());
-    alignPanel.setAnnotationVisible(annotationPanelMenuItem.isSelected());
+    final boolean setVisible = annotationPanelMenuItem.isSelected();
+    viewport.setShowAnnotation(setVisible);
+    alignPanel.setAnnotationVisible(setVisible);
+    this.showAllSeqAnnotations.setEnabled(setVisible);
+    this.hideAllSeqAnnotations.setEnabled(setVisible);
+    this.showAllAlAnnotations.setEnabled(setVisible);
+    this.hideAllAlAnnotations.setEnabled(setVisible);
   }
 
   @Override
@@ -4021,6 +4195,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           tm.setText(title);//
           tm.addActionListener(new java.awt.event.ActionListener()
           {
+            @Override
             public void actionPerformed(ActionEvent e)
             {
               NewTreePanel(type, (String) pwtype, title);
@@ -4112,16 +4287,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
        */
       msa = viewport.getAlignmentView(true);
     }
+    else if (viewport.getSelectionGroup() != null
+            && viewport.getSelectionGroup().getSize() == 1)
+    {
+      int option = JOptionPane.showConfirmDialog(this,
+              MessageManager.getString("warn.oneseq_msainput_selection"),
+              MessageManager.getString("label.invalid_selection"),
+              JOptionPane.OK_CANCEL_OPTION);
+      if (option == JOptionPane.OK_OPTION)
+      {
+        msa = viewport.getAlignmentView(false);
+      }
+    }
     else
     {
-      /*
-       * Vector seqs = viewport.getAlignment().getSequences();
-       * 
-       * if (seqs.size() > 1) { msa = new SequenceI[seqs.size()];
-       * 
-       * for (int i = 0; i < seqs.size(); i++) { msa[i] = (SequenceI)
-       * seqs.elementAt(i); } }
-       */
       msa = viewport.getAlignmentView(false);
     }
     return msa;
@@ -4327,11 +4506,22 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           // object broker mechanism.
           final Vector<JMenu> wsmenu = new Vector<JMenu>();
           final IProgressIndicator af = me;
-          final JMenu msawsmenu = new JMenu(MessageManager.getString("label.alignment"));
-          final JMenu secstrmenu = new JMenu(MessageManager.getString("label.secondary_structure_prediction"));
-          final JMenu seqsrchmenu = new JMenu(MessageManager.getString("label.sequence_database_search"));
-          final JMenu analymenu = new JMenu(MessageManager.getString("label.analysis"));
-          final JMenu dismenu = new JMenu(MessageManager.getString("label.protein_disorder"));
+          final JMenu msawsmenu = new JMenu("Alignment");
+          final JMenu secstrmenu = new JMenu(
+                  "Secondary Structure Prediction");
+          final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
+          final JMenu analymenu = new JMenu("Analysis");
+          final JMenu dismenu = new JMenu("Protein Disorder");
+          // final JMenu msawsmenu = new
+          // JMenu(MessageManager.getString("label.alignment"));
+          // final JMenu secstrmenu = new
+          // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
+          // final JMenu seqsrchmenu = new
+          // JMenu(MessageManager.getString("label.sequence_database_search"));
+          // final JMenu analymenu = new
+          // JMenu(MessageManager.getString("label.analysis"));
+          // final JMenu dismenu = new
+          // JMenu(MessageManager.getString("label.protein_disorder"));
           // JAL-940 - only show secondary structure prediction services from
           // the legacy server
           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
@@ -4616,7 +4806,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               if (ds.getSequences() == null
                       || !ds.getSequences().contains(
                               sprods[s].getDatasetSequence()))
+              {
                 ds.addSequence(sprods[s].getDatasetSequence());
+              }
               sprods[s].updatePDBIds();
             }
             Alignment al = new Alignment(sprods);
@@ -4678,85 +4870,40 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  @Override
-  public void showProducts_actionPerformed(ActionEvent e)
-  {
-    // /////////////////////////////
-    // Collect Data to be translated/transferred
-
-    SequenceI[] selection = viewport.getSequenceSelection();
-    AlignmentI al = null;
-    try
-    {
-      al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
-              .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
-              viewport.getAlignment().getDataset());
-    } catch (Exception ex)
-    {
-      al = null;
-      jalview.bin.Cache.log.debug("Exception during translation.", ex);
-    }
-    if (al == null)
-    {
-      JOptionPane
-              .showMessageDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
-                      MessageManager.getString("label.translation_failed"),
-                      JOptionPane.WARNING_MESSAGE);
-    }
-    else
-    {
-      AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-      Desktop.addInternalFrame(af, MessageManager.formatMessage(
-              "label.translation_of_params", new String[]
-              { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
-    }
-  }
-
+  /**
+   * Construct and display a new frame containing the translation of this
+   * frame's cDNA sequences to their aligned protein (amino acid) equivalents.
+   */
   @Override
   public void showTranslation_actionPerformed(ActionEvent e)
   {
-    // /////////////////////////////
-    // Collect Data to be translated/transferred
-
-    SequenceI[] selection = viewport.getSequenceSelection();
-    String[] seqstring = viewport.getViewAsString(true);
     AlignmentI al = null;
     try
     {
-      al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
-              viewport.getViewAsVisibleContigs(true), viewport
-                      .getGapCharacter(), viewport.getAlignment()
-                      .getAlignmentAnnotation(), viewport.getAlignment()
-                      .getWidth(), viewport.getAlignment().getDataset());
+      Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
+
+      al = dna.translateCdna();
     } catch (Exception ex)
     {
-      al = null;
       jalview.bin.Cache.log.error(
               "Exception during translation. Please report this !", ex);
-      JOptionPane
-              .showMessageDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.error_when_translating_sequences_submit_bug_report"),
-                      MessageManager
-                              .getString("label.implementation_error")
-                              + MessageManager
-                                      .getString("translation_failed"),
-                      JOptionPane.ERROR_MESSAGE);
+      final String msg = MessageManager
+              .getString("label.error_when_translating_sequences_submit_bug_report");
+      final String title = MessageManager
+              .getString("label.implementation_error")
+              + MessageManager.getString("translation_failed");
+      JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
+              JOptionPane.ERROR_MESSAGE);
       return;
     }
     if (al == null)
     {
-      JOptionPane
-              .showMessageDialog(
-                      Desktop.desktop,
-                      MessageManager
-                              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
-                      MessageManager.getString("label.translation_failed"),
-                      JOptionPane.WARNING_MESSAGE);
+      final String msg = MessageManager
+              .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
+      final String title = MessageManager
+              .getString("label.translation_failed");
+      JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
+              JOptionPane.WARNING_MESSAGE);
     }
     else
     {
@@ -4764,6 +4911,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       Desktop.addInternalFrame(af, MessageManager.formatMessage(
               "label.translation_of_params", new String[]
               { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
+      viewport.getStructureSelectionManager().addCommandListener(viewport);
     }
   }
 
@@ -4978,7 +5126,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               {
                 PDBEntry pe = new AssociatePdbFileWithSeq()
                         .associatePdbWithSeq((String) fm[0],
-                                (String) fm[1], toassoc, false);
+                                (String) fm[1], toassoc, false,
+                                Desktop.instance);
                 if (pe != null)
                 {
                   System.err.println("Associated file : "
@@ -4998,14 +5147,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
                           .showConfirmDialog(
                                   this,
-                                  MessageManager
+                                  "<html>"+MessageManager
                                           .formatMessage(
                                                   "label.ignore_unmatched_dropped_files_info",
                                                   new String[]
                                                   { Integer.valueOf(
                                                           filesnotmatched
                                                                   .size())
-                                                          .toString() }),
+                                                          .toString() })+"</html>",
                                   MessageManager
                                           .getString("label.ignore_unmatched_dropped_files"),
                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
@@ -5308,6 +5457,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
     trimrs.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         trimrs.setSelected(trimrs.isSelected());
@@ -5411,8 +5561,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                   }
 
                 });
-                fetchr.setToolTipText("<html>"
-                        + JvSwingUtils.wrapTooltip(MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{src.getDbName()})));
+                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{src.getDbName()})));
                 dfetch.add(fetchr);
                 comp++;
               }
@@ -5445,8 +5594,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                   }
                 });
 
-                fetchr.setToolTipText("<html>"
-                        + JvSwingUtils.wrapTooltip(MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new String[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
+                fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new String[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
                 dfetch.add(fetchr);
                 comp++;
                 // and then build the rest of the individual menus
@@ -5692,6 +5840,54 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
     }
   }
+
+  /**
+   * Action on selection of menu options to Show or Hide annotations.
+   * 
+   * @param visible
+   * @param forSequences
+   *          update sequence-related annotations
+   * @param forAlignment
+   *          update non-sequence-related annotations
+   */
+  @Override
+  protected void setAnnotationsVisibility(boolean visible,
+          boolean forSequences, boolean forAlignment)
+  {
+    for (AlignmentAnnotation aa : alignPanel.getAlignment()
+            .getAlignmentAnnotation())
+    {
+      boolean apply = (aa.sequenceRef == null && forAlignment)
+              || (aa.sequenceRef != null && forSequences);
+      if (apply)
+      {
+        aa.visible = visible;
+      }
+    }
+    alignPanel.validateAnnotationDimensions(false);
+    alignPanel.alignmentChanged();
+  }
+
+  /**
+   * Store selected annotation sort order for the view and repaint.
+   */
+  @Override
+  protected void sortAnnotations_actionPerformed()
+  {
+    this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
+    this.alignPanel.av
+            .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+    alignPanel.paintAlignment(true);
+  }
+
+  /**
+   * 
+   * @return alignment panels in this alignemnt frame
+   */
+  public List<AlignmentViewPanel> getAlignPanels()
+  {
+    return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
+  }
 }
 
 class PrintThread extends Thread