apply jalview code style
[jalview.git] / src / jalview / gui / Desktop.java
index 8d824d7..3205dab 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
  * 
  * This file is part of Jalview.
@@ -19,11 +19,14 @@ package jalview.gui;
 
 import jalview.bin.Cache;
 import jalview.io.*;
+import jalview.ws.params.ParamManager;
 
 import java.awt.*;
 import java.awt.datatransfer.*;
 import java.awt.dnd.*;
 import java.awt.event.*;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -40,7 +43,8 @@ import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
 /**
- * DOCUMENT ME!
+ * Jalview Desktop
+ * 
  * 
  * @author $author$
  * @version $Revision$
@@ -48,15 +52,115 @@ import javax.swing.event.MenuListener;
 public class Desktop extends jalview.jbgui.GDesktop implements
         DropTargetListener, ClipboardOwner, IProgressIndicator
 {
-  /** DOCUMENT ME!! */
+
+  private class JalviewChangeSupport implements PropertyChangeListener
+  {
+    public void propertyChange(PropertyChangeEvent evt)
+    {
+      // Handle change events - most are simply routed to other sources
+      changeSupport.firePropertyChange(evt);
+    }
+
+    /**
+     * change listeners are notified of changes to resources so they can update
+     * their state. E.g. - the 'services' property notifies when the available
+     * set of web service endpoints have changed.
+     */
+    private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
+            this);
+
+    /**
+     * @param propertyName
+     * @param listener
+     * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.lang.String,
+     *      java.beans.PropertyChangeListener)
+     */
+    public void addJalviewPropertyChangeListener(String propertyName,
+            PropertyChangeListener listener)
+    {
+      changeSupport.addPropertyChangeListener(propertyName, listener);
+    }
+
+    /**
+     * @param listener
+     * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
+     */
+    public void addJalviewPropertyChangeListener(
+            PropertyChangeListener listener)
+    {
+      changeSupport.addPropertyChangeListener(listener);
+    }
+
+    /*
+     * @param propertyName
+     * 
+     * @param oldValue
+     * 
+     * @param newValue
+     * 
+     * @see
+     * java.beans.PropertyChangeSupport#firePropertyChange(java.lang.String,
+     * java.lang.Object, java.lang.Object) public void firePropertyChange(String
+     * propertyName, Object oldValue, Object newValue) {
+     * changeSupport.firePropertyChange(propertyName, oldValue, newValue); }
+     */
+
+    /**
+     * @param propertyName
+     * @param listener
+     * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.lang.String,
+     *      java.beans.PropertyChangeListener)
+     */
+    public void removeJalviewPropertyChangeListener(String propertyName,
+            PropertyChangeListener listener)
+    {
+      changeSupport.removePropertyChangeListener(propertyName, listener);
+    }
+
+  }
+
+  private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
+
+  /**
+   * @param listener
+   * @see jalview.gui.Desktop.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
+   */
+  public void addJalviewPropertyChangeListener(
+          PropertyChangeListener listener)
+  {
+    changeSupport.addJalviewPropertyChangeListener(listener);
+  }
+
+  /**
+   * @param propertyName
+   * @param listener
+   * @see jalview.gui.Desktop.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
+   *      java.beans.PropertyChangeListener)
+   */
+  public void addJalviewPropertyChangeListener(String propertyName,
+          PropertyChangeListener listener)
+  {
+    changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
+  }
+
+  /**
+   * @param propertyName
+   * @param listener
+   * @see jalview.gui.Desktop.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
+   *      java.beans.PropertyChangeListener)
+   */
+  public void removeJalviewPropertyChangeListener(String propertyName,
+          PropertyChangeListener listener)
+  {
+    changeSupport.removeJalviewPropertyChangeListener(propertyName,
+            listener);
+  }
+
+  /** Singleton Desktop instance */
   public static Desktop instance;
 
-  // Need to decide if the Memory Usage is to be included in
-  // Next release or not.
   public static MyDesktopPane desktop;
 
-  // public static JDesktopPane desktop;
-
   static int openFrameCount = 0;
 
   static final int xOffset = 30;
@@ -71,6 +175,104 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
   static int fileLoadingCount = 0;
 
