Merge branch 'alpha/origin_2022_JAL-3066_Jalview_212_slivka-integration' into spike...
[jalview.git] / src / jalview / bin / Jalview.java
index f5b7009..eca8147 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.bin;
 
+import java.awt.GraphicsEnvironment;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -37,6 +39,7 @@ import java.security.PermissionCollection;
 import java.security.Permissions;
 import java.security.Policy;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Vector;
 import java.util.logging.ConsoleHandler;
@@ -50,9 +53,13 @@ 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;
@@ -60,6 +67,7 @@ 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;
@@ -67,6 +75,8 @@ 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;
@@ -86,20 +96,67 @@ import jalview.ws.jws2.Jws2Discoverer;
  * @author $author$
  * @version $Revision$
  */
-public class Jalview
+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 AlignFrame currentAlignFrame;
+
+  public String appletResourcePath;
+
+  public String j2sAppletID;
+
+  private boolean noCalculation, noMenuBar, noStatus;
+
+  private boolean noAnnotation;
+
+  public boolean getStartCalculations()
+  {
+    return !noCalculation;
+  }
+
+  public boolean getAllowMenuBar()
+  {
+    return !noMenuBar;
+  }
+
+  public boolean getShowStatus()
+  {
+    return !noStatus;
+  }
+
+  public boolean getShowAnnotation()
+  {
+    return !noAnnotation;
+  }
 
   static
   {
@@ -188,10 +245,7 @@ public class Jalview
 
   }
 
-  public static Jalview getInstance()
-  {
-    return instance;
-  }
+  private final static boolean doPlatformLogging = false;
 
   /**
    * main class for Jalview application
@@ -201,50 +255,17 @@ public class Jalview
    */
   public static void main(String[] args)
   {
-    // setLogging(); // BH - for event debugging in JavaScript
-    instance = new Jalview();
-    instance.doMain(args);
-  }
-
-  private static void logClass(String name)
-  {
-    // BH - for event debugging in JavaScript
-    ConsoleHandler consoleHandler = new ConsoleHandler();
-    consoleHandler.setLevel(Level.ALL);
-    Logger logger = Logger.getLogger(name);
-    logger.setLevel(Level.ALL);
-    logger.addHandler(consoleHandler);
-  }
-
-  @SuppressWarnings("unused")
-  private static void setLogging()
-  {
-
-    /**
-     * @j2sIgnore
-     * 
-     */
+    if (doPlatformLogging)
     {
-      System.out.println("not in js");
+      Platform.startJavaLogging();
     }
 
-    // BH - for event debugging in JavaScript (Java mode only)
-    if (!Platform.isJS())
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
-    {
-      Logger.getLogger("").setLevel(Level.ALL);
-      logClass("java.awt.EventDispatchThread");
-      logClass("java.awt.EventQueue");
-      logClass("java.awt.Component");
-      logClass("java.awt.focus.Component");
-      logClass("java.awt.focus.DefaultKeyboardFocusManager");
-    }
+    getInstance().doMain(args);
 
   }
