JAL-3878 Separate gui elements from operations.
[jalview.git] / src / jalview / gui / AlignFrame.java
index 6f3a7e3..3336663 100644 (file)
@@ -1,19 +1,19 @@
 /*
  * 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 
+ * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation, either version 3
  * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ *
+ * 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/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
@@ -115,7 +115,11 @@ import jalview.ws.params.ArgumentI;
 import jalview.ws.params.ParamDatastoreI;
 import jalview.ws.params.WsParamSetI;
 import jalview.ws.seqfetcher.DbSourceProxy;
-import jalview.ws.slivkaws.SlivkaWSDiscoverer;
+import jalview.ws2.WebServiceDiscoverer;
+import jalview.ws2.WebServiceI;
+import jalview.ws2.operations.Operation;
+import jalview.ws2.slivka.SlivkaWSDiscoverer;
+
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
@@ -158,10 +162,13 @@ import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.Deque;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
 import javax.swing.ButtonGroup;
@@ -183,14 +190,15 @@ import ext.vamsas.ServiceHandle;
 
 /**
  * DOCUMENT ME!
- * 
+ *
  * @author $author$
  * @version $Revision$
  */
 @SuppressWarnings("serial")
 public class AlignFrame extends GAlignFrame
         implements DropTargetListener, IProgressIndicator,
-        AlignViewControllerGuiI, ColourChangeListener, ServiceChangeListener
+        AlignViewControllerGuiI, ColourChangeListener, ServiceChangeListener,
+        WebServiceDiscoverer.ServiceChangeListener
 {
 
   public static int frameCount;
@@ -231,7 +239,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Creates a new AlignFrame object with specific width and height.
-   * 
+   *
    * @param al
    * @param width
    * @param height
@@ -244,7 +252,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Creates a new AlignFrame object with specific width, height and
    * sequenceSetId
-   * 
+   *
    * @param al
    * @param width
    * @param height
@@ -259,7 +267,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Creates a new AlignFrame object with specific width, height and
    * sequenceSetId
-   * 
+   *
    * @param al
    * @param width
    * @param height
@@ -274,7 +282,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * new alignment window with hidden columns
-   * 
+   *
    * @param al
    *          AlignmentI
    * @param hiddenColumns
@@ -293,7 +301,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Create alignment frame for al with hiddenColumns, a specific width and
    * height, and specific sequenceId
-   * 
+   *
    * @param al
    * @param hiddenColumns
    * @param width
@@ -310,7 +318,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Create alignment frame for al with hiddenColumns, a specific width and
    * height, and specific sequenceId
-   * 
+   *
    * @param al
    * @param hiddenColumns
    * @param width
@@ -364,7 +372,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Make a new AlignFrame from existing alignmentPanels
-   * 
+   *
    * @param ap
    *          AlignmentPanel
    * @param av
@@ -570,7 +578,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Change the filename and format for the alignment, and enable the 'reload'
    * button functionality.
-   * 
+   *
    * @param file
    *          valid filename
    * @param format
@@ -586,7 +594,7 @@ public class AlignFrame extends GAlignFrame
   }
 
   /**
-   * 
+   *
    * @param fileName
    * @param file  from SwingJS; may contain bytes -- for reload
    * @param protocol from SwingJS; may be RELATIVE_URL
@@ -604,7 +612,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * JavaScript will have this, maybe others. More dependable than a file name
    * and maintains a reference to the actual bytes loaded.
-   * 
+   *
    * @param file
    */
 
@@ -909,13 +917,20 @@ public class AlignFrame extends GAlignFrame
     buildWebServicesMenu();
   }
 