+  class MyDesktopManager implements DesktopManager
+  {
+
+    private DesktopManager delegate;
+
+    public MyDesktopManager(DesktopManager delegate)
+    {
+      this.delegate = delegate;
+    }
+
+    public void activateFrame(JInternalFrame f)
+    {
+      try
+      {
+        delegate.activateFrame(f);
+      } catch (NullPointerException npe)
+      {
+        Point p = getMousePosition();
+        instance.showPasteMenu(p.x, p.y);
+      }
+    }
+
+    public void beginDraggingFrame(JComponent f)
+    {
+      delegate.beginDraggingFrame(f);
+    }
+
+    public void beginResizingFrame(JComponent f, int direction)
+    {
+      delegate.beginResizingFrame(f, direction);
+    }
+
+    public void closeFrame(JInternalFrame f)
+    {
+      delegate.closeFrame(f);
+    }
+
+    public void deactivateFrame(JInternalFrame f)
+    {
+      delegate.deactivateFrame(f);
+    }
+
+    public void deiconifyFrame(JInternalFrame f)
+    {
+      delegate.deiconifyFrame(f);
+    }
+
+    public void dragFrame(JComponent f, int newX, int newY)
+    {
+      delegate.dragFrame(f, newX, newY);
+    }
+
+    public void endDraggingFrame(JComponent f)
+    {
+      delegate.endDraggingFrame(f);
+    }
+
+    public void endResizingFrame(JComponent f)
+    {
+      delegate.endResizingFrame(f);
+    }
+
+    public void iconifyFrame(JInternalFrame f)
+    {
+      delegate.iconifyFrame(f);
+    }
+
+    public void maximizeFrame(JInternalFrame f)
+    {
+      delegate.maximizeFrame(f);
+    }
+
+    public void minimizeFrame(JInternalFrame f)
+    {
+      delegate.minimizeFrame(f);
+    }
+
+    public void openFrame(JInternalFrame f)
+    {
+      delegate.openFrame(f);
+    }
+
+    public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
+            int newHeight)
+    {
+      delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
+    }
+
+    public void setBoundsForFrame(JComponent f, int newX, int newY,
+            int newWidth, int newHeight)
+    {
+      delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
+    }
+
+    // All other methods, simply delegate
+
+  }
+
   /**
    * Creates a new Desktop object.
    */
@@ -100,8 +302,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
     // This line prevents Windows Look&Feel resizing all new windows to maximum
     // if previous window was maximised
-    desktop.setDesktopManager(new DefaultDesktopManager());
-
+    desktop.setDesktopManager(new MyDesktopManager(
+            new DefaultDesktopManager()));
     Rectangle dims = getLastKnownDimensions("");
     if (dims != null)
     {
@@ -113,9 +315,9 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       setBounds((int) (screenSize.width - 900) / 2,
               (int) (screenSize.height - 650) / 2, 900, 650);
     }
-    jconsole = new Console(this);
-    // immediately output essential build information
-    System.out.println("Jalview Desktop "
+    jconsole = new Console(this, showjconsole);
+    // add essential build information
+    jconsole.setHeader("Jalview Desktop "
             + jalview.bin.Cache.getProperty("VERSION") + "\n"
             + "Build Date: "
             + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
@@ -155,7 +357,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       }
     });
 
-    discoverer = new jalview.ws.jws1.Discoverer(); // Only gets started if gui is
     // displayed.
     // Thread off a new instance of the file chooser - this reduces the time it
     // takes to open it later on.
@@ -212,6 +413,18 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         iw = (int) (iw * sw);
         iy = (int) (iy * sh);
         ih = (int) (ih * sh);
+        while (ix >= screenSize.width)
+        {
+          jalview.bin.Cache.log
+                  .debug("Window geometry location recall error: shifting horizontal to within screenbounds.");
+          ix -= screenSize.width;
+        }
+        while (iy >= screenSize.height)
+        {
+          jalview.bin.Cache.log
+                  .debug("Window geometry location recall error: shifting vertical to within screenbounds.");
+          iy -= screenSize.height;
+        }
         jalview.bin.Cache.log.debug("Got last known dimensions for "
                 + windowName + ": x:" + ix + " y:" + iy + " width:" + iw
                 + " height:" + ih);
@@ -374,34 +587,33 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     }
 
     final JMenuItem menuItem = new JMenuItem(title);
