Merge branch 'alpha/origin_2022_JAL-3066_Jalview_212_slivka-integration' into spike...
[jalview.git] / src / jalview / bin / Jalview.java
index 98dc039..eca8147 100755 (executable)
  */
 package jalview.bin;
 
-import jalview.api.AlignCalcWorkerI;
-import jalview.api.AlignViewportI;
-import jalview.api.JalviewApp;
-import jalview.api.StructureSelectionManagerProvider;
-import jalview.datamodel.ColumnSelection;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.ext.so.SequenceOntology;
-import jalview.gui.AlignFrame;
-import jalview.gui.AlignmentPanel;
-import jalview.gui.CalculationChooser;
-import jalview.gui.Desktop;
-import jalview.gui.PromptUserConfig;
-import jalview.gui.StructureViewer;
-import jalview.io.AppletFormatAdapter;
-import jalview.io.BioJsHTMLOutput;
-import jalview.io.DataSourceType;
-import jalview.io.FileFormat;
-import jalview.io.FileFormatException;
-import jalview.io.FileFormatI;
-import jalview.io.FileLoader;
-import jalview.io.HtmlSvgOutput;
-import jalview.io.IdentifyFile;
-import jalview.io.NewickFile;
-import jalview.io.gff.SequenceOntologyFactory;
-import jalview.javascript.JSFunctionExec;
-import jalview.javascript.MouseOverStructureListener;
-import jalview.renderer.seqfeatures.FeatureRenderer;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ColourSchemeProperty;
-import jalview.structure.SelectionSource;
-import jalview.structure.VamsasSource;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.ws.jws2.Jws2Discoverer;
-import netscape.javascript.JSObject;
+import java.awt.GraphicsEnvironment;
 
-import java.applet.AppletContext;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -77,20 +39,47 @@ import java.security.PermissionCollection;
 import java.security.Permissions;
 import java.security.Policy;
 import java.util.HashMap;
-import java.util.Hashtable;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Vector;
 import java.util.logging.ConsoleHandler;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import javax.swing.LookAndFeel;
 import javax.swing.UIManager;
+import javax.swing.UIManager.LookAndFeelInfo;
 
 import com.threerings.getdown.util.LaunchUtil;
 
 import groovy.lang.Binding;
 import groovy.util.GroovyScriptEngine;
+import jalview.api.AlignCalcWorkerI;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+import jalview.ext.so.SequenceOntology;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.Desktop;
+import jalview.gui.Preferences;
+import jalview.gui.PromptUserConfig;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.BioJsHTMLOutput;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatException;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
+import jalview.io.FileLoader;
+import jalview.io.HtmlSvgOutput;
+import jalview.io.IdentifyFile;
+import jalview.io.NewickFile;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.util.ChannelProperties;
+import jalview.util.HttpUtils;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.jws2.Jws2Discoverer;
 
 /**
  * Main class for Jalview Application <br>
@@ -107,28 +96,43 @@ import groovy.util.GroovyScriptEngine;
  * @author $author$
  * @version $Revision$
  */
-public class Jalview implements JalviewJSApi
+public class Jalview implements ApplicationSingletonI
 {
-  static
+  // for testing those nasty messages you cannot ever find.
+  // static
+  // {
+  // System.setOut(new PrintStream(new ByteArrayOutputStream())
+  // {
+  // @Override
+  // public void println(Object o)
+  // {
+  // if (o != null)
+  // {
+  // System.err.println(o);
+  // }
+  // }
+  //
+  // });
+  // }
+  public static Jalview getInstance()
   {
-    Platform.getURLCommandArguments();
+    return (Jalview) ApplicationSingletonProvider
+            .getInstance(Jalview.class);
   }
 
-  // singleton instance of this class
+  private Jalview()
+  {
+    Platform.getURLCommandArguments();
+  }
 
-  private static Jalview instance;
 
   private Desktop desktop;
 
-  public static AlignFrame currentAlignFrame;
-
-  public boolean isJavaAppletTag;
+  public AlignFrame currentAlignFrame;
 
   public String appletResourcePath;
 
-  JalviewAppLoader appLoader;
-
-  protected JSFunctionExec jsFunctionExec;
+  public String j2sAppletID;
 
   private boolean noCalculation, noMenuBar, noStatus;
 
@@ -154,7 +158,6 @@ public class Jalview implements JalviewJSApi
     return !noAnnotation;
   }
 
-
   static
   {
     if (!Platform.isJS())
@@ -165,21 +168,21 @@ public class Jalview implements JalviewJSApi
      */
     {
       // grab all the rights we can for the JVM
-           Policy.setPolicy(new Policy()
-           {
-             @Override
-             public PermissionCollection getPermissions(CodeSource codesource)
-             {
-               Permissions perms = new Permissions();
-               perms.add(new AllPermission());
-               return (perms);
-             }
-       
-             @Override
-             public void refresh()
-             {
-             }
-           });
+      Policy.setPolicy(new Policy()
+      {
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource)
+        {
+          Permissions perms = new Permissions();
+          perms.add(new AllPermission());
+          return (perms);
+        }
+
+        @Override
+        public void refresh()
+        {
+        }
+      });
     }
   }
 
@@ -242,27 +245,27 @@ public class Jalview implements JalviewJSApi
 
   }
 
-  public static Jalview getInstance()
-  {
-    return instance;
-  }
+  private final static boolean doPlatformLogging = false;
 
   /**
    * main class for Jalview application
    * 
    * @param args
-   *               open <em>filename</em>
+   *          open <em>filename</em>
    */
   public static void main(String[] args)
   {
-    if (false)
+    if (doPlatformLogging)
     {
       Platform.startJavaLogging();
     }
 
-    instance = new Jalview();
-    instance.doMain(args);
-}
+    getInstance().doMain(args);
+
+  }
+  
+
+  
 
   /**
    * @param args
@@ -270,71 +273,97 @@ public class Jalview implements JalviewJSApi
   void doMain(String[] args)
   {
 
-    if (!Platform.isJS())
+    boolean isJS = Platform.isJS();
+    if (!isJS)
     {
       System.setSecurityManager(null);
     }
 
+    /*
+     * @j2sNative J2S.db._DirectDatabaseCalls["compbio.dundee.ac.uk"]=null;
+     * @j2sNative J2S.db._DirectDatabaseCalls["jalview.org"]=null;
+     * 
+     */
     System.out
-            .println("Java version: "
-                    + System.getProperty("java.version"));
+            .println("Java version: " + System.getProperty("java.version"));
     System.out.println("Java Home: " + System.getProperty("java.home"));
     System.out.println(System.getProperty("os.arch") + " "
             + System.getProperty("os.name") + " "
             + System.getProperty("os.version"));
     String val = System.getProperty("sys.install4jVersion");