+  @Override
+  public void operationsChanged(WebServiceDiscoverer discoverer,
+      List<Operation> list)
+  {
+    buildWebServicesMenu();
+  }
+
   /* Set up intrinsic listeners for dynamically generated GUI bits. */
   private void addServiceListeners()
   {
     if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
     {
-      WSDiscovererI discoverer = SlivkaWSDiscoverer.getInstance();
-      discoverer.addServiceChangeListener(this);
+      WebServiceDiscoverer discoverer = SlivkaWSDiscoverer.getInstance();
+      discoverer.addServiceChangeListener((disc, srvcs) -> buildWebServicesMenu());
     }
     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
     {
@@ -927,12 +942,12 @@ public class AlignFrame extends GAlignFrame
       buildWebServicesMenu();
     };
     Desktop.getInstance().addJalviewPropertyChangeListener("services",legacyListener);
-    
+
     addInternalFrameListener(new InternalFrameAdapter() {
       @Override
       public void internalFrameClosed(InternalFrameEvent e) {
         System.out.println("deregistering discoverer listener");
-        SlivkaWSDiscoverer.getInstance().removeServiceChangeListener(AlignFrame.this);
+//        SlivkaWSDiscoverer.getInstance().removeServiceChangeListener(AlignFrame.this);
         Jws2Discoverer.getInstance().removeServiceChangeListener(AlignFrame.this);
         Desktop.getInstance().removeJalviewPropertyChangeListener("services", legacyListener);
         closeMenuItem_actionPerformed(true);
@@ -980,7 +995,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Need to call this method when tabs are selected for multiple views, or when
    * loading from Jalview2XML.java
-   * 
+   *
    * @param av
    *          AlignViewport
    */
@@ -1037,7 +1052,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Set the enabled state of the 'Run Groovy' option in the Calculate menu
-   * 
+   *
    * @param b
    */
 
@@ -1050,7 +1065,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
    */
 
@@ -1059,7 +1074,7 @@ public class AlignFrame extends GAlignFrame
   {
     progressBar.setProgressBar(message, id);
   }
-  
+
   @Override
   public void removeProgressBar(long id)
   {
@@ -1074,7 +1089,7 @@ public class AlignFrame extends GAlignFrame
   }
 
   /**
-   * 
+   *
    * @return true if any progress bars are still active
    */
 
@@ -1197,7 +1212,7 @@ public class AlignFrame extends GAlignFrame
     }
 
     /*
-     * get default parameters, and (if requested) show 
+     * get default parameters, and (if requested) show
      * dialog to allow modification
      */
     ParamDatastoreI store = HMMERParamStore.forSearch(viewport);
@@ -1228,7 +1243,7 @@ public class AlignFrame extends GAlignFrame
   {
 
     /*
-     * get default parameters, and (if requested) show 
+     * get default parameters, and (if requested) show
      * dialog to allow modification
      */
 
@@ -1258,7 +1273,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Checks if the alignment has at least one hidden Markov model, if not shows
    * a dialog advising to run hmmbuild or load an HMM profile
-   * 
+   *
    * @return
    */
   private boolean checkForHMM()
@@ -1303,7 +1318,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Checks if the alignment contains the required number of sequences.
-   * 
+   *
    * @param required
    * @return
    */
@@ -1356,7 +1371,7 @@ public class AlignFrame extends GAlignFrame
    * Opens a file chooser, optionally restricted to selecting folders
    * (directories) only. Answers the path to the selected file or folder, or
    * null if none is chosen.
-   * 
+   *
    * @param
    * @return
    */
@@ -1579,7 +1594,7 @@ public class AlignFrame extends GAlignFrame
    * alignment has hidden regions, or the format is one capable of including
    * non-sequence data (features, annotations, groups), then the user may be
    * prompted to specify what to include in the output.
-   * 
+   *
    * @param file
    * @param format
    */
@@ -1692,7 +1707,7 @@ public class AlignFrame extends GAlignFrame
    * Outputs the alignment to textbox in the requested format, if necessary
    * first prompting the user for whether to include hidden regions or
    * non-sequence data
-   * 
+   *
    * @param fileFormatName
    */
 
@@ -1751,7 +1766,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -1780,7 +1795,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Creates a PNG image of the alignment and writes it to the given file. If
    * the file is null, the user is prompted to choose a file.
-   * 
+   *
    * @param f
    */
 
@@ -1793,7 +1808,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Creates an EPS image of the alignment and writes it to the given file. If
    * the file is null, the user is prompted to choose a file.
-   * 
+   *
    * @param f
    */
 
@@ -1806,7 +1821,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Creates an SVG image of the alignment and writes it to the given file. If
    * the file is null, the user is prompted to choose a file.
-   * 
+   *
    * @param f
    */
 
@@ -1825,7 +1840,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -1879,7 +1894,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Close the current view or all views in the alignment frame. If the frame
    * only contains one view then the alignment will be removed from memory.
-   * 
+   *
    * @param closeAllTabs
    */
 
@@ -1934,7 +1949,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Close the specified panel and close up tabs appropriately.
-   * 
+   *
    * @param panelToClose
    */
 
@@ -2012,7 +2027,7 @@ public class AlignFrame extends GAlignFrame
   }
 
   /**
-   * 
+   *
    * @return alignment objects for all views
    */
 
@@ -2037,7 +2052,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -2076,7 +2091,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -2159,7 +2174,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param up
    *          DOCUMENT ME!
    */
@@ -2284,7 +2299,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -2350,7 +2365,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    * @throws InterruptedException
@@ -2366,7 +2381,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    * @throws InterruptedException
@@ -2382,7 +2397,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Paste contents of Jalview clipboard
-   * 
+   *
    * @param newAlignment
    *          true to paste to a new alignment, otherwise add to this.
    * @throws InterruptedException
@@ -2856,7 +2871,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -2890,7 +2905,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -2903,7 +2918,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -2916,7 +2931,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -2956,7 +2971,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -2969,7 +2984,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3042,7 +3057,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3093,7 +3108,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3131,7 +3146,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3146,7 +3161,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3169,7 +3184,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Creates and shows a new view of the current alignment.
-   * 
+   *
    * @param viewTitle
    *          title of newly created view; if null, one will be generated
    * @param copyAnnotation
@@ -3245,7 +3260,7 @@ public class AlignFrame extends GAlignFrame
    * Make a new name for the view, ensuring it is unique within the current
    * sequenceSetId. (This used to be essential for Jalview Project archives, but
    * these now use viewId. Unique view names are still desirable for usability.)
-   * 
+   *
    * @param viewTitle
    * @return
    */
@@ -3280,7 +3295,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Returns a list of distinct view names found in the given list of
    * components. View names are held on the viewport of an AlignmentPanel.
-   * 
+   *
    * @param comps
    * @return
    */
@@ -3324,7 +3339,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3337,7 +3352,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3368,7 +3383,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
    */
 
@@ -3389,7 +3404,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3403,7 +3418,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3440,7 +3455,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * called by key handler and the hide all/show all menu items
-   * 
+   *
    * @param toggleSeqs
    * @param toggleCols
    */
@@ -3507,7 +3522,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
    * event.ActionEvent)
@@ -3522,7 +3537,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
    * .ActionEvent)
@@ -3542,7 +3557,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
    * ActionEvent)
