Merge branch 'improvement/JAL-1988+JAL-3416_Java8_macOS_APQHandlers_and_FlatLaF_optio...
[jalview.git] / src / jalview / bin / Jalview.java
index 19de95e..fd1783a 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.bin;
 
+import java.awt.Color;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -37,14 +38,22 @@ 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;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
+import javax.swing.SwingUtilities;
 import javax.swing.UIManager;
 import javax.swing.UIManager.LookAndFeelInfo;
 
+import com.formdev.flatlaf.FlatLightLaf;
+import com.formdev.flatlaf.util.SystemInfo;
 import com.threerings.getdown.util.LaunchUtil;
 
+//import edu.stanford.ejalbert.launching.IBrowserLaunching;
 import groovy.lang.Binding;
 import groovy.util.GroovyScriptEngine;
 import jalview.ext.so.SequenceOntology;
@@ -65,6 +74,7 @@ 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;
@@ -87,6 +97,15 @@ import jalview.ws.jws2.Jws2Discoverer;
  */
 public class Jalview
 {
+  static
+  {
+    Platform.getURLCommandArguments();
+    Platform.addJ2SDirectDatabaseCall("https://www.jalview.org");
+    Platform.addJ2SDirectDatabaseCall("http://www.jalview.org");
+    Platform.addJ2SDirectDatabaseCall("http://www.compbio.dundee.ac.uk");
+    Platform.addJ2SDirectDatabaseCall("https://www.compbio.dundee.ac.uk");
+  }
+
   /*
    * singleton instance of this class
    */
@@ -98,22 +117,30 @@ public class Jalview
 
   static
   {
-    // grab all the rights we can the JVM
-    Policy.setPolicy(new Policy()
+    if (!Platform.isJS())
+    /**
+     * Java only
+     * 
+     * @j2sIgnore
+     */
     {
-      @Override
-      public PermissionCollection getPermissions(CodeSource codesource)
+      // grab all the rights we can for the JVM
+      Policy.setPolicy(new Policy()
       {
-        Permissions perms = new Permissions();
-        perms.add(new AllPermission());
-        return (perms);
-      }
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource)
+        {
+          Permissions perms = new Permissions();
+          perms.add(new AllPermission());
+          return (perms);
+        }
 
-      @Override
-      public void refresh()
-      {
-      }
-    });
+        @Override
+        public void refresh()
+        {
+        }
+      });
+    }
   }
 
   /**
@@ -188,16 +215,62 @@ 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
+     * 
+     */
+    {
+      System.out.println("not in js");
+    }
+
+    // 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");
+    }
+
+  }
+
   /**
    * @param args
    */
   void doMain(String[] args)
   {
-    System.setSecurityManager(null);
+
+    if (!Platform.isJS())
+    {
+      System.setSecurityManager(null);
+    }
+
     System.out
             .println("Java version: " + System.getProperty("java.version"));
     System.out.println("Java Home: " + System.getProperty("java.home"));
@@ -226,17 +299,6 @@ public class Jalview
     ArgsParser aparser = new ArgsParser(args);
     boolean headless = false;
 
-    if (aparser.contains("help") || aparser.contains("h"))
-    {
-      showUsage();
-      System.exit(0);
-    }
-    if (aparser.contains("nodisplay") || aparser.contains("nogui")
-            || aparser.contains("headless"))
-    {
-      System.setProperty("java.awt.headless", "true");
-      headless = true;
-    }
     String usrPropsFile = aparser.getValue("props");
     Cache.loadProperties(usrPropsFile); // must do this before
     if (usrPropsFile != null)
@@ -245,20 +307,42 @@ public class Jalview
               "CMD [-props " + usrPropsFile + "] executed successfully!");
     }
 