-    frame
-            .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
-            {
-              public void internalFrameActivated(
-                      javax.swing.event.InternalFrameEvent evt)
-              {
-                JInternalFrame itf = desktop.getSelectedFrame();
-                if (itf != null)
-                {
-                  itf.requestFocus();
-                }
+    frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+    {
+      public void internalFrameActivated(
+              javax.swing.event.InternalFrameEvent evt)
+      {
+        JInternalFrame itf = desktop.getSelectedFrame();
+        if (itf != null)
+        {
+          itf.requestFocus();
+        }
 
-              }
+      }
 
-              public void internalFrameClosed(
-                      javax.swing.event.InternalFrameEvent evt)
-              {
-                PaintRefresher.RemoveComponent(frame);
-                openFrameCount--;
-                windowMenu.remove(menuItem);
-                JInternalFrame itf = desktop.getSelectedFrame();
-                if (itf != null)
-                {
-                  itf.requestFocus();
-                }
-                System.gc();
-              };
-            });
+      public void internalFrameClosed(
+              javax.swing.event.InternalFrameEvent evt)
+      {
+        PaintRefresher.RemoveComponent(frame);
+        openFrameCount--;
+        windowMenu.remove(menuItem);
+        JInternalFrame itf = desktop.getSelectedFrame();
+        if (itf != null)
+        {
+          itf.requestFocus();
+        }
+        System.gc();
+      };
+    });
 
     menuItem.addActionListener(new ActionListener()
     {
@@ -553,8 +765,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
   {
-    JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
-            .getProperty("LAST_DIRECTORY"),
+    JalviewFileChooser chooser = new JalviewFileChooser(
+            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
             jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
             jalview.io.AppletFormatAdapter.READABLE_FNAMES,
             jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
@@ -734,22 +946,20 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     if (!jalview.bin.Cache.getProperty("LATEST_VERSION").equals(
             jalview.bin.Cache.getProperty("VERSION")))
     {
-      message
-              .append("\n\n!! Jalview version "
-                      + jalview.bin.Cache.getProperty("LATEST_VERSION")
-                      + " is available for download from http://www.jalview.org !!\n");
+      message.append("\n\n!! Jalview version "
+              + jalview.bin.Cache.getProperty("LATEST_VERSION")
+              + " is available for download from http://www.jalview.org !!\n");
 
     }
     // TODO: update this text for each release or centrally store it for lite
     // and application
-    message
-            .append("\nAuthors:  Andrew Waterhouse, Jim Procter, Michele Clamp, James Cuff, Steve Searle,\n    David Martin & Geoff Barton."
-                    + "\nDevelopment managed by The Barton Group, University of Dundee, Scotland, UK.\n"
-                    + "\nFor help, see the FAQ at www.jalview.org and/or join the jalview-discuss@jalview.org mailing list\n"
-                    + "\nIf  you use Jalview, please cite:"
-                    + "\nWaterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
-                    + "\nJalview Version 2 - a multiple sequence alignment editor and analysis workbench"
-                    + "\nBioinformatics doi: 10.1093/bioinformatics/btp033");
+    message.append("\nAuthors:  Andrew Waterhouse, Jim Procter, Michele Clamp, James Cuff, Steve Searle,\n    David Martin & Geoff Barton."
+            + "\nDevelopment managed by The Barton Group, University of Dundee, Scotland, UK.\n"
+            + "\nFor help, see the FAQ at www.jalview.org and/or join the jalview-discuss@jalview.org mailing list\n"
+            + "\nIf  you use Jalview, please cite:"
+            + "\nWaterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
+            + "\nJalview Version 2 - a multiple sequence alignment editor and analysis workbench"
+            + "\nBioinformatics doi: 10.1093/bioinformatics/btp033");
     JOptionPane.showInternalMessageDialog(Desktop.desktop,
 
     message.toString(), "About Jalview", JOptionPane.INFORMATION_MESSAGE);
@@ -962,10 +1172,10 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   public void saveState_actionPerformed(ActionEvent e)
   {
-    JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
-            .getProperty("LAST_DIRECTORY"), new String[]
-    { "jar" }, new String[]
-    { "Jalview Project" }, "Jalview Project");
+    JalviewFileChooser chooser = new JalviewFileChooser(
+            jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
+            { "jar" }, new String[]
+            { "Jalview Project" }, "Jalview Project");
 
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle("Save State");
@@ -975,12 +1185,27 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
       java.io.File choice = chooser.getSelectedFile();
-      JProgressBar progpanel = addProgressPanel("Saving jalview project "
-              + choice.getName());
+      setProgressBar("Saving jalview project " + choice.getName(),
+              choice.hashCode());
       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
       // TODO catch and handle errors for savestate
-      new Jalview2XML().SaveState(choice);
-      removeProgressPanel(progpanel);
+      try
+      {
+        new Jalview2XML().SaveState(choice);
+      } catch (OutOfMemoryError oom)
+      {
+        new OOMWarning(
+                "Whilst saving current state to " + choice.getName(), oom);
+      } catch (Exception ex)
+      {
+        Cache.log
+                .error("Problems whilst trying to save to "
+                        + choice.getName(), ex);
+        JOptionPane.showMessageDialog(this,
+                "Error whilst saving current state to " + choice.getName(),
+                "Couldn't save project", JOptionPane.WARNING_MESSAGE);
+      }
+      setProgressBar(null, choice.hashCode());
 
     }
   }
@@ -993,10 +1218,10 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   public void loadState_actionPerformed(ActionEvent e)
   {
-    JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
-            .getProperty("LAST_DIRECTORY"), new String[]
-    { "jar" }, new String[]
-    { "Jalview Project" }, "Jalview Project");
+    JalviewFileChooser chooser = new JalviewFileChooser(
+            jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
+            { "jar" }, new String[]
+            { "Jalview Project" }, "Jalview Project");
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle("Restore state");
 
@@ -1004,13 +1229,32 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
-      String choice = chooser.getSelectedFile().getAbsolutePath();
-      setProgressBar("loading jalview project "
-              + chooser.getSelectedFile().getName(), choice.hashCode());
+      final String choice = chooser.getSelectedFile().getAbsolutePath();
       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
               .getSelectedFile().getParent());
-      new Jalview2XML().LoadJalviewAlign(choice);
-      setProgressBar(null, choice.hashCode());
+      new Thread(new Runnable()
+      {
+        public void run()
+        {
+          setProgressBar("loading jalview project " + choice,
+                  choice.hashCode());
+          try
+          {
+            new Jalview2XML().LoadJalviewAlign(choice);
+          } catch (OutOfMemoryError oom)
+          {
+            new OOMWarning("Whilst loading project from " + choice, oom);
+          } catch (Exception ex)
+          {
+            Cache.log.error("Problems whilst loading project from "
+                    + choice, ex);
+            JOptionPane.showMessageDialog(Desktop.desktop,
+                    "Error whilst loading project from " + choice,
+                    "Couldn't load project", JOptionPane.WARNING_MESSAGE);
+          }
+          setProgressBar(null, choice.hashCode());
+        }
+      }).start();
     }
   }
 