@@ -3575,7 +3590,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3590,7 +3605,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3605,7 +3620,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3620,7 +3635,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3634,7 +3649,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3648,7 +3663,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3694,7 +3709,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Set or clear 'Show Sequence Features'
-   * 
+   *
    * @param evt
    *          DOCUMENT ME!
    */
@@ -3709,10 +3724,10 @@ public class AlignFrame extends GAlignFrame
   /**
    * 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
    */
 
@@ -3731,7 +3746,7 @@ public class AlignFrame extends GAlignFrame
       {
         alignPanel.updateScrollBarsFromRanges();
       }
-      
+
     });
   }
 
@@ -3769,7 +3784,7 @@ public class AlignFrame extends GAlignFrame
     else
     /**
      * Java only
-     * 
+     *
      * @j2sIgnore
      */
     {
@@ -3790,7 +3805,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -3885,7 +3900,7 @@ public class AlignFrame extends GAlignFrame
    * Action on the user checking or unchecking the option to apply the selected
    * colour scheme to all groups. If unchecked, groups may have their own
    * independent colour schemes.
-   * 
+   *
    * @param selected
    */
 
@@ -3897,7 +3912,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Action on user selecting a colour from the colour menu
-   * 
+   *
    * @param name
    *          the name (not the menu item label!) of the colour scheme
    */
@@ -3926,7 +3941,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Actions on setting or changing the alignment colour scheme
-   * 
+   *
    * @param cs
    */
 
@@ -4015,7 +4030,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -4033,7 +4048,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -4050,7 +4065,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -4067,7 +4082,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -4107,7 +4122,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -4120,7 +4135,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -4175,7 +4190,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Constructs a tree panel and adds it to the desktop
-   * 
+   *
    * @param type
    *          tree type (NJ or AV)
    * @param modelName
@@ -4239,7 +4254,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param title
    *          DOCUMENT ME!
    * @param order
@@ -4275,7 +4290,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Add a new sort by annotation score menu item
-   * 
+   *
    * @param sort
    *          the menu to add the option to
    * @param scoreLabel
@@ -4314,7 +4329,7 @@ public class AlignFrame extends GAlignFrame
    * search the alignment and rebuild the sort by annotation score submenu the
    * last alignment annotation vector hash is stored to minimize cost of
    * rebuilding in subsequence calls.
-   * 
+   *
    */
 
   @Override
@@ -4356,9 +4371,9 @@ public class AlignFrame extends GAlignFrame
   }
 
   /**
-   * Enable (or, if desired, make visible) the By Tree 
+   * Enable (or, if desired, make visible) the By Tree
    * submenu only if it has at least one element (or will have).
-   * 
+   *
    */
   @Override
   protected void enableSortMenuOptions()