-    // anything else!
-
-    final String jabawsUrl = aparser.getValue("jabaws");
-    if (jabawsUrl != null)
+    if (!Platform.isJS())
+    /**
+     * Java only
+     * 
+     * @j2sIgnore
+     */
     {
-      try
+      if (aparser.contains("help") || aparser.contains("h"))
       {
-        Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
-        System.out.println(
-                "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
-      } catch (MalformedURLException e)
+        showUsage();
+        System.exit(0);
+      }
+      if (aparser.contains("nodisplay") || aparser.contains("nogui")
+              || aparser.contains("headless"))
       {
-        System.err.println(
-                "Invalid jabaws parameter: " + jabawsUrl + " ignored");
+        System.setProperty("java.awt.headless", "true");
+        headless = true;
+      }
+      // anything else!
+
+      // allow https handshakes to download intermediate certs if necessary
+      System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
+
+      final String jabawsUrl = aparser.getValue("jabaws");
+      if (jabawsUrl != null)
+      {
+        try
+        {
+          Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
+          System.out.println(
+                  "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
+        } catch (MalformedURLException e)
+        {
+          System.err.println(
+                  "Invalid jabaws parameter: " + jabawsUrl + " ignored");
+        }
       }
     }
 
@@ -273,6 +357,10 @@ public class Jalview
       else
       {
         System.out.println("Executing setprop argument: " + defs);
+        if (Platform.isJS())
+        {
+          Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
+        }
         // DISABLED FOR SECURITY REASONS
         // TODO: add a property to allow properties to be overriden by cli args
         // Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
@@ -286,9 +374,10 @@ public class Jalview
     }
     System.setProperty("http.agent",
             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
+
     try
     {
-      Cache.initLogger();
+      Console.initLogger();
     } catch (NoClassDefFoundError error)
     {
       error.printStackTrace();
@@ -302,17 +391,18 @@ public class Jalview
     setLookAndFeel();
 
     /*
-     * configure 'full' SO model if preferences say to, else use the default (SO
-     * Lite)
+     * 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
      */
-    if (Cache.getDefault("USE_FULL_SO", true))
+    boolean soDefault = !Platform.isJS();
+    if (Cache.getDefault("USE_FULL_SO", soDefault))
     {
       SequenceOntologyFactory.setInstance(new SequenceOntology());
     }
 
     if (!headless)
     {
-
+      Desktop.nosplash = aparser.contains("nosplash");
       desktop = new Desktop();
       desktop.setInBatchMode(true); // indicate we are starting up
 
@@ -321,70 +411,86 @@ public class Jalview
         JalviewTaskbar.setTaskbar(this);
       } catch (Exception e)
       {
-        Cache.log.info("Cannot set Taskbar");
-        Cache.log.error(e.getMessage());
+        Console.info("Cannot set Taskbar");
+        Console.error(e.getMessage());
         // e.printStackTrace();
       } catch (Throwable t)
       {
-        Cache.log.info("Cannot set Taskbar");
-        Cache.log.error(t.getMessage());
+        Console.info("Cannot set Taskbar");
+        Console.error(t.getMessage());
         // t.printStackTrace();
       }
 
+      // set Proxy settings before all the internet calls
+      Cache.setProxyPropertiesFromPreferences();
+
       desktop.setVisible(true);
-      desktop.startServiceDiscovery();
-      if (!aparser.contains("nousagestats"))
-      {
-        startUsageStats(desktop);
-      }
-      else
-      {
-        System.err.println("CMD [-nousagestats] executed successfully!");
-      }
 
-      if (!aparser.contains("noquestionnaire"))
+      if (!Platform.isJS())
+      /**
+       * Java only
+       * 
+       * @j2sIgnore
+       */
       {
-        String url = aparser.getValue("questionnaire");
-        if (url != null)
+        if (!aparser.contains("nowebservicediscovery"))
         {
-          // Start the desktop questionnaire prompter with the specified
-          // questionnaire
-          Cache.log.debug("Starting questionnaire url at " + url);
-          desktop.checkForQuestionnaire(url);
-          System.out.println(
-                  "CMD questionnaire[-" + url + "] executed successfully!");
+          desktop.startServiceDiscovery();
+        }
+        if (!aparser.contains("nousagestats"))
+        {
+          startUsageStats(desktop);
         }
         else
         {
-          if (Cache.getProperty("NOQUESTIONNAIRES") == null)
+          System.err.println("CMD [-nousagestats] executed successfully!");
+        }
+
+        if (!aparser.contains("noquestionnaire"))
+        {
+          String url = aparser.getValue("questionnaire");
+          if (url != null)
           {
             // Start the desktop questionnaire prompter with the specified
             // questionnaire
-            // String defurl =
-            // "http://anaplog.compbio.dundee.ac.uk/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);
+            Console.debug("Starting questionnaire url at " + url);
+            desktop.checkForQuestionnaire(url);
+            System.out.println("CMD questionnaire[-" + url
+                    + "] executed successfully!");
+          }
+          else
+          {
+            if (Cache.getProperty("NOQUESTIONNAIRES") == null)
+            {
+              // Start the desktop questionnaire prompter with the specified
+              // questionnaire
+              // String defurl =
+              // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
+              // //
+              String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
+              Console.debug(
+                      "Starting questionnaire with default url: " + defurl);
+              desktop.checkForQuestionnaire(defurl);
+            }
           }
         }
-      }
-      else
-      {
-        System.err.println("CMD [-noquestionnaire] executed successfully!");
-      }
+        else
+        {
+          System.err
+                  .println("CMD [-noquestionnaire] executed successfully!");
+        }
 
-      if (!aparser.contains("nonews")
-              || Cache.getProperty("NONEWS") == null)
-      {
-        desktop.checkForNews();
-      }
+        if (!aparser.contains("nonews")
+                || Cache.getProperty("NONEWS") == null)
+        {
+          desktop.checkForNews();
+        }
 
-      if (!aparser.contains("nohtmltemplates")
-              || Cache.getProperty("NOHTMLTEMPLATES") == null)
-      {
-        BioJsHTMLOutput.updateBioJS();
+        if (!aparser.contains("nohtmltemplates")
+                || Cache.getProperty("NOHTMLTEMPLATES") == null)
+        {
+          BioJsHTMLOutput.updateBioJS();
+        }
       }
     }
 
@@ -436,20 +542,28 @@ public class Jalview
       }
       System.out.println("CMD [-open " + file + "] executed successfully!");
 
-      protocol = AppletFormatAdapter.checkProtocol(file);
-
-      if (protocol == DataSourceType.FILE)
+      if (!Platform.isJS())
+      /**
+       * ignore in JavaScript -- can't just file existence - could load it?
+       * 
+       * @j2sIgnore
+       */
       {
-        if (!(new File(file)).exists())
+        if (!HttpUtils.startsWithHttpOrHttps(file))
         {
-          System.out.println("Can't find " + file);
-          if (headless)
+          if (!(new File(file)).exists())
           {
-            System.exit(1);
+            System.out.println("Can't find " + file);
+            if (headless)
+            {
+              System.exit(1);
+            }
           }
         }
       }
 
+      protocol = AppletFormatAdapter.checkProtocol(file);
+
       try
       {
         format = new IdentifyFile().identify(file, protocol);
@@ -652,18 +766,20 @@ public class Jalview
             }
             else
             {
-              if (af.saveAlignment(file, outFormat))
+              af.saveAlignment(file, outFormat);
+              if (af.isSaveAlignmentSuccessful())
               {
-                System.out.println("Written alignment in " + format
-                        + " format to " + file);
+                System.out.println("Written alignment in "
+                        + outFormat.getName() + " format to " + file);
               }
               else
               {
                 System.out.println("Error writing file " + file + " in "
-                        + format + " format!!");
+                        + outFormat.getName() + " format!!");
               }
             }
           }