@@ -1253,13 +1497,12 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     if (v_client == null)
     {
       // Load and try to start a session.
-      JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
-              .getProperty("LAST_DIRECTORY"));
+      JalviewFileChooser chooser = new JalviewFileChooser(
+              jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
 
       chooser.setFileView(new JalviewFileView());
       chooser.setDialogTitle("Open a saved VAMSAS session");
-      chooser
-              .setToolTipText("select a vamsas session to be opened as a new vamsas session.");
+      chooser.setToolTipText("select a vamsas session to be opened as a new vamsas session.");
 
       int value = chooser.showOpenDialog(this);
 
@@ -1314,8 +1557,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       }
       bis.close();
       fos.close();
-      v_client = new jalview.gui.VamsasApplication(this, file, url
-              .toExternalForm());
+      v_client = new jalview.gui.VamsasApplication(this, file,
+              url.toExternalForm());
     } catch (Exception ex)
     {
       jalview.bin.Cache.log.error(
@@ -1344,8 +1587,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       return false;
     }
 
-    setProgressBar("Importing VAMSAS session from " + file.getName(), file
-            .hashCode());
+    setProgressBar("Importing VAMSAS session from " + file.getName(),
+            file.hashCode());
     try
     {
       v_client = new jalview.gui.VamsasApplication(this, file, null);
@@ -1359,8 +1602,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     }
     setupVamsasConnectedGui();
     v_client.initial_update(); // TODO: thread ?
-    setProgressBar("Importing VAMSAS session from " + file.getName(), file
-            .hashCode());
+    setProgressBar("Importing VAMSAS session from " + file.getName(),
+            file.hashCode());
     return v_client.inSession();
   }
 