+  
+
+  
 
   /**
    * @param args
@@ -252,11 +273,17 @@ public class Jalview
   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"));
     System.out.println("Java Home: " + System.getProperty("java.home"));
@@ -280,45 +307,63 @@ public class Jalview
     }
 
     // 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)
@@ -329,7 +374,7 @@ public class Jalview
       }
 
     }
-    String defs = aparser.getValue("setprop");
+    String defs = aparser.getValue(ArgsParser.SETPROP);
     while (defs != null)
     {
       int p = defs.indexOf('=');
@@ -340,7 +385,7 @@ public class Jalview
       else
       {
         System.out.println("Executing setprop argument: " + defs);
-        if (Platform.isJS())
+        if (isJS)
         {
           Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
         }
@@ -367,97 +412,13 @@ public class Jalview
 
     desktop = null;
 
-    // property laf = "crossplatform", "system", "gtk", "metal" 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)
-    {
-      laf = lafProp;
-    }
-    else if (lafSetting != null)
-    {
-      laf = lafSetting;
-    }
-    boolean lafSet = false;
-    switch (laf)
-    {
-    case "crossplatform":
-      lafSet = setCrossPlatformLookAndFeel();
-      if (!lafSet)
-      {
-        System.err.println("Could not set requested laf=" + laf);
-      }
-      break;
-    case "system":
-      lafSet = setSystemLookAndFeel();
-      if (!lafSet)
-      {
-        System.err.println("Could not set requested laf=" + laf);
-      }
-      break;
-    case "gtk":
-      lafSet = setGtkLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "metal":
-      lafSet = setMetalLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "nimbus":
-      lafSet = setNimbusLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "quaqua":
-      lafSet = setQuaquaLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "vaqua":
-      lafSet = setVaquaLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "mac":
-      lafSet = setMacLookAndFeel();
-      if (!lafSet)
-      {
-        System.err.println("Could not set requested laf=" + laf);
-      }
-      break;
-    case "none":
-      break;
-    default:
-      System.err.println("Requested laf=" + laf + " not implemented");
-    }
-    if (!lafSet)
-    {
-      setSystemLookAndFeel();
-      if (Platform.isLinux() && !Platform.isJS())
-      {
-        setMetalLookAndFeel();
-      }
-      if (Platform.isAMacAndNotJS())
-      {
-        setMacLookAndFeel();
-      }
-    }
+    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());
@@ -465,27 +426,45 @@ public class Jalview
 
     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);
@@ -516,7 +495,7 @@ public class Jalview
               // 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);
@@ -537,45 +516,127 @@ public class Jalview
         BioJsHTMLOutput.updateBioJS();
       }
     }
+    parseArguments(aparser, true);
+  }
 
-    // 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)
+  /**
+   * 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)
+  {
+
+    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)
       {
@@ -593,8 +654,7 @@ public class Jalview
        * @j2sIgnore
        */
       {
-        if (!file.startsWith("http://") && !file.startsWith("https://"))
-        // BH 2019 added https check for Java
+        if (!HttpUtils.startsWithHttpOrHttps(file))
         {
           if (!(new File(file)).exists())
           {
@@ -607,313 +667,541 @@ public class Jalview
         }
       }
 
+      // 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)
+        
+        // JalviewLite interface for JavaScript allows second file open
+        String file2 = aparser.getValue(ArgsParser.OPEN2, true);
+        if (file2 != null)
         {
-          data.replaceAll("%20", " ");
-
-          ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
-                  af.getViewport(), af.getViewport().getAlignment(), data);
-
-          if (cs != null)
+          protocol = AppletFormatAdapter.checkProtocol(file2);
+          try
           {
-            System.out.println(
-                    "CMD [-color " + data + "] executed successfully!");
+            format = new IdentifyFile().identify(file2, protocol);
+          } catch (FileFormatException e1)
+          {
+            // TODO ?
           }
-          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"))
-        {
-          af.getViewport().setSortByTree(true);
-          if (af.getViewport().getSortByTree())
+          AlignFrame af2 = new FileLoader(!headless)
+                  .LoadFileWaitTillLoaded(file2, protocol, format);
+          if (af2 == null)
           {
-            System.out.println("CMD [-sortbytree] executed successfully!");
+            System.out.println("error");
           }
-        }
-        if (aparser.contains("no-annotation"))
-        {
-          af.getViewport().setShowAnnotation(false);
-          if (!af.getViewport().isShowAnnotation())
+          else
           {
-            System.out.println("CMD no-annotation executed successfully!");
+            AlignViewport.openLinkedAlignmentAs(af,
+                    af.getViewport().getAlignment(),
+                    af2.getViewport().getAlignment(), "",
+                    AlignViewport.SPLIT_FRAME);
+            System.out.println(
+                    "CMD [-open2 " + file2 + "] executed successfully!");
           }
         }
-        if (aparser.contains("nosortbytree"))
+        // af is loaded - so set it as current frame
+        setCurrentAlignFrame(af);
+
+        setFrameDependentProperties(aparser, af);
+        
+        if (isJS)
         {
-          af.getViewport().setSortByTree(false);
-          if (!af.getViewport().getSortByTree())
-          {
-            System.out
-                    .println("CMD [-nosortbytree] executed successfully!");
-          }
+          jsApp.initFromParams(af);
         }
-        data = aparser.getValue("tree", true);
-        if (data != null)
+        else
+        /**
+         * Java only
+         * 
+         * @j2sIgnore
+         */
         {
-          try
+          if (groovyscript != null)
           {
-            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);
+            // 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;
           }
         }
