refactor to allow distinct StructureSelectionManager instances for
[jalview.git] / src / jalview / gui / Desktop.java
old mode 100755 (executable)
new mode 100644 (file)
index 8d824d7..db2dbf2
@@ -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,15 @@ 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.beans.PropertyVetoException;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -40,23 +44,58 @@ import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
 /**
- * DOCUMENT ME!
+ * Jalview Desktop
+ * 
  * 
  * @author $author$
- * @version $Revision$
+ * @version $Revision: 1.155 $
  */
 public class Desktop extends jalview.jbgui.GDesktop implements
-        DropTargetListener, ClipboardOwner, IProgressIndicator
+        DropTargetListener, ClipboardOwner, IProgressIndicator, jalview.api.StructureSelectionManagerProvider
 {
-  /** DOCUMENT ME!! */
+
+  private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
+
+  /**
+   * @param listener
+   * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
+   */
+  public void addJalviewPropertyChangeListener(
+          PropertyChangeListener listener)
+  {
+    changeSupport.addJalviewPropertyChangeListener(listener);
+  }
+
+  /**
+   * @param propertyName
+   * @param listener
+   * @see jalview.gui.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.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 +110,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 +237,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 +250,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 +292,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.
@@ -172,6 +308,20 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         Cache.log.debug("Filechooser init thread finished.");
       }
     }).start();
+    // Add the service change listener
+    changeSupport.addJalviewPropertyChangeListener("services",
+            new PropertyChangeListener()
+            {
+
+              @Override
+              public void propertyChange(PropertyChangeEvent evt)
+              {
+                Cache.log.debug("Firing service changed event for "
+                        + evt.getNewValue());
+                JalviewServicesChanged(evt);
+              }
+
+            });
   }
 
   /**
@@ -212,6 +362,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 +536,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()
     {
@@ -417,6 +578,47 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         }
       }
     });
+    menuItem.addMouseListener(new MouseListener()
+    {
+      
+      @Override
+      public void mouseReleased(MouseEvent e)
+      {
+      }
+      
+      @Override
+      public void mousePressed(MouseEvent e)
+      {
+      }
+      
+      @Override
+      public void mouseExited(MouseEvent e)
+      {
+        try
+        {
+          frame.setSelected(false);
+        } catch (PropertyVetoException e1)
+        {
+        }
+      }
+      
+      @Override
+      public void mouseEntered(MouseEvent e)
+      {
+        try
+        {
+          frame.setSelected(true);
+        } catch (PropertyVetoException e1)
+        {
+        }
+      }
+      
+      @Override
+      public void mouseClicked(MouseEvent e)
+      {
+        
+      }
+    });
 
     windowMenu.add(menuItem);
 
@@ -553,8 +755,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 +936,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:  Jim Procter, Andrew Waterhouse, 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 +1162,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 +1175,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 +1208,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 +1219,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();
     }
   }
 
@@ -1082,18 +1316,18 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     validate();
   }
 
-  public static int getViewCount(String viewId)
+  public static int getViewCount(String alignmentId)
   {
-    AlignViewport[] aps = getViewports(viewId);
+    AlignViewport[] aps = getViewports(alignmentId);
     return (aps == null) ? 0 : aps.length;
   }
 
   /**
    * 
-   * @param viewId
-   * @return all AlignmentPanels concerning the viewId sequence set
+   * @param alignmentId
+   * @return all AlignmentPanels concerning the alignmentId sequence set
    */
-  public static AlignmentPanel[] getAlignmentPanels(String viewId)
+  public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
   {
     int count = 0;
     if (Desktop.desktop == null)
@@ -1111,7 +1345,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         AlignFrame af = (AlignFrame) frames[t];
         for (int a = 0; a < af.alignPanels.size(); a++)
         {
-          if (viewId
+          if (alignmentId
                   .equals(((AlignmentPanel) af.alignPanels.elementAt(a)).av
                           .getSequenceSetId()))
           {
@@ -1253,13 +1487,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 +1547,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 +1577,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 +1592,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 +1743,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 +1909,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());
         }
       }
     }
@@ -1752,6 +1986,42 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     avp.clear();
     return afs;
   }