@@ -4366,7 +4381,7 @@ public class AlignFrame extends GAlignFrame
     List<TreePanel> treePanels = getTreePanels();
     sortByTreeMenu.setEnabled(!treePanels.isEmpty());
   }
-  
+
   /**
    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
@@ -4432,7 +4447,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Work out whether the whole set of sequences or just the selected set will
    * be submitted for multiple alignment.
-   * 
+   *
    */
 
   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
@@ -4448,7 +4463,7 @@ public class AlignFrame extends GAlignFrame
       /*
        * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
        * SequenceI[sz = seqs.getSize(false)];
-       * 
+       *
        * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
        * seqs.getSequenceAt(i); }
        */
@@ -4510,7 +4525,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * DOCUMENT ME!
-   * 
+   *
    * @param e
    *          DOCUMENT ME!
    */
@@ -4577,7 +4592,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Add a treeviewer for the tree extracted from a Newick file object to the
    * current alignment view
-   * 
+   *
    * @param nf
    *          the tree
    * @param title
@@ -4665,12 +4680,12 @@ public class AlignFrame extends GAlignFrame
   private void buildLegacyWebServicesMenu(JMenu menu)
   {
     JMenu secstrmenu = new JMenu("Secondary Structure Prediction");
-    if (Discoverer.getServices() != null && Discoverer.getServices().size() > 0) 
+    if (Discoverer.getServices() != null && Discoverer.getServices().size() > 0)
     {
       var secstrpred = Discoverer.getServices().get("SecStrPred");
-      if (secstrpred != null) 
+      if (secstrpred != null)
       {
-        for (ext.vamsas.ServiceHandle sh : secstrpred) 
+        for (ext.vamsas.ServiceHandle sh : secstrpred)
         {
           var menuProvider = Discoverer.getServiceClient(sh);
           menuProvider.attachWSMenuEntry(secstrmenu, this);
@@ -4683,7 +4698,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Constructs the web services menu for the given discoverer under the
    * specified menu. This method must be called on the EDT
-   * 
+   *
    * @param discoverer
    *          the discoverer used to build the menu
    * @param menu
@@ -4711,9 +4726,35 @@ public class AlignFrame extends GAlignFrame
     }
   }
 
+  private void buildWebServicesMenu(WebServiceDiscoverer discoverer, final JMenu menu)
+  {
+    if (discoverer.hasServices())
+    {
+      var builder = new WebServicesMenuBuilder();
+      builder.addAllOperations(discoverer.getOperations());
+      builder.addSelectedHostChangeListener((name, op) -> {
+        menu.removeAll();
+        builder.buildMenu(menu, this);
+      });
+      builder.buildMenu(menu, this);
+    }
+    if (discoverer.isRunning())
+    {
+      JMenuItem item = new JMenuItem("Service discovery in progress.");
+      item.setEnabled(false);
+      menu.add(item);
+    }
+    else if (!discoverer.hasServices())
+    {
+      JMenuItem item = new JMenuItem("No services available.");
+      item.setEnabled(false);
+      menu.add(item);
+    }
+  }
+
   /**
    * construct any groupURL type service menu entries.
-   * 
+   *
    * @param webService
    */
 
@@ -4724,12 +4765,12 @@ public class AlignFrame extends GAlignFrame
     /*
      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
-     * 
+     *
      *  public void actionPerformed(ActionEvent e) {
      * jalview.datamodel.AlignmentView
      * .testSelectionViews(af.viewport.getAlignment(),
      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
-     * 
+     *
      * }); webService.add(testAlView);
      */
     // TODO: refactor to RestClient discoverer and merge menu entries for
@@ -4751,7 +4792,7 @@ public class AlignFrame extends GAlignFrame
    * Cross-References menu (formerly called Show Products), with database
    * sources for which cross-references are found (protein sources for a
    * nucleotide alignment and vice versa)
-   * 
+   *
    * @return true if Show Cross-references menu should be enabled
    */
 
@@ -4807,7 +4848,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Finds and displays cross-references for the selected sequences (protein
    * products for nucleotide sequences, dna coding sequences for peptides).
-   * 
+   *
    * @param sel
    *          the sequences to show cross-references for
    * @param dna
@@ -4882,7 +4923,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Set the file format
-   * 
+   *
    * @param format
    */
 
@@ -4893,7 +4934,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Try to load a features file onto the alignment.
-   * 
+   *
    * @param file
    *          contents or path to retrieve file or a File object
    * @param sourceType
@@ -5146,7 +5187,7 @@ public class AlignFrame extends GAlignFrame
    * <li>a features file</li>
    * <li>else try to interpret as an alignment file</li>
    * </ul>
-   * 
+   *
    * @param file
    *          either a filename or a URL string.
    * @throws InterruptedException
@@ -5311,7 +5352,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Change the display state for the given feature groups -- Added by BH from
    * JalviewLite
-   * 
+   *
    * @param groups
    *          list of group strings
    * @param state
@@ -5456,7 +5497,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
    * )
@@ -5470,7 +5511,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
    * ActionEvent)
    */
