JAL-629 Opening files in new windows. not working yet
[jalview.git] / src / jalview / bin / Jalview.java
index b5e1958..bc8c997 100755 (executable)
@@ -26,7 +26,9 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.OutputStream;
 import java.io.OutputStreamWriter;
+import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.net.MalformedURLException;
 import java.net.URI;
@@ -40,23 +42,31 @@ import java.security.Policy;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Properties;
 import java.util.Vector;
 import java.util.logging.ConsoleHandler;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import javax.swing.JDialog;
+import javax.swing.JFrame;
 import javax.swing.JOptionPane;
 import javax.swing.SwingUtilities;
 import javax.swing.UIManager;
 import javax.swing.UIManager.LookAndFeelInfo;
+import javax.swing.UnsupportedLookAndFeelException;
 
 import com.formdev.flatlaf.FlatLightLaf;
+import com.formdev.flatlaf.themes.FlatMacLightLaf;
 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.bin.argparser.Arg;
+import jalview.bin.argparser.ArgParser;
+import jalview.bin.argparser.BootstrapArgs;
 import jalview.ext.so.SequenceOntology;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
@@ -117,6 +127,8 @@ public class Jalview
 
   private Desktop desktop;
 
+  protected Commands cmds;
+
   public static AlignFrame currentAlignFrame;
 
   static
@@ -273,28 +285,50 @@ public class Jalview
     if (!Platform.isJS())
     {
       System.setSecurityManager(null);
+    }
 
-      Runtime.getRuntime().addShutdownHook(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)
+    {
+      final File appdir = new File(appdirString);
+      new Thread()
       {
+        @Override
         public void run()
         {
-          Console.debug("Running shutdown hook");
-          if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT)
-          {
-            // Got to here by a SIGTERM signal.
-            // Note we will not actually cancel the quit from here -- it's too
-            // late -- but we can wait for saving files.
-            Console.debug("Checking for saving files");
-            QuitHandler.getQuitResponse(false);
-          }
-          else
+          LaunchUtil.upgradeGetdown(
+                  new File(appdir, "getdown-launcher-old.jar"),
+                  new File(appdir, "getdown-launcher.jar"),
+                  new File(appdir, "getdown-launcher-new.jar"));
+        }
+      }.start();
+    }
+
+    // get args needed before proper ArgParser
+    BootstrapArgs bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
+
+    if (!Platform.isJS())
+    {
+      // are we being --quiet ?
+      if (bootstrapArgs.contains(Arg.QUIET))
+      {
+        OutputStream devNull = new OutputStream()
+        {
+          @Override
+          public void write(int b)
           {
-            Console.debug("Nothing more to do");
+            // DO NOTHING
           }
-          Console.debug("Exiting, bye!");
-          // shutdownHook cannot be cancelled, JVM will now halt
+        };
+        System.setOut(new PrintStream(devNull));
+        // redirecting stderr not working
+        if (bootstrapArgs.getList(Arg.QUIET).size() > 1)
+        {
+          System.setErr(new PrintStream(devNull));
         }
-      });
+      }
     }
 
     System.out
@@ -320,22 +354,88 @@ public class Jalview
       System.out.println("Launcher version: " + val);
     }
 
+    if (Platform.isLinux() && LaunchUtils.getJavaVersion() < 11)
+    {
+      System.setProperty("flatlaf.uiScale", "1");
+    }
+
+    // get bootstrap properties (mainly for the logger level)
+    Properties bootstrapProperties = Cache
+            .bootstrapProperties(bootstrapArgs.get(Arg.PROPS));
+
     // report Jalview version
     Cache.loadBuildProperties(true);
 
+    // old ArgsParser
     ArgsParser aparser = new ArgsParser(args);
 
+    // old
     boolean headless = false;