+
         }
 
         while (aparser.getSize() > 0)
@@ -677,12 +793,16 @@ public class Jalview
     // And the user
     // ////////////////////
 
-    if (!headless && file == null
-            && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true))
+    if (!Platform.isJS() && !headless && file == null
+            && Cache.getDefault("SHOW_STARTUP_FILE", true))
+    /**
+     * Java only
+     * 
+     * @j2sIgnore
+     */
     {
-      file = jalview.bin.Cache.getDefault("STARTUP_FILE",
-              jalview.bin.Cache.getDefault("www.jalview.org",
-                      "https://www.jalview.org")
+      file = Cache.getDefault("STARTUP_FILE",
+              Cache.getDefault("www.jalview.org", "https://www.jalview.org")
                       + "/examples/exampleFile_2_7.jvp");
       if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
               || file.equals(
@@ -693,7 +813,7 @@ public class Jalview
         file.replace("_2_3", "_2_7");
         file.replace("2_7.jar", "2_7.jvp");
         // and remove the stale setting
-        jalview.bin.Cache.removeProperty("STARTUP_FILE");
+        Cache.removeProperty("STARTUP_FILE");
       }
 
       protocol = AppletFormatAdapter.checkProtocol(file);
@@ -746,8 +866,8 @@ public class Jalview
 
   private static void setLookAndFeel()
   {
-    // property laf = "crossplatform", "system", "gtk", "metal", "nimbus" or
-    // "mac"
+    // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
+    // "mac" or "flat"
     // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
     // try Quaqua/Vaqua.
     String lafProp = System.getProperty("laf");
@@ -768,62 +888,69 @@ public class Jalview
       lafSet = setCrossPlatformLookAndFeel();
       if (!lafSet)
       {
-        Cache.log.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "system":
       lafSet = setSystemLookAndFeel();
       if (!lafSet)
       {
-        Cache.log.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "gtk":
       lafSet = setGtkLookAndFeel();
       if (!lafSet)
       {
-        Cache.log.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "metal":
       lafSet = setMetalLookAndFeel();
       if (!lafSet)
       {
-        Cache.log.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "nimbus":
       lafSet = setNimbusLookAndFeel();
       if (!lafSet)
       {
-        Cache.log.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
+      }
+      break;
+    case "flat":
+      lafSet = setFlatLookAndFeel();
+      if (!lafSet)
+      {
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "quaqua":
       lafSet = setQuaquaLookAndFeel();
       if (!lafSet)
       {
-        Cache.log.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "vaqua":
       lafSet = setVaquaLookAndFeel();
       if (!lafSet)
       {
-        Cache.log.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "mac":
       lafSet = setMacLookAndFeel();
       if (!lafSet)
       {
-        Cache.log.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "none":
       break;
     default:
-      Cache.log.error("Requested laf=" + laf + " not implemented");
+      Console.error("Requested laf=" + laf + " not implemented");
     }
     if (!lafSet)
     {
@@ -832,7 +959,7 @@ public class Jalview
       {
         setMetalLookAndFeel();
       }
-      if (Platform.isAMac())
+      if (Platform.isMac())
       {
         setMacLookAndFeel();
       }
@@ -841,28 +968,33 @@ public class Jalview
 
   private static boolean setCrossPlatformLookAndFeel()
   {
-    return setGenericLookAndFeel(false);
+    boolean set = false;
+    try
+    {
+      UIManager.setLookAndFeel(
+              UIManager.getCrossPlatformLookAndFeelClassName());
+      set = true;
+    } catch (Exception ex)
+    {
+      Console.error("Unexpected Look and Feel Exception");
+      Console.error(ex.getMessage());
+      Console.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)
     {
-      Cache.log.error("Unexpected Look and Feel Exception");
-      Cache.log.error(ex.getMessage());
-      Cache.log.debug(Cache.getStackTraceString(ex));
+      Console.error("Unexpected Look and Feel Exception");
+      Console.error(ex.getMessage());
+      Console.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -876,9 +1008,10 @@ 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;
@@ -888,9 +1021,9 @@ public class Jalview
       set = true;
     } catch (Exception ex)
     {
-      Cache.log.error("Unexpected Look and Feel Exception");
-      Cache.log.error(ex.getMessage());
-      Cache.log.debug(Cache.getStackTraceString(ex));
+      Console.error("Unexpected Look and Feel Exception");
+      Console.error(ex.getMessage());
+      Console.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -913,6 +1046,45 @@ public class Jalview
             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
   }
 
+  private static boolean setFlatLookAndFeel()
+  {
+    boolean set = setSpecificLookAndFeel("flatlaf light",
+            "com.formdev.flatlaf.FlatLightLaf", false);
+    if (set)
+    {
+      if (Platform.isMac())
+      {
+        System.setProperty("apple.laf.useScreenMenuBar", "true");
+        System.setProperty("apple.awt.application.name",
+                ChannelProperties.getProperty("app_name"));
+        System.setProperty("apple.awt.application.appearance", "system");
+        if (SystemInfo.isMacFullWindowContentSupported
+                && Desktop.desktop != null)
+        {
+          Desktop.desktop.getRootPane()
+                  .putClientProperty("apple.awt.fullWindowContent", true);
+          Desktop.desktop.getRootPane()
+                  .putClientProperty("apple.awt.transparentTitleBar", true);
+        }
+
+        SwingUtilities.invokeLater(() -> {
+          FlatLightLaf.setup();
+        });
+      }
+
+      UIManager.put("TabbedPane.showTabSeparators", true);
+      UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
+      UIManager.put("TabbedPane.tabsOverlapBorder", true);
+      // UIManager.put("TabbedPane.hasFullBorder", true);
+      UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
+      UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
+      UIManager.put("TabbedPane.smoothScrolling", true);
+      UIManager.put("TabbedPane.tabWidthMode", "compact");
+      UIManager.put("TabbedPane.selectedBackground", Color.white);
+    }
+    return set;
+  }
+
   private static boolean setQuaquaLookAndFeel()
   {
     return setSpecificLookAndFeel("quaqua",
@@ -931,11 +1103,11 @@ 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();
     }
@@ -1000,17 +1172,17 @@ public class Jalview
               @Override
               public void run()
               {
-                Cache.log.debug(
+                Console.debug(
                         "Initialising googletracker for usage stats.");
                 Cache.initGoogleTracker();
-                Cache.log.debug("Tracking enabled.");
+                Console.debug("Tracking enabled.");
               }
             }, new Runnable()
             {
               @Override
               public void run()
               {
-                Cache.log.debug("Not enabling Google Tracking.");
+                Console.debug("Not enabling Google Tracking.");
               }
             }, null, true);
     desktop.addDialogThread(prompter);