@@ -5484,7 +5525,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * find the viewport amongst the tabs in this alignment frame and close that
    * tab
-   * 
+   *
    * @param av
    */
 
@@ -5858,7 +5899,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
    * awt.event.ActionEvent)
@@ -5873,7 +5914,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
    * .ActionEvent)
@@ -5889,7 +5930,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
    * .event.ActionEvent)
@@ -5904,7 +5945,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
    * .event.ActionEvent)
@@ -5919,7 +5960,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
    * .event.ActionEvent)
@@ -5949,7 +5990,7 @@ public class AlignFrame extends GAlignFrame
 
   /*
    * (non-Javadoc)
-   * 
+   *
    * @see
    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
    * .event.ActionEvent)
@@ -6003,7 +6044,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * make the given alignmentPanel the currently selected tab
-   * 
+   *
    * @param alignmentPanel
    */
 
@@ -6024,7 +6065,7 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * Action on selection of menu options to Show or Hide annotations.
-   * 
+   *
    * @param visible
    * @param forSequences
    *          update sequence-related annotations
@@ -6076,7 +6117,7 @@ public class AlignFrame extends GAlignFrame
   }
 
   /**
-   * 
+   *
    * @return alignment panels in this alignment frame
    */
 
@@ -6139,7 +6180,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Set visibility of dna/protein complement view (available when shown in a
    * split frame).
-   * 
+   *
    * @param show
    */
 
@@ -6212,7 +6253,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Hides columns containing (or not containing) a specified feature, provided
    * that would not leave all columns hidden
-   * 
+   *
    * @param featureType
    * @param columnsContaining
    * @return
@@ -6351,9 +6392,9 @@ public class AlignFrame extends GAlignFrame
 
   /**
    * BH 2019 from JalviewLite
-   * 
+   *
    * get sequence feature groups that are hidden or shown
-   * 
+   *
    * @param visible
    *          true is visible
    * @return list
@@ -6373,7 +6414,7 @@ public class AlignFrame extends GAlignFrame
   }
 
   /**
-   * 
+   *
    * @return list of feature groups on the view
    */
 
@@ -6438,5 +6479,6 @@ public class AlignFrame extends GAlignFrame
       }
     }
   }
+
 }