+    // new
+    boolean headlessArg = false;
+
+    try
+    {
+      String logLevel = bootstrapArgs.contains(Arg.DEBUG) ? "DEBUG" : null;
+      if (logLevel == null && !(bootstrapProperties == null))
+      {
+        logLevel = bootstrapProperties.getProperty(Cache.JALVIEWLOGLEVEL);
+      }
+      Console.initLogger(logLevel);
+    } catch (NoClassDefFoundError error)
+    {
+      error.printStackTrace();
+      String message = "\nEssential logging libraries not found."
+              + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
+      Jalview.exit(message, 0);
+    }
+
+    // register SIGTERM listener
+    Runtime.getRuntime().addShutdownHook(new Thread()
+    {
+      public void run()
+      {
+        Console.debug("Running shutdown hook");
+        if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT)
+        {
+          // Got to here by a SIGTERM signal.
+          // Note we will not actually cancel the quit from here -- it's too
+          // late -- but we can wait for saving files.
+          Console.debug("Checking for saving files");
+          QuitHandler.getQuitResponse(false);
+        }
+        else
+        {
+          Console.debug("Nothing more to do");
+        }
+        Console.debug("Exiting, bye!");
+        // shutdownHook cannot be cancelled, JVM will now halt
+      }
+    });
 
-    String usrPropsFile = aparser.getValue("props");
-    Cache.loadProperties(usrPropsFile); // must do this
-                                        // before
+    String usrPropsFile = bootstrapArgs.contains(Arg.PROPS)
+            ? bootstrapArgs.get(Arg.PROPS)
+            : aparser.getValue("props");
     if (usrPropsFile != null)
     {
+      Cache.loadProperties(usrPropsFile);
       System.out.println(
               "CMD [-props " + usrPropsFile + "] executed successfully!");
     }
 