@@ -1510,9 +1753,9 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   {
     if (v_client != null)
     {
-      JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
-              .getProperty("LAST_DIRECTORY"), new String[]
-      { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
+      JalviewFileChooser chooser = new JalviewFileChooser(
+              jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
+              { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
               new String[]
               { "Vamsas Document" }, "Vamsas Document");
 
@@ -1676,17 +1919,18 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
     public void paintComponent(Graphics g)
     {
-      if (showMemoryUsage && g != null)
+      if (showMemoryUsage && g != null && df != null)
       {
         if (percentUsage < 20)
           g.setColor(Color.red);
         FontMetrics fm = g.getFontMetrics();
         if (fm != null)
         {
-          g.drawString("Total Free Memory: " + df.format(totalFreeMemory)
-                  + "MB; Max Memory: " + df.format(maxMemory) + "MB; "
-                  + df.format(percentUsage) + "%", 10, getHeight()
-                  - fm.getHeight());
+          g.drawString(
+                  "Total Free Memory: " + df.format(totalFreeMemory)
+                          + "MB; Max Memory: " + df.format(maxMemory)
+                          + "MB; " + df.format(percentUsage) + "%", 10,
+                  getHeight() - fm.getHeight());
         }
       }
     }
@@ -1850,8 +2094,10 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         public void actionPerformed(ActionEvent e)
         {
           handler.cancelActivity(id);
-          us.setProgressBar("Cancelled "
-                  + ((JLabel) progressPanel.getComponent(0)).getText(), id);
+          us.setProgressBar(
+                  "Cancelled "
+                          + ((JLabel) progressPanel.getComponent(0))
+                                  .getText(), id);
         }
       });
       progressPanel.add(cancel, BorderLayout.EAST);
@@ -1887,9 +2133,135 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
   }
 
+  /**
+   * flag set if jalview GUI is being operated programmatically
+   */
+  private boolean inBatchMode = false;
+
+  /**
+   * check if jalview GUI is being operated programmatically
+   * 
+   * @return inBatchMode
+   */
+  public boolean isInBatchMode()
+  {
+    return inBatchMode;
+  }
+
+  /**
+   * set flag if jalview GUI is being operated programmatically
+   * 
+   * @param inBatchMode
+   */
+  public void setInBatchMode(boolean inBatchMode)
+  {
+    this.inBatchMode = inBatchMode;
+  }
+
   public void startServiceDiscovery()
   {
-    discoverer.start();
-    new Thread(jalview.ws.jws2.Jws2Discoverer.getDiscoverer()).start();
+    startServiceDiscovery(false);
   }
+
+  public void startServiceDiscovery(boolean blocking)
+  {
+    boolean alive = true;
+    Thread t0 = null, t1 = null, t2 = null;
+
+    // todo: changesupport handlers need to be transferred
+    if (discoverer == null)
+    {
+      discoverer = new jalview.ws.jws1.Discoverer();
+      // register PCS handler for desktop.
+      discoverer.addPropertyChangeListener(changeSupport);
+    }
+    if (Cache.getDefault("SHOW_JWS1_SERVICES", true))
+    {
+      (t0 = new Thread(discoverer)).start();
+    }
+
+    try
+    {
+      if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
+      {
+        // EnfinEnvision web service menu entries are rebuild every time the
+        // menu is shown, so no changeSupport events are needed.
+        jalview.ws.EnfinEnvision2OneWay.getInstance();
+        (t1 = new Thread(jalview.ws.EnfinEnvision2OneWay.getInstance()))
+                .start();
+      }
+    } catch (Exception e)
+    {
+      Cache.log
+              .info("Exception when trying to launch Envision2 workflow discovery.",
+                      e);
+      Cache.log.info(e.getStackTrace());
+    }
+    if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
+    {
+      jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
+              .addPropertyChangeListener(changeSupport);
+      (t2 = new Thread(jalview.ws.jws2.Jws2Discoverer.getDiscoverer()))
+              .start();
+    }
+    if (blocking)
+    {
+      while (alive)
+      {
+        try
+        {
+          Thread.sleep(15);
+        } catch (Exception e)
+        {
+        }
+        alive = (t1 != null && t1.isAlive())
+                || (t2 != null && t2.isAlive())
+                || (t0 != null && t0.isAlive());
+      }
+    }
+  }
+
+  /**
+   * start a thread to open a URL in the configured browser. Pops up a warning
+   * dialog to the user if there is an exception when calling out to the browser
+   * to open the URL.
+   * 
+   * @param url
+   */
+  public static void showUrl(final String url)
+  {
+    new Thread(new Runnable()
+    {
+      public void run()
+      {
+        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();
+        }
+      }
+    }).start();
+  }
+
+  public static WsParamSetManager wsparamManager = null;
+
+  public static ParamManager getUserParameterStore()
+  {
+    if (wsparamManager == null)
+    {
+      wsparamManager = new WsParamSetManager();
+    }
+    return wsparamManager;
+  }
+
 }