-        // TODO - load PDB structure(s) to alignment JAL-629
-        // (associate with identical sequence in alignment, or a specified
-        // sequence)
-        if (groovyscript != null)
-        {
-          // 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;
+        if (!isJS || !isStartup) {
+          createOutputFiles(aparser, format);
         }
-        String imageName = "unnamed.png";
-        while (aparser.getSize() > 1)
-        {
-          String outputFormat = aparser.nextValue();
-          file = aparser.nextValue();
+      }
+      if (headless)
+      {
+        af.getViewport().getCalcManager().shutdown();
+      }
+    }
+    // extract groovy arguments before anything else.
+    // Once all other stuff is done, execute any groovy scripts (in order)
+    if (!isJS && groovyscript != null)
+    {
+      if (Cache.groovyJarsPresent())
+      {
+        // 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!");
 
-          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);
+      }
+      else
+      {
+        System.err.println(
+                "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
+                        + groovyscript);
+      }
+    }
 
-            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;
-          }
+    // 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!");
+    }
 
-          af.saveAlignment(file, format);
-          if (af.isSaveAlignmentSuccessful())
-          {
-            System.out.println("Written alignment in " + format
-                    + " format to " + file);
-          }
-          else
-          {
-            System.out.println("Error writing file " + file + " in "
-                    + format + " format!!");
-          }
+    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!");
+    }
+  }
 
-        while (aparser.getSize() > 0)
-        {
-          System.out.println("Unknown arg: " + aparser.nextValue());
-        }
+
+  private void setFrameDependentProperties(ArgsParser aparser,
+          AlignFrame af)
+  {
+    String data = aparser.getValue(ArgsParser.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(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!");
+    }
+
+    // set or clear the sortbytree flag.
+    if (aparser.contains(ArgsParser.SORTBYTREE))
+    {
+      af.getViewport().setSortByTree(true);
+      if (af.getViewport().getSortByTree())
+      {
+        System.out.println("CMD [-sortbytree] executed successfully!");
       }
     }
-    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))
+    boolean doUpdateAnnotation = false;
     /**
-     * Java only
+     * we do this earlier in JalviewJS because of a complication with
+     * SHOWOVERVIEW
+     * 
+     * For now, just fixing this in JalviewJS.
+     *
      * 
      * @j2sIgnore
+     * 
      */
     {
-      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 (noAnnotation)
       {
-        // hardwire upgrade of the startup file
-        file.replace("_2_3.jar", "_2_7.jar");
-        // and remove the stale setting
-        Cache.removeProperty("STARTUP_FILE");
+        af.getViewport().setShowAnnotation(false);
+        if (!af.getViewport().isShowAnnotation())
+        {
+          doUpdateAnnotation = true;
+        }
       }
+    }
 
-      protocol = DataSourceType.FILE;
-
-      if (file.indexOf("http:") > -1)
+    if (aparser.contains(ArgsParser.NOSORTBYTREE))
+    {
+      af.getViewport().setSortByTree(false);
+      if (!af.getViewport().getSortByTree())
       {
-        protocol = DataSourceType.URL;
+        doUpdateAnnotation = true;
+        System.out
+                .println("CMD [-nosortbytree] executed successfully!");
       }
+    }
+    if (doUpdateAnnotation)
+    { // BH 2019.07.24
+      af.setMenusForViewport();
+      af.alignPanel.updateLayout();
+    }
 