+    // new ArgParser
+    ArgParser argparser;
+    // --argfile=... -- OVERRIDES ALL NON-BOOTSTRAP ARGS
+    if (bootstrapArgs.contains(Arg.ARGFILE))
+    {
+      argparser = ArgParser
+              .parseArgFiles(bootstrapArgs.getList(Arg.ARGFILE));
+    }
+    else
+    {
+      argparser = new ArgParser(args);
+    }
+
     if (!Platform.isJS())
     /**
      * Java only
@@ -343,15 +443,24 @@ public class Jalview
      * @j2sIgnore
      */
     {
-      if (aparser.contains("help") || aparser.contains("h"))
+      if (aparser.contains("help") || aparser.contains("h")
+              || argparser.getBool(Arg.HELP))
       {
         showUsage();
-        System.exit(0);
+        Jalview.exit(null, 0);
+      }
+
+      if (bootstrapArgs.contains(Arg.HEADLESS))
+      {
+        System.setProperty("java.awt.headless", "true");
+        // new
+        headlessArg = argparser.getBool(Arg.HEADLESS);
       }
       if (aparser.contains("nodisplay") || aparser.contains("nogui")
               || aparser.contains("headless"))
       {
         System.setProperty("java.awt.headless", "true");
+        // old
         headless = true;
       }
       // anything else!
@@ -412,14 +521,14 @@ public class Jalview
     NoClassDefFoundError error)
     {
       error.printStackTrace();
-      System.out.println("\nEssential logging libraries not found."
-              + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
-      System.exit(0);
+      String message = "\nEssential logging libraries not found."
+              + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
+      Jalview.exit(message, 0);
     }
-
     desktop = null;
 
-    setLookAndFeel();
+    if (!(headless || headlessArg))
+      setLookAndFeel();
 
     /*
      * configure 'full' SO model if preferences say to, else use the default (full SO)
@@ -431,7 +540,7 @@ public class Jalview
       SequenceOntologyFactory.setInstance(new SequenceOntology());
     }
 
-    if (!headless)
+    if (!(headless || headlessArg))
     {
       Desktop.nosplash = aparser.contains("nosplash");
       desktop = new Desktop();
@@ -540,19 +649,40 @@ public class Jalview
                   .println("CMD [-noquestionnaire] executed successfully!");
         }
 
-        if (!aparser.contains("nonews")
-                || Cache.getProperty("NONEWS") == null)
+        if ((!aparser.contains("nonews")
+                && Cache.getProperty("NONEWS") == null
+                && !"false".equals(bootstrapArgs.get(Arg.NEWS)))
+                || "true".equals(bootstrapArgs.get(Arg.NEWS)))
         {
           desktop.checkForNews();
         }
 
         if (!aparser.contains("nohtmltemplates")
-                || Cache.getProperty("NOHTMLTEMPLATES") == null)
+                && Cache.getProperty("NOHTMLTEMPLATES") == null)
         {
           BioJsHTMLOutput.updateBioJS();
         }
       }
     }
+    // Run Commands from cli
+    cmds = new Commands(argparser, headlessArg);
+    boolean commandsSuccess = cmds.argsWereParsed();
+    if (commandsSuccess)
+    {
+      if (headlessArg)
+      {
+        Jalview.exit("Successfully completed commands in headless mode", 0);
+      }
+      Console.info("Successfully completed commands");
+    }
+    else
+    {
+      if (headlessArg)
+      {
+        Jalview.exit("Error when running Commands in headless mode", 1);
+      }
+      Console.warn("Error when running commands");
+    }
 
     // Check if JVM and compile version might cause problems and log if it
     // might.
@@ -564,25 +694,6 @@ public class Jalview
               + LaunchUtils.getJavaCompileVersion() + ".");
     }
 
-    // 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)
-    {
-      final File appdir = new File(appdirString);
-      new Thread()
-      {
-        @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;
@@ -597,10 +708,9 @@ public class Jalview
     groovyscript = aparser.getValue("groovy", true);
     file = aparser.getValue("open", true);
 
-    if (file == null && desktop == null)
+    if (file == null && desktop == null && !commandsSuccess)
     {
-      System.out.println("No files to open!");
-      System.exit(1);
+      Jalview.exit("No files to open!", 1);
     }
 
     long progress = -1;
@@ -627,11 +737,12 @@ public class Jalview
         {
           if (!(new File(file)).exists())
           {
-            System.out.println("Can't find " + file);
             if (headless)
             {
-              System.exit(1);
+              Jalview.exit(
+                      "Can't find file '" + file + "' in headless mode", 1);
             }
+            Console.warn("Can't find file'" + file + "'");
           }
         }
       }
@@ -666,7 +777,7 @@ public class Jalview
           if (cs != null)
           {
             System.out.println(
-                    "CMD [-color " + data + "] executed successfully!");
+                    "CMD [-colour " + data + "] executed successfully!");
           }
           af.changeColour(cs);
         }
@@ -869,7 +980,10 @@ public class Jalview
     // ////////////////////
 
     if (!Platform.isJS() && !headless && file == null
-            && Cache.getDefault("SHOW_STARTUP_FILE", true))
+            && Cache.getDefault("SHOW_STARTUP_FILE", true)
+            && !cmds.commandArgsProvided())
+    // don't open the startup file if command line args have been processed
+    // (&& !Commands.commandArgsProvided())
     /**
      * Java only
      * 
@@ -910,6 +1024,10 @@ public class Jalview
 
       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
               format);
+      // don't ask to save when quitting if only the startup file has been
+      // opened
+      Console.debug("Resetting up-to-date flag for startup file");
+      startUpAlframe.getViewport().setSavedUpToDate(true);
       // extract groovy arguments before anything else.
     }
 
@@ -1001,20 +1119,6 @@ public class Jalview
         Console.error("Could not set requested laf=" + laf);
       }
       break;
-    case "quaqua":
-      lafSet = setQuaquaLookAndFeel();
-      if (!lafSet)
-      {
-        Console.error("Could not set requested laf=" + laf);
-      }
-      break;
-    case "vaqua":
-      lafSet = setVaquaLookAndFeel();
-      if (!lafSet)
-      {
-        Console.error("Could not set requested laf=" + laf);
-      }
-      break;
     case "mac":
       lafSet = setMacLookAndFeel();
       if (!lafSet)
@@ -1032,7 +1136,7 @@ public class Jalview
       setSystemLookAndFeel();
       if (Platform.isLinux())
       {
-        setMetalLookAndFeel();
+        setLinuxLookAndFeel();
       }
       if (Platform.isMac())
       {
@@ -1123,55 +1227,113 @@ public class Jalview
 
   private static boolean setFlatLookAndFeel()
   {
-    boolean set = setSpecificLookAndFeel("flatlaf light",
-            "com.formdev.flatlaf.FlatLightLaf", false);
-    if (set)
+    boolean set = false;
+    if (SystemInfo.isMacOS)
     {
-      if (Platform.isMac())
+      try
       {
-        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);
-        }
+        UIManager.setLookAndFeel(
+                "com.formdev.flatlaf.themes.FlatMacLightLaf");
+        set = true;
+        Console.debug("Using FlatMacLightLaf");
+      } catch (ClassNotFoundException | InstantiationException
+              | IllegalAccessException | UnsupportedLookAndFeelException e)
+      {
+        Console.debug("Exception loading FlatLightLaf", e);
+      }
+      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)
+      {
+        Console.debug("Setting transparent title bar");
+        Desktop.desktop.getRootPane()
+                .putClientProperty("apple.awt.fullWindowContent", true);
+        Desktop.desktop.getRootPane()
+                .putClientProperty("apple.awt.transparentTitleBar", true);
+        Desktop.desktop.getRootPane()
+                .putClientProperty("apple.awt.fullscreenable", true);
+      }
+      SwingUtilities.invokeLater(() -> {
+        FlatMacLightLaf.setup();
+      });
+      Console.debug("Using FlatMacLightLaf");
+      set = true;
+    }
+    if (!set)
+    {
+      try
+      {
+        UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
+        set = true;
+        Console.debug("Using FlatLightLaf");
+      } catch (ClassNotFoundException | InstantiationException
+              | IllegalAccessException | UnsupportedLookAndFeelException e)
+      {
+        Console.debug("Exception loading FlatLightLaf", e);
+      }
+      // Windows specific properties here
+      SwingUtilities.invokeLater(() -> {
+        FlatLightLaf.setup();
+      });
+      Console.debug("Using FlatLightLaf");
+      set = true;
+    }
+    else if (SystemInfo.isLinux)
+    {
+      try
+      {
+        UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
+        set = true;
+        Console.debug("Using FlatLightLaf");
+      } catch (ClassNotFoundException | InstantiationException
+              | IllegalAccessException | UnsupportedLookAndFeelException e)
+      {
+        Console.debug("Exception loading FlatLightLaf", e);
+      }
+      // enable custom window decorations
+      JFrame.setDefaultLookAndFeelDecorated(true);
+      JDialog.setDefaultLookAndFeelDecorated(true);
+      SwingUtilities.invokeLater(() -> {
+        FlatLightLaf.setup();
+      });
+      Console.debug("Using FlatLightLaf");
+      set = true;
+    }
 
-        SwingUtilities.invokeLater(() -> {
-          FlatLightLaf.setup();
-        });
+    if (!set)
+    {
+      try
+      {
+        UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
+        set = true;
+        Console.debug("Using FlatLightLaf");
+      } catch (ClassNotFoundException | InstantiationException
+              | IllegalAccessException | UnsupportedLookAndFeelException e)
+      {
+        Console.debug("Exception loading FlatLightLaf", e);
       }
+    }
 
+    if (set)
+    {
+      UIManager.put("TabbedPane.tabType", "card");
       UIManager.put("TabbedPane.showTabSeparators", true);
+      UIManager.put("TabbedPane.showContentSeparator", true);
       UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
       UIManager.put("TabbedPane.tabsOverlapBorder", true);
-      // UIManager.put("TabbedPane.hasFullBorder", 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",
-            ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass()
-                    .getName(),
-            false);
-  }
-
-  private static boolean setVaquaLookAndFeel()
-  {
-    return setSpecificLookAndFeel("vaqua",
-            "org.violetlib.aqua.AquaLookAndFeel", false);
+    Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
+    return set;
   }
 
   private static boolean setMacLookAndFeel()
@@ -1193,6 +1355,18 @@ public class Jalview
     return set;
   }
 
+  private static boolean setLinuxLookAndFeel()
+  {
+    boolean set = false;
+    set = setFlatLookAndFeel();
+    if (!set)
+      set = setMetalLookAndFeel();
+    // avoid GtkLookAndFeel -- not good results especially on HiDPI
+    if (!set)
+      set = setNimbusLookAndFeel();
+    return set;
+  }
+
   private static void showUsage()
   {
     System.out.println(
@@ -1408,7 +1582,7 @@ public class Jalview
   public void quit()
   {
     // System.exit will run the shutdownHook first
-    System.exit(0);
+    Jalview.exit("Quitting now. Bye!", 0);
   }
 
   public static AlignFrame getCurrentAlignFrame()
@@ -1420,4 +1594,21 @@ public class Jalview
   {
     Jalview.currentAlignFrame = currentAlignFrame;
   }
+
+  protected Commands getCommands()
+  {
+    return cmds;
+  }
+
+  public static void exit(String message, int exitcode)
+  {
+    Console.debug("Using Jalview.exit");
+    if (message != null)
+      if (exitcode == 0)
+        Console.info(message);
+      else
+        Console.error(message);
+    if (exitcode > -1)
+      System.exit(exitcode);
+  }
 }