-    if (val != null) {
-    System.out.println("Install4j version: " + val);
+    if (val != null)
+    {
+      System.out.println("Install4j version: " + val);
     }
     val = System.getProperty("installer_template_version");
-    if (val != null) {
+    if (val != null)
+    {
       System.out.println("Install4j template version: " + val);
     }
     val = System.getProperty("launcher_version");
-    if (val != null) {
+    if (val != null)
+    {
       System.out.println("Launcher version: " + val);
     }
 
     // report Jalview version
-    Cache.loadBuildProperties(true);
+    Cache.getInstance().loadBuildProperties(true);
 
     ArgsParser aparser = new ArgsParser(args);
     boolean headless = false;
 
     String usrPropsFile = aparser.getValue("props");
     Cache.loadProperties(usrPropsFile); // must do this before
-    if (usrPropsFile != null)
+    boolean allowServices = true;
+    
+    if (isJS)
     {
-      System.out.println(
-              "CMD [-props " + usrPropsFile + "] executed successfully!");
+      j2sAppletID = Platform.getAppID(null);
+      Preferences.setAppletDefaults();
+      Cache.loadProperties(usrPropsFile); // again, because we
+      // might be changing defaults here?
+      appletResourcePath = (String) aparser.getAppletValue("resourcepath",
+              null, true);
     }
 
-    if (!Platform.isJS())
+    else
     /**
      * Java only
      * 
      * @j2sIgnore
      */
     {
+      if (usrPropsFile != null)
+      {
+        System.out.println(
+                "CMD [-props " + usrPropsFile + "] executed successfully!");
+      }
       if (aparser.contains("help") || aparser.contains("h"))
       {
         showUsage();
         System.exit(0);
       }
+      // BH note: Only -nodisplay is official; others are deprecated?
       if (aparser.contains("nodisplay") || aparser.contains("nogui")
-              || aparser.contains("headless"))
+              || aparser.contains("headless")
+              || GraphicsEnvironment.isHeadless())
       {
-        System.setProperty("java.awt.headless", "true");
+        if (!isJS) {
+          // BH Definitely not a good idea in JavaScript; 
+          // probably should not be here for Java, either.  
+          System.setProperty("java.awt.headless", "true");
+        }
         headless = true;
       }
       // anything else!
 
-      final String jabawsUrl = aparser.getValue("jabaws");
-      if (jabawsUrl != null)
+      final String jabawsUrl = aparser.getValue(ArgsParser.JABAWS);
+      allowServices = !("none".equals(jabawsUrl));
+      if (allowServices && jabawsUrl != null)
       {
         try
         {
-          Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
+          Jws2Discoverer.getInstance().setPreferredUrl(jabawsUrl);
           System.out.println(
                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
         } catch (MalformedURLException e)
@@ -345,7 +374,7 @@ public class Jalview implements JalviewJSApi
       }
 
     }
-    String defs = aparser.getValue("setprop");
+    String defs = aparser.getValue(ArgsParser.SETPROP);
     while (defs != null)
     {
       int p = defs.indexOf('=');
@@ -356,9 +385,9 @@ public class Jalview implements JalviewJSApi
       else
       {
         System.out.println("Executing setprop argument: " + defs);
-        if (Platform.isJS())
+        if (isJS)
         {
-          Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
+          Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
         }
       }
       defs = aparser.getValue("setprop");
@@ -383,57 +412,13 @@ public class Jalview implements JalviewJSApi
 
     desktop = null;
 
-    try
-    {
-      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
-    } catch (Exception ex)
-    {
-      System.err.println("Unexpected Look and Feel Exception");
-      ex.printStackTrace();
-    }
-    if (Platform.isAMacAndNotJS())
-    {
-
-      LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager
-              .getLookAndFeel();
-      System.setProperty("com.apple.mrj.application.apple.menu.about.name",
-              "Jalview");
-      System.setProperty("apple.laf.useScreenMenuBar", "true");
-      if (lookAndFeel != null)
-      {
-        try
-        {
-          UIManager.setLookAndFeel(lookAndFeel);
-        } catch (Throwable e)
-        {
-          System.err.println(
-                  "Failed to set QuaQua look and feel: " + e.toString());
-        }
-      }
-      if (lookAndFeel == null
-              || !(lookAndFeel.getClass().isAssignableFrom(
-                      UIManager.getLookAndFeel().getClass()))
-              || !UIManager.getLookAndFeel().getClass().toString()
-                      .toLowerCase().contains("quaqua"))
-      {
-        try
-        {
-          System.err.println(
-                  "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976");
-          UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
-        } catch (Throwable e)
-        {
-          System.err.println(
-                  "Failed to reset look and feel: " + e.toString());
-        }
-      }
-    }
+    setLookAndFeel();
 
     /*
      * configure 'full' SO model if preferences say to, else use the default (full SO)
      * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
      */
-    boolean soDefault = !Platform.isJS();
+    boolean soDefault = !isJS;
     if (Cache.getDefault("USE_FULL_SO", soDefault))
     {
       SequenceOntologyFactory.setInstance(new SequenceOntology());
@@ -441,27 +426,45 @@ public class Jalview implements JalviewJSApi
 
     if (!headless)
     {
-      desktop = new Desktop();
+      Desktop.nosplash = aparser.contains("nosplash");
+      desktop = Desktop.getInstance();
       desktop.setInBatchMode(true); // indicate we are starting up
 
       try
       {
         JalviewTaskbar.setTaskbar(this);
+      } catch (Exception e)
+      {
+        Cache.log.info("Cannot set Taskbar");
+        Cache.log.error(e.getMessage());
+        // e.printStackTrace();
       } catch (Throwable t)
       {
-        System.out.println("Error setting Taskbar: " + t.getMessage());
+        Cache.log.info("Cannot set Taskbar");
+        Cache.log.error(t.getMessage());
+        // t.printStackTrace();
       }
+      // set Proxy settings before all the internet calls
+      Cache.setProxyPropertiesFromPreferences();
 
       desktop.setVisible(true);
 
-      if (!Platform.isJS())
+      if (isJS)
+      {
+        Cache.setProperty("SHOW_JWS2_SERVICES", "false");
+      }
+      if (allowServices && !aparser.contains("nowebservicediscovery"))
+      {
+        desktop.startServiceDiscovery();
+      }
+
+      if (!isJS)
       /**
        * Java only
        * 
        * @j2sIgnore
        */
       {
-        desktop.startServiceDiscovery();
         if (!aparser.contains("nousagestats"))
         {
           startUsageStats(desktop);
@@ -492,7 +495,7 @@ public class Jalview implements JalviewJSApi
               // String defurl =
               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
               // //
-              String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl";
+              String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
               Cache.log.debug(
                       "Starting questionnaire with default url: " + defurl);
               desktop.checkForQuestionnaire(defurl);
@@ -513,45 +516,127 @@ public class Jalview implements JalviewJSApi
         BioJsHTMLOutput.updateBioJS();
       }
     }
+    parseArguments(aparser, true);
+  }
+
+  /**
+   * Parse all command-line String[] arguments as well as all JavaScript-derived
+   * parameters from Info.
+   * 
+   * We allow for this method to be run from JavaScript. Basically allowing
+   * simple scripting.
+   * 
+   * @param aparser
+   * @param isStartup
+   */
+  public void parseArguments(ArgsParser aparser, boolean isStartup)
+  {
 
-    // Move any new getdown-launcher-new.jar into place over old
-    // getdown-launcher.jar
-    String appdirString = System.getProperty("getdownappdir");
-    if (appdirString != null && appdirString.length() > 0)
+    String groovyscript = null; // script to execute after all loading is
+    boolean isJS = Platform.isJS();
+    if (!isJS)
+    /** @j2sIgnore */
     {
-      final File appdir = new File(appdirString);
-      new Thread()
+      // Move any new getdown-launcher-new.jar into place over old
+      // getdown-launcher.jar
+      String appdirString = System.getProperty("getdownappdir");
+      if (appdirString != null && appdirString.length() > 0)
       {
-        @Override
-        public void run()
+        final File appdir = new File(appdirString);
+        new Thread()
         {
-          LaunchUtil.upgradeGetdown(
-                  new File(appdir, "getdown-launcher-old.jar"),
-                  new File(appdir, "getdown-launcher.jar"),
-                  new File(appdir, "getdown-launcher-new.jar"));
-        }
-      }.start();
-    }
+          @Override
+          public void run()
+          {
+            LaunchUtil.upgradeGetdown(
+                    new File(appdir, "getdown-launcher-old.jar"),
+                    new File(appdir, "getdown-launcher.jar"),
+                    new File(appdir, "getdown-launcher-new.jar"));
+          }
+        }.start();
+      }
 
-    String file = null, data = null;
-    FileFormatI format = null;
-    DataSourceType protocol = null;
-    FileLoader fileLoader = new FileLoader(!headless);
+      // completed one way or another
+      // extract groovy argument and execute if necessary
+      groovyscript = aparser.getValue("groovy", true);
+    }
 
-    String groovyscript = null; // script to execute after all loading is
-    // completed one way or another
-    // extract groovy argument and execute if necessary
-    groovyscript = aparser.getValue("groovy", true);
-    file = aparser.getValue("open", true);
+    String file = aparser.getValue("open", true);
 
-    if (file == null && desktop == null)
+    if (!isJS && file == null && desktop == null)
     {
       System.out.println("No files to open!");
       System.exit(1);
     }
+    setDisplayParameters(aparser);
+    
+    // time to open a file.
     long progress = -1;
+    DataSourceType protocol = null;
+    FileLoader fileLoader = new FileLoader(!headless);
+    FileFormatI format = null;
     // Finally, deal with the remaining input data.
-    if (file != null)
+    AlignFrame af = null;
+
+    JalviewJSApp jsApp = (isJS ? new JalviewJSApp(this, aparser) : null);
+
+    if (file == null)
+    {
+      if (isJS)
+      {
+        // JalviewJS allows sequence1 sequence2 ....
+        
+      }
+      else if (!headless && Cache.getDefault("SHOW_STARTUP_FILE", true))
+      /**
+       * Java only
+       * 
+       * @j2sIgnore
+       */
+      {
+
+        // We'll only open the default file if the desktop is visible.
+        // And the user
+        // ////////////////////
+
+        file = Cache.getDefault("STARTUP_FILE",
+                Cache.getDefault("www.jalview.org",
+                        "http://www.jalview.org")
+                        + "/examples/exampleFile_2_7.jar");
+        if (file.equals(
+                "http://www.jalview.org/examples/exampleFile_2_3.jar"))
+        {
+          // hardwire upgrade of the startup file
+          file.replace("_2_3.jar", "_2_7.jar");
+          // and remove the stale setting
+          Cache.removeProperty("STARTUP_FILE");
+        }
+
+        protocol = DataSourceType.FILE;
+
+        if (file.indexOf("http:") > -1)
+        {
+          protocol = DataSourceType.URL;
+        }
+
+        if (file.endsWith(".jar"))
+        {
+          format = FileFormat.Jalview;
+        }
+        else
+        {
+          try
+          {
+            format = new IdentifyFile().identify(file, protocol);
+          } catch (FileFormatException e)
+          {
+            // TODO what?
+          }
+        }
+        af = fileLoader.LoadFileWaitTillLoaded(file, protocol, format);
+      }
+    }
+    else
     {
       if (!headless)
       {
@@ -563,14 +648,13 @@ public class Jalview implements JalviewJSApi
       System.out.println("CMD [-open " + file + "] executed successfully!");
 
       if (!Platform.isJS())
-        /**
-         * ignore in JavaScript -- can't just file existence - could load it?
-         * 
-         * @j2sIgnore
-         */
+      /**
+       * ignore in JavaScript -- can't just file existence - could load it?
+       * 
+       * @j2sIgnore
+       */
       {
-        if (!file.startsWith("http://") && !file.startsWith("https://"))
-        // BH 2019 added https check for Java
+        if (!HttpUtils.startsWithHttpOrHttps(file))
         {
           if (!(new File(file)).exists())
           {
@@ -583,291 +667,619 @@ public class Jalview implements JalviewJSApi
         }
       }
 
-        protocol = AppletFormatAdapter.checkProtocol(file);
+      // JS Only argument to provide a format parameter to specify what format to use
+      String fileFormat = (isJS
+              ? (String) aparser.getAppletValue("format", null, true)
+              : null);
+      protocol = AppletFormatAdapter.checkProtocol(file);
 
       try
       {
-        format = new IdentifyFile().identify(file, protocol);
+        format = (fileFormat != null
+                ? FileFormats.getInstance().forName(fileFormat)
+                : null);
+        if (format == null)
+        {
+          format = new IdentifyFile().identify(file, protocol);
+        }
       } catch (FileFormatException e1)
       {
         // TODO ?
       }
 
-      AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
+      af = new FileLoader(!headless).LoadFileWaitTillLoaded(file, protocol,
               format);
       if (af == null)
       {
-        System.out.println("error");
+        System.out.println("jalview error - AlignFrame was not created");
       }
       else
       {
-        setCurrentAlignFrame(af);
-        data = aparser.getValue("colour", true);
-        if (data != null)
-        {
-          data.replaceAll("%20", " ");
-
-          ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
-                  af.getViewport(), af.getViewport().getAlignment(), data);
-
-          if (cs != null)
-          {
-            System.out.println(
-                    "CMD [-color " + data + "] executed successfully!");
-          }
-          af.changeColour(cs);
-        }
-
-        // Must maintain ability to use the groups flag
-        data = aparser.getValue("groups", true);
-        if (data != null)
-        {
-          af.parseFeaturesFile(data,
-                  AppletFormatAdapter.checkProtocol(data));
-          // System.out.println("Added " + data);
-          System.out.println(
-                  "CMD groups[-" + data + "]  executed successfully!");
-        }
-        data = aparser.getValue("features", true);
-        if (data != null)
-        {
-          af.parseFeaturesFile(data,
-                  AppletFormatAdapter.checkProtocol(data));
-          // System.out.println("Added " + data);
-          System.out.println(
-                  "CMD [-features " + data + "]  executed successfully!");
-        }
-
-        data = aparser.getValue("annotations", true);
-        if (data != null)
-        {
-          af.loadJalviewDataFile(data, null, null, null);
-          // System.out.println("Added " + data);
-          System.out.println(
-                  "CMD [-annotations " + data + "] executed successfully!");
-        }
-        // set or clear the sortbytree flag.
-        if (aparser.contains("sortbytree"))
+        
+        // JalviewLite interface for JavaScript allows second file open
+        String file2 = aparser.getValue(ArgsParser.OPEN2, true);
+        if (file2 != null)
         {
-          af.getViewport().setSortByTree(true);
-          if (af.getViewport().getSortByTree())
+          protocol = AppletFormatAdapter.checkProtocol(file2);
+          try
           {
-            System.out.println("CMD [-sortbytree] executed successfully!");
-          }
-        }
-        if (aparser.contains("no-annotation"))
-        {
-          af.getViewport().setShowAnnotation(false);
-          if (!af.getViewport().isShowAnnotation())
+            format = new IdentifyFile().identify(file2, protocol);
+          } catch (FileFormatException e1)
           {
-            System.out.println("CMD no-annotation executed successfully!");
+            // TODO ?
           }
-        }
-        if (aparser.contains("nosortbytree"))
-        {
-          af.getViewport().setSortByTree(false);
-          if (!af.getViewport().getSortByTree())
+          AlignFrame af2 = new FileLoader(!headless)
+                  .LoadFileWaitTillLoaded(file2, protocol, format);
+          if (af2 == null)
           {
-            System.out
-                    .println("CMD [-nosortbytree] executed successfully!");
+            System.out.println("error");
           }
-        }
-        data = aparser.getValue("tree", true);
-        if (data != null)
-        {
-          try
+          else
           {
+            AlignViewport.openLinkedAlignmentAs(af,
+                    af.getViewport().getAlignment(),
+                    af2.getViewport().getAlignment(), "",
+                    AlignViewport.SPLIT_FRAME);
             System.out.println(
-                    "CMD [-tree " + data + "] executed successfully!");
-            NewickFile nf = new NewickFile(data,
-                    AppletFormatAdapter.checkProtocol(data));
-            af.getViewport()
-                    .setCurrentTree(af.showNewickTree(nf, data).getTree());
-          } catch (IOException ex)
-          {
-            System.err.println("Couldn't add tree " + data);
-            ex.printStackTrace(System.err);
+                    "CMD [-open2 " + file2 + "] executed successfully!");
           }
         }
-        // TODO - load PDB structure(s) to alignment JAL-629
-        // (associate with identical sequence in alignment, or a specified
-        // sequence)
-        if (groovyscript != null)
+        // af is loaded - so set it as current frame
+        setCurrentAlignFrame(af);
+
+        setFrameDependentProperties(aparser, af);
+        
+        if (isJS)
         {
-          // Execute the groovy script after we've done all the rendering stuff
-          // and before any images or figures are generated.
-          System.out.println("Executing script " + groovyscript);
-          executeGroovyScript(groovyscript, af);
-          System.out.println("CMD groovy[" + groovyscript
-                  + "] executed successfully!");
-          groovyscript = null;
+          jsApp.initFromParams(af);
         }
-        String imageName = "unnamed.png";
-        while (aparser.getSize() > 1)
+        else
+        /**
+         * Java only
+         * 
+         * @j2sIgnore
+         */
         {
-          String outputFormat = aparser.nextValue();
-          file = aparser.nextValue();
-
-          if (outputFormat.equalsIgnoreCase("png"))
-          {
-            af.createPNG(new File(file));
-            imageName = (new File(file)).getName();
-            System.out.println("Creating PNG image: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("svg"))
-          {
-            File imageFile = new File(file);
-            imageName = imageFile.getName();
-            af.createSVG(imageFile);
-            System.out.println("Creating SVG image: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("html"))
-          {
-            File imageFile = new File(file);
-            imageName = imageFile.getName();
-            HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
-            htmlSVG.exportHTML(file);
-
-            System.out.println("Creating HTML image: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("biojsmsa"))
-          {
-            if (file == null)
-            {
-              System.err.println("The output html file must not be null");
-              return;
-            }
-            try
-            {
-              BioJsHTMLOutput.refreshVersionInfo(
-                      BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
-            } catch (URISyntaxException e)
-            {
-              e.printStackTrace();
-            }
-            BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
-            bjs.exportHTML(file);
-            System.out
-                    .println("Creating BioJS MSA Viwer HTML file: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("imgMap"))
-          {
-            af.createImageMap(new File(file), imageName);
-            System.out.println("Creating image map: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("eps"))
-          {
-            File outputFile = new File(file);
-            System.out.println(
-                    "Creating EPS file: " + outputFile.getAbsolutePath());
-            af.createEPS(outputFile);
-            continue;
-          }
-
-          af.saveAlignment(file, format);
-          if (af.isSaveAlignmentSuccessful())
-          {
-            System.out.println("Written alignment in " + format
-                    + " format to " + file);
-          }
-          else
+          if (groovyscript != null)
           {
-            System.out.println("Error writing file " + file + " in "
-                    + format + " format!!");
+            // Execute the groovy script after we've done all the rendering
+            // stuff
+            // and before any images or figures are generated.
+            System.out.println("Executing script " + groovyscript);
+            executeGroovyScript(groovyscript, af);
+            System.out.println("CMD groovy[" + groovyscript
+                    + "] executed successfully!");
+            groovyscript = null;
           }
-
         }
-
-        while (aparser.getSize() > 0)
-        {
-          System.out.println("Unknown arg: " + aparser.nextValue());
+        if (!isJS || !isStartup) {
+          createOutputFiles(aparser, format);
         }
       }
+      if (headless)
+      {
+        af.getViewport().getCalcManager().shutdown();
+      }
     }
-    AlignFrame startUpAlframe = null;
-    // We'll only open the default file if the desktop is visible.
-    // And the user
-    // ////////////////////
-
-    if (!Platform.isJS() && !headless && file == null
-            && Cache.getDefault("SHOW_STARTUP_FILE", true))
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
+    // extract groovy arguments before anything else.
+    // Once all other stuff is done, execute any groovy scripts (in order)
+    if (!isJS && groovyscript != null)
     {
-      file = Cache.getDefault("STARTUP_FILE",
-              Cache.getDefault("www.jalview.org",
-                      "http://www.jalview.org")
-                      + "/examples/exampleFile_2_7.jar");
-      if (file.equals(
-              "http://www.jalview.org/examples/exampleFile_2_3.jar"))
+      if (Cache.groovyJarsPresent())
       {
-        // hardwire upgrade of the startup file
-        file.replace("_2_3.jar", "_2_7.jar");
-        // and remove the stale setting
-        Cache.removeProperty("STARTUP_FILE");
+        // TODO: DECIDE IF THIS SECOND PASS AT GROOVY EXECUTION IS STILL REQUIRED !!
+        System.out.println("Executing script " + groovyscript);
+        executeGroovyScript(groovyscript, af);
+        System.out.println("CMD groovy[" + groovyscript
+                    + "] executed successfully!");
+
+      }
+      else
+      {
+        System.err.println(
+                "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
+                        + groovyscript);
+      }
+    }
+
+    // and finally, turn off batch mode indicator - if the desktop still exists
+    if (desktop != null)
+    {
+      if (progress != -1)
+      {
+        desktop.setProgressBar(null, progress);
       }
+      desktop.setInBatchMode(false);
+    }
+    
+    if (jsApp != null) {
+      jsApp.callInitCallback();
+    }
+  }
+  
+  /**
+   * Set general display parameters irrespective of file loading or headlessness.
+   * 
+   * @param aparser
+   */
+  private void setDisplayParameters(ArgsParser aparser)
+  {
+    if (aparser.contains(ArgsParser.NOMENUBAR))
+    {
+      noMenuBar = true;
+      System.out.println("CMD [nomenu] executed successfully!");
+    }
+
+    if (aparser.contains(ArgsParser.NOSTATUS))
+    {
+      noStatus = true;
+      System.out.println("CMD [nostatus] executed successfully!");
+    }
+
+    if (aparser.contains(ArgsParser.NOANNOTATION)
+            || aparser.contains(ArgsParser.NOANNOTATION2))
+    {
+      noAnnotation = true;
+      System.out.println("CMD no-annotation executed successfully!");
+    }
+    if (aparser.contains(ArgsParser.NOCALCULATION))
+    {
+      noCalculation = true;
+      System.out.println("CMD [nocalculation] executed successfully!");
+    }
+  }
+
 
-      protocol = DataSourceType.FILE;
+  private void setFrameDependentProperties(ArgsParser aparser,
+          AlignFrame af)
+  {
+    String data = aparser.getValue(ArgsParser.COLOUR, true);
+    if (data != null)
+    {
+      data.replaceAll("%20", " ");
 
-      if (file.indexOf("http:") > -1)
+      ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
+              af.getViewport(), af.getViewport().getAlignment(), data);
+
+      if (cs != null)
       {
-        protocol = DataSourceType.URL;
+        System.out.println(
+                "CMD [-color " + data + "] executed successfully!");
       }
+      af.changeColour(cs);
+    }
+
+    // Must maintain ability to use the groups flag
+    data = aparser.getValue(ArgsParser.GROUPS, true);
+    if (data != null)
+    {
+      af.parseFeaturesFile(data,
+              AppletFormatAdapter.checkProtocol(data));
+      // System.out.println("Added " + data);
+      System.out.println(
+              "CMD groups[-" + data + "]  executed successfully!");
+    }
+    data = aparser.getValue(ArgsParser.FEATURES, true);
+    if (data != null)
+    {
+      af.parseFeaturesFile(data,
+              AppletFormatAdapter.checkProtocol(data));
+      // System.out.println("Added " + data);
+      System.out.println(
+              "CMD [-features " + data + "]  executed successfully!");
+    }
+    data = aparser.getValue(ArgsParser.ANNOTATIONS, true);
+    if (data != null)
+    {
+      af.loadJalviewDataFile(data, null, null, null);
+      // System.out.println("Added " + data);
+      System.out.println(
+              "CMD [-annotations " + data + "] executed successfully!");
+    }
+
+    // JavaScript feature
+
+    if (aparser.contains(ArgsParser.SHOWOVERVIEW))
+    {
+      af.overviewMenuItem_actionPerformed(null);
+      System.out.println("CMD [showoverview] executed successfully!");
+    }
 
-      if (file.endsWith(".jar"))
+    // set or clear the sortbytree flag.
+    if (aparser.contains(ArgsParser.SORTBYTREE))
+    {
+      af.getViewport().setSortByTree(true);
+      if (af.getViewport().getSortByTree())
       {
-        format = FileFormat.Jalview;
+        System.out.println("CMD [-sortbytree] executed successfully!");
       }
-      else
+    }
+
+    boolean doUpdateAnnotation = false;
+    /**
+     * we do this earlier in JalviewJS because of a complication with
+     * SHOWOVERVIEW
+     * 
+     * For now, just fixing this in JalviewJS.
+     *
+     * 
+     * @j2sIgnore
+     * 
+     */
+    {
+      if (noAnnotation)
+      {
+        af.getViewport().setShowAnnotation(false);
+        if (!af.getViewport().isShowAnnotation())
+        {
+          doUpdateAnnotation = true;
+        }
+      }
+    }
+
+    if (aparser.contains(ArgsParser.NOSORTBYTREE))
+    {
+      af.getViewport().setSortByTree(false);
+      if (!af.getViewport().getSortByTree())
+      {
+        doUpdateAnnotation = true;
+        System.out
+                .println("CMD [-nosortbytree] executed successfully!");
+      }
+    }
+    if (doUpdateAnnotation)
+    { // BH 2019.07.24
+      af.setMenusForViewport();
+      af.alignPanel.updateLayout();
+    }
+
+    data = aparser.getValue(ArgsParser.TREE, true);
+    if (data != null)
+    {
+      try
+      {
+        NewickFile nf = new NewickFile(data,
+                AppletFormatAdapter.checkProtocol(data));
+        af.getViewport()
+                .setCurrentTree(af.showNewickTree(nf, data).getTree());
+        System.out.println(
+                "CMD [-tree " + data + "] executed successfully!");
+      } catch (IOException ex)
+      {
+        System.err.println("Couldn't add tree " + data);
+        ex.printStackTrace(System.err);
+      }
+    }
+    // TODO - load PDB structure(s) to alignment JAL-629
+    // (associate with identical sequence in alignment, or a specified
+    // sequence)
+
+  }
+
+  /**
+   * Writes an output file for each format (if any) specified in the
+   * command-line arguments. Supported formats are currently
+   * <ul>
+   * <li>png</li>
+   * <li>svg</li>
+   * <li>html</li>
+   * <li>biojsmsa</li>
+   * <li>imgMap</li>
+   * <li>eps</li>
+   * </ul>
+   * A format parameter should be followed by a parameter specifying the output
+   * file name. {@code imgMap} parameters should follow those for the
+   * corresponding alignment image output.
+   * 
+   * @param aparser
+   * @param format
+   */
+  private void createOutputFiles(ArgsParser aparser, FileFormatI format)
+  {
+    // logic essentially the same as 2.11.2/2.11.3 but uses a switch instead
+    AlignFrame af = currentAlignFrame;
+    while (aparser.getSize() >= 2)
+    {
+      String outputFormat = aparser.nextValue();
+      File imageFile;
+      String fname;
+      switch (outputFormat.toLowerCase(Locale.ROOT))
       {
+      case "png":
+        imageFile = new File(aparser.nextValue());
+        af.createPNG(imageFile);
+        System.out.println(
+                "Creating PNG image: " + imageFile.getAbsolutePath());
+        continue;
+      case "svg":
+        imageFile = new File(aparser.nextValue());
+        af.createSVG(imageFile);
+        System.out.println(
+                "Creating SVG image: " + imageFile.getAbsolutePath());
+        continue;
+      case "eps":
+        imageFile = new File(aparser.nextValue());
+        System.out.println(
+                "Creating EPS file: " + imageFile.getAbsolutePath());
+        af.createEPS(imageFile);
+        continue;
+      case "biojsmsa":
+        fname = new File(aparser.nextValue()).getAbsolutePath();
         try
         {
-          format = new IdentifyFile().identify(file, protocol);
-        } catch (FileFormatException e)
+          BioJsHTMLOutput.refreshVersionInfo(
+                  BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
+        } catch (URISyntaxException e)
+        {
+          e.printStackTrace();
+        }
+        BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
+        bjs.exportHTML(fname);
+        System.out.println("Creating BioJS MSA Viwer HTML file: " + fname);
+        continue;
+      case "html":
+        fname = new File(aparser.nextValue()).getAbsolutePath();
+        HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
+        htmlSVG.exportHTML(fname);
+        System.out.println("Creating HTML image: " + fname);
+        continue;
+      case "imgmap":
+        imageFile = new File(aparser.nextValue());
+        af.alignPanel.makePNGImageMap(imageFile, "unnamed.png");
+        System.out.println(
+                "Creating image map: " + imageFile.getAbsolutePath());
+        continue;
+      default:
+        // fall through - try to parse as an alignment data export format
+        FileFormatI outFormat = null;
+        try
+        {
+          outFormat = FileFormats.getInstance().forName(outputFormat);
+        } catch (Exception formatP)
+        {
+        }
+        if (outFormat == null)
+        {
+          System.out.println("Couldn't parse " + outputFormat
+                  + " as a valid Jalview format string.");
+          continue;
+        }
+        if (!outFormat.isWritable())
+        {
+          System.out.println(
+                  "This version of Jalview does not support alignment export as "
+                          + outputFormat);
+          continue;
+        }
+        // record file as it was passed to Jalview so it is recognisable to the CLI
+        // caller
+        String file;
+        fname = new File(file = aparser.nextValue()).getAbsolutePath();
+        // JBPNote - yuck - really wish we did have a bean returned from this which gave
+        // success/fail like before !
+        af.saveAlignment(fname, outFormat);
+        if (!af.isSaveAlignmentSuccessful())
         {
-          // TODO what?
+          System.out.println("Written alignment in " + outputFormat
+                  + " format to " + file);
+          continue;
+        }
+        else
+        {
+          System.out.println("Error writing file " + file + " in "
+                  + outputFormat + " format!!");
         }
       }
-
-      startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
-              format);
-      // extract groovy arguments before anything else.
     }
+    // ??? Should report - 'ignoring' extra args here...
+    while (aparser.getSize() > 0)
+    {
+      System.out.println("Ignoring extra argument: " + aparser.nextValue());
+    }
+  }
 
-    // Once all other stuff is done, execute any groovy scripts (in order)
-    if (groovyscript != null)
+  private static void setLookAndFeel()
+  {
+    // property laf = "crossplatform", "system", "gtk", "metal", "nimbus" or
+    // "mac"
+    // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
+    // try Quaqua/Vaqua.
+    String lafProp = System.getProperty("laf");
+    String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
+    String laf = "none";
+    if (lafProp != null)
     {
-      if (Cache.groovyJarsPresent())
+      laf = lafProp;
+    }
+    else if (lafSetting != null)
+    {
+      laf = lafSetting;
+    }
+    boolean lafSet = false;
+    switch (laf)
+    {
+    case "crossplatform":
+      lafSet = setCrossPlatformLookAndFeel();
+      if (!lafSet)
       {
-        System.out.println("Executing script " + groovyscript);
-        executeGroovyScript(groovyscript, startUpAlframe);
+        Cache.log.error("Could not set requested laf=" + laf);
       }
-      else
+      break;
+    case "system":
+      lafSet = setSystemLookAndFeel();
+      if (!lafSet)
       {
-        System.err.println(
-                "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
-                        + groovyscript);
+        Cache.log.error("Could not set requested laf=" + laf);
       }
+      break;
+    case "gtk":
+      lafSet = setGtkLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "metal":
+      lafSet = setMetalLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "nimbus":
+      lafSet = setNimbusLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "quaqua":
+      lafSet = setQuaquaLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "vaqua":
+      lafSet = setVaquaLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "mac":
+      lafSet = setMacLookAndFeel();
+      if (!lafSet)
+      {
+        Cache.log.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "none":
+      break;
+    default:
+      Cache.log.error("Requested laf=" + laf + " not implemented");
     }
-    // and finally, turn off batch mode indicator - if the desktop still exists
-    if (desktop != null)
+    if (!lafSet)
     {
-      if (progress != -1)
+      setSystemLookAndFeel();
+      if (Platform.isLinux())
       {
-        desktop.setProgressBar(null, progress);
+        setMetalLookAndFeel();
       }
-      desktop.setInBatchMode(false);
+      if (Platform.isMac())
+      {
+        setMacLookAndFeel();
+      }
+    }
+  }
+
+  private static boolean setCrossPlatformLookAndFeel()
+  {
+    boolean set = false;
+    try
+    {
+      UIManager.setLookAndFeel(
+              UIManager.getCrossPlatformLookAndFeelClassName());
+      set = true;
+    } catch (Exception ex)
+    {
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
+    }
+    return set;
+  }
+
+  private static boolean setSystemLookAndFeel()
+  {
+    boolean set = false;
+    try
+    {
+      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+      set = true;
+    } catch (Exception ex)
+    {
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
+    }
+    return set;
+  }
+
+  private static boolean setSpecificLookAndFeel(String name,
+          String className, boolean nameStartsWith)
+  {
+    boolean set = false;
+    try
+    {
+      for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
+      {
+        if (info.getName() != null && nameStartsWith
+                ? info.getName().toLowerCase(Locale.ROOT)
+                        .startsWith(name.toLowerCase(Locale.ROOT))
+                : info.getName().toLowerCase(Locale.ROOT).equals(name.toLowerCase(Locale.ROOT)))
+        {
+          className = info.getClassName();
+          break;
+        }
+      }
+      UIManager.setLookAndFeel(className);
+      set = true;
+    } catch (Exception ex)
+    {
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
     }
+    return set;
   }
 
+  private static boolean setGtkLookAndFeel()
+  {
+    return setSpecificLookAndFeel("gtk",
+            "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
+  }
+
+  private static boolean setMetalLookAndFeel()
+  {
+    return setSpecificLookAndFeel("metal",
+            "javax.swing.plaf.metal.MetalLookAndFeel", false);
+  }
+
+  private static boolean setNimbusLookAndFeel()
+  {
+    return setSpecificLookAndFeel("nimbus",
+            "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
+  }
+
+  private static boolean setQuaquaLookAndFeel()
+  {
+    return setSpecificLookAndFeel("quaqua",
+            ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass()
+                    .getName(),
+            false);
+  }
+
+  private static boolean setVaquaLookAndFeel()
+  {
+    return setSpecificLookAndFeel("vaqua",
+            "org.violetlib.aqua.AquaLookAndFeel", false);
+  }
+
+  private static boolean setMacLookAndFeel()
+  {
+    boolean set = false;
+    System.setProperty("com.apple.mrj.application.apple.menu.about.name",
+            ChannelProperties.getProperty("app_name"));
+    System.setProperty("apple.laf.useScreenMenuBar", "true");
+    set = setQuaquaLookAndFeel();
+    if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
+            .toLowerCase(Locale.ROOT).contains("quaqua"))
+    {
+      set = setVaquaLookAndFeel();
+    }
+    return set;
+  }
   private static void showUsage()
   {
     System.out.println(
@@ -906,7 +1318,9 @@ public class Jalview implements JalviewJSApi
                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
                     + "-groovy FILE\tExecute groovy script in FILE, after all other arguments have been processed (if FILE is the text 'STDIN' then the file will be read from STDIN)\n"
-                    + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
+                    + "-jvmmempc=PERCENT\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
+                    + "-jvmmemmax=MAXMEMORY\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), gigabytes(g) or if you're lucky enough, terabytes(t). This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
+                    + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
   }
 
   private static void startUsageStats(final Desktop desktop)
@@ -914,7 +1328,7 @@ public class Jalview implements JalviewJSApi
     /**
      * start a User Config prompt asking if we can log usage statistics.
      */
-    PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
+    PromptUserConfig prompter = new PromptUserConfig(Desktop.getDesktopPane(),
             "USAGESTATS", "Jalview Usage Statistics",
             "Do you want to help make Jalview better by enabling "
                     + "the collection of usage statistics with Google Analytics ?"
@@ -944,10 +1358,10 @@ public class Jalview implements JalviewJSApi
    * Locate the given string as a file and pass it to the groovy interpreter.
    * 
    * @param groovyscript
-   *                         the script to execute
+   *          the script to execute
    * @param jalviewContext
-   *                         the Jalview Desktop object passed in to the groovy
-   *                         binding as the 'Jalview' object.
+   *          the Jalview Desktop object passed in to the groovy binding as the
+   *          'Jalview' object.
    */
   private void executeGroovyScript(String groovyscript, AlignFrame af)
   {
@@ -1093,824 +1507,30 @@ public class Jalview implements JalviewJSApi
 
   public static AlignFrame getCurrentAlignFrame()
   {
-    return Jalview.currentAlignFrame;
+    return Jalview.getInstance().currentAlignFrame;
   }
 
   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
   {
-    Jalview.currentAlignFrame = currentAlignFrame;
+    Jalview.getInstance().currentAlignFrame = currentAlignFrame;
   }
   
-  /**
-   * Get the SwingJS applet ID and combine that with the frameType
-   * 
-   * @param frameType
-   *          "alignment", "desktop", etc., or null
-   * @return
-   */
-  public static String getAppID(String frameType)
+  public void notifyWorker(AlignCalcWorkerI worker, String status)
   {
-    String id = Cache.getProperty("Info.j2sAppletID");
-    if (id == null)
-    {
-      id = "jalview";
-    }
-    return id + (frameType == null ? "" : "-" + frameType);
+    // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
+    // + " " + status);
   }
 
-  /**
-   * Handle all JalviewLite applet parameters
-   * 
-   * @param aparser
-   * @param af
-   */
-  private void loadAppletParams(ArgsParser aparser, AlignFrame af)
-  {
-    JalviewApp app = new JalviewApp()
-    {
-
-      // TODO BH 2019
-      //
-      // These are methods that are in JalviewLite that various classes call
-      // but are not in JalviewLiteJsApi. Or, even if they are, other classes
-      // call
-      // them to JalviewLite directly. Some may not be necessary, but they have
-      // to
-      // be at least mentioned here, or the classes calling them should
-      // reference
-      // JalviewLite itself.
-
-      private boolean alignPDBStructures; // From JalviewLite; not implemented
-
-      private Hashtable<String, Hashtable<String, String[]>> jsmessages;
-
-      private Hashtable<String, int[]> jshashes;
-
-      @Override
-      public String getParameter(String name)
-      {
-        return aparser.getAppletValue(name, null);
-      }
-
-      @Override
-      public boolean getDefaultParameter(String name, boolean def)
-      {
-        String stn;
-        return ((stn = getParameter(name)) == null ? def
-                : "true".equalsIgnoreCase(stn));
-      }
-
-      /**
-       * Get the applet-like document base even though this is an application.
-       */
-      @Override
-      public URL getDocumentBase()
-      {
-        return Platform.getDocumentBase();
-      }
-
-      /**
-       * Get the applet-like code base even though this is an application.
-       */
-      @Override
-      public URL getCodeBase()
-      {
-        return Platform.getCodeBase();
-      }
-
-      @Override
-      public AlignViewportI getViewport()
-      {
-        return af.getViewport();
-      }
-
-      /**
-       * features
-       * 
-       */
-      @Override
-      public boolean parseFeaturesFile(String filename,
-              DataSourceType protocol)
-      {
-        return af.parseFeaturesFile(filename, protocol);
-      }
-
-      /**
-       * scorefile
-       * 
-       */
-      @Override
-      public boolean loadScoreFile(String sScoreFile) throws IOException
-      {
-        af.loadJalviewDataFile(sScoreFile, null, null, null);
-        return true;
-      }
-
-      /**
-       * annotations, jpredfile, jnetfile
-       * 
-       */
-      @Override
-      public void updateForAnnotations()
-      {
-        af.updateForAnnotations();
-      }
 
-      @Override
-      public void loadTree(NewickFile fin, String treeFile)
-              throws IOException
-      {
-        // n/a -- already done by standard Jalview command line processing
-      }
+  private static boolean isInteractive = true;
 
-      @Override
-      public void setAlignPdbStructures(boolean defaultParameter)
-      {
-        alignPDBStructures = true;
-      }
+  public static boolean isInteractive()
+  {
+    return isInteractive;
+  }
 
-      @Override
-      public void newStructureView(PDBEntry pdb, SequenceI[] seqs,
-              String[] chains, DataSourceType protocol)
-      {
-        StructureViewer.launchStructureViewer(af.alignPanel, pdb, seqs);
-      }
-
-      @Override
-      public void setFeatureGroupState(String[] groups, boolean state)
-      {
-        af.setFeatureGroupState(groups, state);
-      }
-
-      @Override
-      public void alignedStructureView(PDBEntry[] pdb, SequenceI[][] seqs,
-              String[][] chains, String[] protocols)
-      {
-        System.err.println(
-                "Jalview applet interface alignedStructureView not implemented");
-      }
-
-      @Override
-      public void newFeatureSettings()
-      {
-        System.err.println(
-                "Jalview applet interface newFeatureSettings not implemented");
-      }
-
-      private Vector<Runnable> jsExecQueue;
-
-      @Override
-      public Vector<Runnable> getJsExecQueue(JSFunctionExec exec)
-      {
-        jsFunctionExec = exec;
-        return (jsExecQueue == null ? (jsExecQueue = new Vector<>())
-                : jsExecQueue);
-      }
-
-      @Override
-      public AppletContext getAppletContext()
-      {
-        // TODO Auto-generated method stub
-        return null;
-      }
-
-      @Override
-      public boolean isJsfallbackEnabled()
-      {
-        // TODO Auto-generated method stub
-        return false;
-      }
-
-      @Override
-      public JSObject getJSObject()
-      {
-        // TODO Auto-generated method stub
-        return null;
-      }
-
-      @Override
-      public StructureSelectionManagerProvider getStructureSelectionManagerProvider()
-      {
-        // TODO Q: what exactly is this? BH
-        return null;
-      }
-
-      @Override
-      public void updateColoursFromMouseOver(Object source,
-              MouseOverStructureListener mouseOverStructureListener)
-      {
-        // TODO Auto-generated method stub
-
-      }
-
-      @Override
-      public Object[] getSelectionForListener(SequenceGroup seqsel,
-              ColumnSelection colsel, HiddenColumns hidden,
-              SelectionSource source, Object alignFrame)
-      {
-        return appLoader.getSelectionForListener(getCurrentAlignFrame(),
-                seqsel, colsel, hidden, source, alignFrame);
-      }
-
-      @Override
-      public String arrayToSeparatorList(String[] array)
-      {
-        return appLoader.arrayToSeparatorList(array);
-      }
-
-      @Override
-      public Hashtable<String, int[]> getJSHashes()
-      {
-        return (jshashes == null ? (jshashes = new Hashtable<>())
-                : jshashes);
-      }
-
-      @Override
-      public Hashtable<String, Hashtable<String, String[]>> getJSMessages()
-      {
-        return (jsmessages == null ? (jsmessages = new Hashtable<>())
-                : jsmessages);
-      }
-
-      @Override
-      public Object getFrameForSource(VamsasSource source)
-      {
-        if (source != null)
-        {
-          AlignFrame af;
-          if (source instanceof jalview.gui.AlignViewport
-                  && source == (af = getCurrentAlignFrame()).getViewport())
-          {
-            // should be valid if it just generated an event!
-            return af;
-          }
-          // TODO: ensure that if '_af' is specified along with a handler
-          // function, then only events from that alignFrame are sent to that
-          // function
-        }
-        return null;
-      }
-
-      @Override
-      public FeatureRenderer getNewFeatureRenderer(AlignViewportI vp)
-      {
-        return new jalview.gui.FeatureRenderer((AlignmentPanel) vp);
-      }
-
-    };
-
-    appLoader = new JalviewAppLoader(true);
-    appLoader.load(app);
-  }
-
-  /**
-   * 
-   * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
-   */
-  @Override
-  public String getSelectedSequences()
-  {
-    return getSelectedSequencesFrom(getCurrentAlignFrame());
-  }
-
-  /**
-   * 
-   * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
-   */
-  @Override
-  public String getSelectedSequences(String sep)
-  {
-    return getSelectedSequencesFrom(getCurrentAlignFrame(), sep);
-  }
-
-  /**
-   * 
-   * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
-   *      .AlignFrame)
-   */
-  @Override
-  public String getSelectedSequencesFrom(AlignFrame alf)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return getSelectedSequencesFrom(alf, null);
-  }
-
-  /**
-   * 
-   * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
-   *      .AlignFrame, java.lang.String)
-   */
-  @Override
-  public String getSelectedSequencesFrom(AlignFrame alf, String sep)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.getSelectedSequencesFrom(alf, sep);
-  }
-
-  /**
-   * 
-   * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
-   *      .AlignFrame, java.lang.String)
-   */
-  @Override
-  public void highlight(String sequenceId, String position,
-          String alignedPosition)
-  {
-    highlightIn(null, sequenceId, position,
-            alignedPosition);
-  }
-
-  @Override
-  public void highlightIn(AlignFrame alf, String sequenceId,
-          String position, String alignedPosition)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    appLoader.highlightIn(alf, sequenceId, position, alignedPosition);
-  }
-
-  @Override
-  public void select(String sequenceIds, String columns)
-  {
-    selectIn(getCurrentAlignFrame(), sequenceIds, columns, null);
-  }
-
-  @Override
-  public void select(String sequenceIds, String columns, String sep)
-  {
-    selectIn(null, sequenceIds, columns, sep);
-  }
-
-  @Override
-  public void selectIn(AlignFrame alf, String sequenceIds, String columns)
-  {
-    selectIn(alf, sequenceIds, columns, null);
-  }
-
-  @Override
-  public void selectIn(AlignFrame alf, String sequenceIds, String columns,
-          String sep)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    appLoader.selectIn(alf, sequenceIds, columns, sep);
-  }
-
-  @Override
-  public String getSelectedSequencesAsAlignment(String format,
-          String suffix)
-  {
-    return getSelectedSequencesAsAlignmentFrom(null,
-            format, suffix);
-  }
-
-  @Override
-  public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
-          String format, String sep)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format, sep);
-  }
-
-  @Override
-  public String getAlignmentOrder()
-  {
-    return getAlignmentFrom(getCurrentAlignFrame(), null);
-  }
-
-  @Override
-  public String getAlignmentOrderFrom(AlignFrame alf)
-  {
-    return getAlignmentFrom(alf, null);
-  }
-
-  @Override
-  public String getAlignmentOrderFrom(AlignFrame alf, String sep)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.getAlignmentOrderFrom(alf, sep);
-  }
-
-  @Override
-  public String orderBy(String order, String undoName)
-  {
-    return orderBy(order, undoName, null);
-  }
-
-  @Override
-  public String orderBy(String order, String undoName, String sep)
-  {
-    return orderAlignmentBy(getCurrentAlignFrame(), order, undoName, sep);
-  }
-
-  @Override
-  public String orderAlignmentBy(AlignFrame alf, String order,
-          String undoName, String sep)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.orderAlignmentBy(alf, order, undoName, sep);
-  }
-
-  @Override
-  public String getAlignment(String format)
-  {
-    return getAlignmentFrom(null, format, null);
-  }
-
-  @Override
-  public String getAlignmentFrom(AlignFrame alf, String format)
-  {
-    return getAlignmentFrom(alf, format, null);
-  }
-
-  @Override
-  public String getAlignment(String format, String suffix)
-  {
-    return getAlignmentFrom(getCurrentAlignFrame(), format, suffix);
-  }
-
-  @Override
-  public String getAlignmentFrom(AlignFrame alf, String format,
-          String suffix)
-  {
-    return appLoader.getAlignmentFrom(alf, format, suffix);
-  }
-
-  @Override
-  public void loadAnnotation(String annotation)
-  {
-    loadAnnotationFrom(getCurrentAlignFrame(), annotation);
-  }
-
-  @Override
-  public void loadAnnotationFrom(AlignFrame alf, String annotation)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    appLoader.loadAnnotationFrom(alf, annotation);
-  }
-
-  @Override
-  public void loadFeatures(String features, boolean autoenabledisplay)
-  {
-    loadFeaturesFrom(currentAlignFrame, features, autoenabledisplay);
-  }
-
-  @Override
-  public boolean loadFeaturesFrom(AlignFrame alf, String features,
-          boolean autoenabledisplay)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.loadFeaturesFrom(alf, features, autoenabledisplay);
-  }
-
-  @Override
-  public String getFeatures(String format)
-  {
-    return getFeaturesFrom(null, format);
-  }
-
-  @Override
-  public String getFeaturesFrom(AlignFrame alf, String format)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.getFeaturesFrom(alf, format);
-  }
-
-  @Override
-  public String getAnnotation()
-  {
-    return getAnnotationFrom(null);
-  }
-
-  @Override
-  public String getAnnotationFrom(AlignFrame alf)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.getAnnotationFrom(alf);
-  }
-
-//  @Override
-//  public AlignFrame newView()
-//  {
-//    return newViewFrom(null, null);
-//  }
-//
-//  @Override
-//  public AlignFrame newView(String name)
-//  {
-//    return newViewFrom(null, name);
-//  }
-//
-//  @Override
-//  public AlignFrame newViewFrom(AlignFrame alf)
-//  {
-//    return newViewFrom(alf, null);
-//  }
-//
-//  @Override
-//  public AlignFrame newViewFrom(AlignFrame alf, String name)
-//  {
-//    if (alf == null)
-//    {
-//      alf = getCurrentAlignFrame();
-//    }
-//    return appLoader.newViewFrom(alf, name);
-//  }
-
-  @Override
-  public AlignFrame loadAlignment(String text, String title)
-  {
-    return appLoader.loadAlignment(text, AlignFrame.DEFAULT_WIDTH,
-            AlignFrame.DEFAULT_HEIGHT, title);
-  }
-
-  @Override
-  public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
-          String pdbEntryString, String pdbFile)
-  {
-    if (alFrame == null)
-    {
-      alFrame = getCurrentAlignFrame();
-    }
-    return appLoader.addPdbFile(alFrame, sequenceId, pdbEntryString,
-            pdbFile);
-  }
-
-  @Override
-  public void scrollViewToIn(AlignFrame alf, String topRow,
-          String leftHandColumn)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    appLoader.scrollViewToIn(alf, topRow, leftHandColumn);
-  }
-
-  @Override
-  public void scrollViewToRowIn(AlignFrame alf, String topRow)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    appLoader.scrollViewToRowIn(alf, topRow);
-  }
-
-  @Override
-  public void scrollViewToColumnIn(AlignFrame alf, String leftHandColumn)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    appLoader.scrollViewToColumnIn(alf, leftHandColumn);
-  }
-
-  @Override
-  public String getFeatureGroups()
-  {
-    return getFeatureGroupsOn(null);
-  }
-
-  @Override
-  public String getFeatureGroupsOn(AlignFrame alf)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.getFeatureGroupsOn(alf);
-  }
-
-  @Override
-  public String getFeatureGroupsOfState(boolean visible)
-  {
-    return getFeatureGroupsOfStateOn(null, visible);
-  }
-
-  @Override
-  public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.getFeatureGroupsOfStateOn(alf, visible);
-  }
-
-  @Override
-  public void setFeatureGroupState(String groups, boolean state)
-  { // JalviewLite API
-    setFeatureGroupStateOn(null, groups, state);
-  }
-
-  @Override
-  public void setFeatureGroupStateOn(AlignFrame alf, String groups,
-          boolean state)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    appLoader.setFeatureGroupStateOn(alf, groups, state);
-  }
-
-  @Override
-  public String getSeparator()
-  {
-    return appLoader.getSeparator();
-  }
-
-  @Override
-  public void setSeparator(String separator)
-  {
-    appLoader.setSeparator(separator);
-  }
-
-  @Override
-  public String getJsMessage(String messageclass, String viewId)
-  {
-    // see http://www.jalview.org/examples/jalviewLiteJs.html
-    return null;
-  }
-
-  /**
-   * Open a new Tree panel on the desktop statically. Params are standard (not
-   * set by Groovy). No dialog is opened.
-   * 
-   * @param af
-   * @param treeType
-   * @param modelName
-   * @return null, or the string "label.you_need_at_least_n_sequences" if number
-   *         of sequences selected is inappropriate
-   */
-  @Override
-  public Object openTreePanel(AlignFrame af, String treeType,
-          String modelName)
-  { // JalviewJS api
-    if (af == null)
-    {
-      af = getCurrentAlignFrame();
-    }
-    return CalculationChooser.openTreePanel(af, treeType, modelName, null);
-  }
-
-  /**
-   * public static method for JalviewJS API to open a PCAPanel without
-   * necessarily using a dialog.
-   * 
-   * @param af
-   * @param modelName
-   * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
-   *         if number of sequences selected is inappropriate
-   */
-  @Override
-  public Object openPcaPanel(AlignFrame af, String modelName)
-  {
-    if (af == null)
-    {
-      af = getCurrentAlignFrame();
-    }
-    return CalculationChooser.openPcaPanel(af, modelName, null);
-  }
-
-  @Override
-  public String getSelectedSequencesAsAlignment(String format,
-          boolean suffix)
-  {
-    return getSelectedSequencesAsAlignmentFrom(null,
-            format, suffix);
-  }
-
-  @Override
-  public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
-          String format, boolean suffix)
-  {
-    if (alf == null)
-    {
-      alf = getCurrentAlignFrame();
-    }
-    return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format,
-            "" + suffix);
-  }
-
-  @Override
-  public String arrayToSeparatorList(String[] array)
-  {
-    return appLoader.arrayToSeparatorList(array);
-  }
-
-  @Override
-  public String[] separatorListToArray(String list)
-  {
-    return appLoader.separatorListToArray(list);
-  }
-
-  //// probably not needed in JalviewJS -- From when Jmol and Jalview did not
-  //// have a direct connection?
-
-  @Override
-  public void setMouseoverListener(String listener)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
-  public void setMouseoverListener(AlignFrame af, String listener)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
-  public void setSelectionListener(String listener)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
-  public void setSelectionListener(AlignFrame af, String listener)
+  public static void setInteractive(boolean tf)
   {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
-  public void setStructureListener(String listener, String modelSet)
-  {
-    // TODO Auto-generated method stub
-
+    isInteractive = tf;
   }
-
-  @Override
-  public void removeJavascriptListener(AlignFrame af, String listener)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
-  public void mouseOverStructure(String pdbResNum, String chain,
-          String pdbfile)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
-  public void showOverview()
-  {
-    currentAlignFrame.overviewMenuItem_actionPerformed(null);
-  }
-
-  public void notifyWorker(AlignCalcWorkerI worker, String status)
-  {
-    // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
-    // + " " + status);
-  }
-
-  @Override
-  public Object parseArguments(String[] args)
-  {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-
-  
-  
 }