-      if (file.endsWith(".jar"))
+    data = aparser.getValue(ArgsParser.TREE, true);
+    if (data != null)
+    {
+      try
       {
-        format = FileFormat.Jalview;
+        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);
       }
-      else
+    }
+    // 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)
         {
-          // TODO what?
+          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())
+        {
+          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();
+      }
+      if (Platform.isMac())
+      {
+        setMacLookAndFeel();
       }
-      desktop.setInBatchMode(false);
     }
   }
 
   private static boolean setCrossPlatformLookAndFeel()
   {
-    return setGenericLookAndFeel(false);
+    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()
   {
-    return setGenericLookAndFeel(true);
-  }
-
-  private static boolean setGenericLookAndFeel(boolean system)
-  {
     boolean set = false;
     try
     {
-      UIManager.setLookAndFeel(
-              system ? UIManager.getSystemLookAndFeelClassName()
-                      : UIManager.getCrossPlatformLookAndFeelClassName());
+      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
       set = true;
     } catch (Exception ex)
     {
-      System.err.println("Unexpected Look and Feel Exception");
-      ex.printStackTrace();
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -927,9 +1215,9 @@ public class Jalview
       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
       {
         if (info.getName() != null && nameStartsWith
-                ? info.getName().toLowerCase()
-                        .startsWith(name.toLowerCase())
-                : info.getName().toLowerCase().equals(name.toLowerCase()))
+                ? info.getName().toLowerCase(Locale.ROOT)
+                        .startsWith(name.toLowerCase(Locale.ROOT))
+                : info.getName().toLowerCase(Locale.ROOT).equals(name.toLowerCase(Locale.ROOT)))
         {
           className = info.getClassName();
           break;
@@ -939,8 +1227,9 @@ public class Jalview
       set = true;
     } catch (Exception ex)
     {
-      System.err.println("Unexpected Look and Feel Exception");
-      ex.printStackTrace();
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -981,17 +1270,16 @@ public class Jalview
   {
     boolean set = false;
     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
-            "Jalview");
+            ChannelProperties.getProperty("app_name"));
     System.setProperty("apple.laf.useScreenMenuBar", "true");
     set = setQuaquaLookAndFeel();
     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
-            .toLowerCase().contains("quaqua"))
+            .toLowerCase(Locale.ROOT).contains("quaqua"))
     {
       set = setVaquaLookAndFeel();
     }
     return set;
   }
-
   private static void showUsage()
   {
     System.out.println(
@@ -1032,7 +1320,7 @@ public class Jalview
                     + "-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"
                     + "-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 http://www.jalview.org for description of Features and Annotations file~\n\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)
@@ -1040,7 +1328,7 @@ public class Jalview
     /**
      * 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 ?"
@@ -1202,8 +1490,8 @@ public class Jalview
   }
 
   /**
-   * Quit method delegates to Desktop.quit - unless running in headless mode
-   * when it just ends the JVM
+   * Quit method delegates to Desktop.quit - unless running in headless mode when
+   * it just ends the JVM
    */
   public void quit()
   {
@@ -1219,11 +1507,30 @@ public class Jalview
 
   public static AlignFrame getCurrentAlignFrame()
   {
-    return Jalview.currentAlignFrame;
+    return Jalview.getInstance().currentAlignFrame;
   }
 
   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
   {
-    Jalview.currentAlignFrame = currentAlignFrame;
+    Jalview.getInstance().currentAlignFrame = currentAlignFrame;
+  }
+  
+  public void notifyWorker(AlignCalcWorkerI worker, String status)
+  {
+    // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
+    // + " " + status);
+  }
+
+
+  private static boolean isInteractive = true;
+
+  public static boolean isInteractive()
+  {
+    return isInteractive;
+  }
+
+  public static void setInteractive(boolean tf)
+  {
+    isInteractive = tf;
   }
 }