+  public AppJmol[] getJmols()
+  {
+    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+
+    if (frames == null)
+    {
+      return null;
+    }
+    Vector avp = new Vector();
+    try
+    {
+      // REVERSE ORDER
+      for (int i = frames.length - 1; i > -1; i--)
+      {
+        if (frames[i] instanceof AppJmol)
+        {
+          AppJmol af = (AppJmol) frames[i];
+          avp.addElement(af);
+        }
+      }
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
+    }
+    if (avp.size() == 0)
+    {
+      return null;
+    }
+    AppJmol afs[] = new AppJmol[avp.size()];
+    for (int i = 0, j = avp.size(); i < j; i++)
+    {
+      afs[i] = (AppJmol) avp.elementAt(i);
+    }
+    avp.clear();
+    return afs;
+  }
 
   /**
    * Add Groovy Support to Jalview
@@ -1850,8 +2120,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 +2159,208 @@ 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))
+    {
+      if (jalview.ws.jws2.Jws2Discoverer.getDiscoverer().isRunning())
+      {
+        jalview.ws.jws2.Jws2Discoverer.getDiscoverer().setAborted(true);
+      }
+      t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer(
+              changeSupport);
+      
+    }
+    Thread t3=null;
+    {
+      // TODO: do rest service discovery
+    }
+    if (blocking)
+    {
+      while (alive)
+      {
+        try
+        {
+          Thread.sleep(15);
+        } catch (Exception e)
+        {
+        }
+        alive = (t1 != null && t1.isAlive())
+                || (t2 != null && t2.isAlive())
+                || (t3 != null && t3.isAlive())
+                || (t0 != null && t0.isAlive());
+      }
+    }
+  }
+
+  /**
+   * called to check if the service discovery process completed successfully.
+   * 
+   * @param evt
+   */
+  protected void JalviewServicesChanged(PropertyChangeEvent evt)
+  {
+    if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
+    {
+      final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
+              .getErrorMessages();
+      if (ermsg != null)
+      {
+        if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
+        {
+        if (serviceChangedDialog == null)
+        {
+          // only run if we aren't already displaying one of these.
+          javax.swing.SwingUtilities
+                  .invokeLater(serviceChangedDialog = new Runnable()
+                  {
+                    public void run()
+                    {
+
+                      JOptionPane
+                              .showInternalMessageDialog(
+                                      Desktop.desktop,
+                                      ermsg
+                                                + "It may be that you have invalid JABA URLs\nin your web service preferences.\n\nGo to the Web services tab of the\nTools->Preferences dialog box to change them.\n",
+                                      "Preferences Problem",
+                                      JOptionPane.WARNING_MESSAGE);
+                      serviceChangedDialog = null;
+
+                    }
+                  });
+        }
+      }
+        else
+        {
+          Cache.log
+                  .error("Errors reported by JABA discovery service. Check web services preferences.\n"
+                          + ermsg);
+        }
+      }
+    }
+  }
+
+  private Runnable serviceChangedDialog = null;
+
+  /**
+   * 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)
+  {
+    showUrl(url, Desktop.instance);
+  }
+  /**
+   * Like showUrl but allows progress handler to be specified
+   * @param url
+   * @param progress (null) or object implementing IProgressIndicator
+   */
+  public static void showUrl(final String url, final IProgressIndicator progress)
+  {
+    new Thread(new Runnable()
+    {
+      public void run()
+      {
+        try
+        {
+          if (progress!=null) {
+            progress.setProgressBar("Opening "+url, this.hashCode());
+          }
+          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();
+        }
+        if (progress!=null) {
+          progress.setProgressBar(null, this.hashCode());
+        }
+      }
+    }).start();
+  }
+
+  public static WsParamSetManager wsparamManager = null;
+
+  public static ParamManager getUserParameterStore()
+  {
+    if (wsparamManager == null)
+    {
+      wsparamManager = new WsParamSetManager();
+    }
+    return wsparamManager;
   }
+
+
 }