JAL-1988 JAL-3772 Quit confirmation dialog boxes with saving files check and wait
[jalview.git] / src / jalview / bin / Jalview.java
index f01cbb6..3d3e99f 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;
@@ -44,11 +45,16 @@ import java.util.logging.ConsoleHandler;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import javax.swing.JOptionPane;
+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;
@@ -67,10 +73,13 @@ import jalview.io.HtmlSvgOutput;
 import jalview.io.IdentifyFile;
 import jalview.io.NewickFile;
 import jalview.io.gff.SequenceOntologyFactory;
+import jalview.jbgui.QuitHandler;
+import jalview.jbgui.QuitHandler.QResponse;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.util.ChannelProperties;
 import jalview.util.HttpUtils;
+import jalview.util.LaunchUtils;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.ws.jws2.Jws2Discoverer;
@@ -95,6 +104,10 @@ 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");
   }
 
   /*
@@ -260,6 +273,25 @@ public class Jalview
     if (!Platform.isJS())
     {
       System.setSecurityManager(null);
+
+      Runtime.getRuntime().addShutdownHook(new Thread()
+      {
+        public void run()
+        {
+          Console.debug("Running shutdown hook");
+          if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT)
+          {
+            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
+        }
+      });
     }
 
     System.out
@@ -268,6 +300,7 @@ public class Jalview
     System.out.println(System.getProperty("os.arch") + " "
             + System.getProperty("os.name") + " "
             + System.getProperty("os.version"));
+
     String val = System.getProperty("sys.install4jVersion");
     if (val != null)
     {
@@ -288,10 +321,12 @@ public class Jalview
     Cache.loadBuildProperties(true);
 
     ArgsParser aparser = new ArgsParser(args);
+
     boolean headless = false;
 
     String usrPropsFile = aparser.getValue("props");
-    Cache.loadProperties(usrPropsFile); // must do this before
+    Cache.loadProperties(usrPropsFile); // must do this
+                                        // before
     if (usrPropsFile != null)
     {
       System.out.println(
@@ -368,8 +403,10 @@ public class Jalview
 
     try
     {
-      Cache.initLogger();
-    } catch (NoClassDefFoundError error)
+      Console.initLogger();
+    } catch (
+
+    NoClassDefFoundError error)
     {
       error.printStackTrace();
       System.out.println("\nEssential logging libraries not found."
@@ -402,13 +439,13 @@ public class Jalview
         JalviewTaskbar.setTaskbar(this);
       } catch (Exception e)
       {
-        Cache.info("Cannot set Taskbar");
-        Cache.error(e.getMessage());
+        Console.info("Cannot set Taskbar");
+        Console.error(e.getMessage());
         // e.printStackTrace();
       } catch (Throwable t)
       {
-        Cache.info("Cannot set Taskbar");
-        Cache.error(t.getMessage());
+        Console.info("Cannot set Taskbar");
+        Console.error(t.getMessage());
         // t.printStackTrace();
       }
 
@@ -424,6 +461,35 @@ public class Jalview
        * @j2sIgnore
        */
       {
+
+        /**
+         * Check to see that the JVM version being run is suitable for the Java
+         * version this Jalview was compiled for. Popup a warning if not.
+         */
+        if (!LaunchUtils.checkJavaVersion())
+        {
+          Console.warn("The Java version being used (Java "
+                  + LaunchUtils.getJavaVersion()
+                  + ") may lead to problems. This installation of Jalview should be used with Java "
+                  + LaunchUtils.getJavaCompileVersion() + ".");
+
+          if (!LaunchUtils
+                  .getBooleanUserPreference("IGNORE_JVM_WARNING_POPUP"))
+          {
+            Object[] options = {
+                MessageManager.getString("label.continue") };
+            JOptionPane.showOptionDialog(null,
+                    MessageManager.formatMessage(
+                            "warning.wrong_jvm_version_message",
+                            LaunchUtils.getJavaVersion(),
+                            LaunchUtils.getJavaCompileVersion()),
+                    MessageManager
+                            .getString("warning.wrong_jvm_version_title"),
+                    JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
+                    null, options, options[0]);
+          }
+        }
+
         if (!aparser.contains("nowebservicediscovery"))
         {
           desktop.startServiceDiscovery();
@@ -444,7 +510,7 @@ public class Jalview
           {
             // Start the desktop questionnaire prompter with the specified
             // questionnaire
-            Cache.debug("Starting questionnaire url at " + url);
+            Console.debug("Starting questionnaire url at " + url);
             desktop.checkForQuestionnaire(url);
             System.out.println("CMD questionnaire[-" + url
                     + "] executed successfully!");
@@ -459,7 +525,7 @@ public class Jalview
               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
               // //
               String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
-              Cache.debug(
+              Console.debug(
                       "Starting questionnaire with default url: " + defurl);
               desktop.checkForQuestionnaire(defurl);
             }
@@ -471,15 +537,30 @@ public class Jalview
                   .println("CMD [-noquestionnaire] executed successfully!");
         }
 
-        if (!aparser.contains("nonews"))
+        if (!aparser.contains("nonews")
+                || Cache.getProperty("NONEWS") == null)
         {
           desktop.checkForNews();
         }
 
-        BioJsHTMLOutput.updateBioJS();
+        if (!aparser.contains("nohtmltemplates")
+                || Cache.getProperty("NOHTMLTEMPLATES") == null)
+        {
+          BioJsHTMLOutput.updateBioJS();
+        }
       }
     }
 
+    // Check if JVM and compile version might cause problems and log if it
+    // might.
+    if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
+    {
+      Console.warn("The Java version being used (Java "
+              + LaunchUtils.getJavaVersion()
+              + ") may lead to problems. This installation of Jalview should be used with Java "
+              + LaunchUtils.getJavaCompileVersion() + ".");
+    }
+
     // Move any new getdown-launcher-new.jar into place over old
     // getdown-launcher.jar
     String appdirString = System.getProperty("getdownappdir");
@@ -500,8 +581,11 @@ public class Jalview
     }
 
     String file = null, data = null;
+
     FileFormatI format = null;
+
     DataSourceType protocol = null;
+
     FileLoader fileLoader = new FileLoader(!headless);
 
     String groovyscript = null; // script to execute after all loading is
@@ -515,6 +599,7 @@ public class Jalview
       System.out.println("No files to open!");
       System.exit(1);
     }
+
     long progress = -1;
     // Finally, deal with the remaining input data.
     if (file != null)
@@ -774,6 +859,7 @@ public class Jalview
         }
       }
     }
+
     AlignFrame startUpAlframe = null;
     // We'll only open the default file if the desktop is visible.
     // And the user
@@ -852,8 +938,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");
@@ -874,62 +960,69 @@ public class Jalview
       lafSet = setCrossPlatformLookAndFeel();
       if (!lafSet)
       {
-        Cache.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "system":
       lafSet = setSystemLookAndFeel();
       if (!lafSet)
       {
-        Cache.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "gtk":
       lafSet = setGtkLookAndFeel();
       if (!lafSet)
       {
-        Cache.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "metal":
       lafSet = setMetalLookAndFeel();
       if (!lafSet)
       {
-        Cache.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "nimbus":
       lafSet = setNimbusLookAndFeel();
       if (!lafSet)
       {
-        Cache.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.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "vaqua":
       lafSet = setVaquaLookAndFeel();
       if (!lafSet)
       {
-        Cache.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "mac":
       lafSet = setMacLookAndFeel();
       if (!lafSet)
       {
-        Cache.error("Could not set requested laf=" + laf);
+        Console.error("Could not set requested laf=" + laf);
       }
       break;
     case "none":
       break;
     default:
-      Cache.error("Requested laf=" + laf + " not implemented");
+      Console.error("Requested laf=" + laf + " not implemented");
     }
     if (!lafSet)
     {
@@ -955,9 +1048,9 @@ public class Jalview
       set = true;
     } catch (Exception ex)
     {
-      Cache.error("Unexpected Look and Feel Exception");
-      Cache.error(ex.getMessage());
-      Cache.debug(Cache.getStackTraceString(ex));
+      Console.error("Unexpected Look and Feel Exception");
+      Console.error(ex.getMessage());
+      Console.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -971,9 +1064,9 @@ public class Jalview
       set = true;
     } catch (Exception ex)
     {
-      Cache.error("Unexpected Look and Feel Exception");
-      Cache.error(ex.getMessage());
-      Cache.debug(Cache.getStackTraceString(ex));
+      Console.error("Unexpected Look and Feel Exception");
+      Console.error(ex.getMessage());
+      Console.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -1000,9 +1093,9 @@ public class Jalview
       set = true;
     } catch (Exception ex)
     {
-      Cache.error("Unexpected Look and Feel Exception");
-      Cache.error(ex.getMessage());
-      Cache.debug(Cache.getStackTraceString(ex));
+      Console.error("Unexpected Look and Feel Exception");
+      Console.error(ex.getMessage());
+      Console.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -1025,6 +1118,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",
@@ -1112,16 +1244,17 @@ public class Jalview
               @Override
               public void run()
               {
-                Cache.debug("Initialising googletracker for usage stats.");
+                Console.debug(
+                        "Initialising googletracker for usage stats.");
                 Cache.initGoogleTracker();
-                Cache.debug("Tracking enabled.");
+                Console.debug("Tracking enabled.");
               }
             }, new Runnable()
             {
               @Override
               public void run()
               {
-                Cache.debug("Not enabling Google Tracking.");
+                Console.debug("Not enabling Google Tracking.");
               }
             }, null, true);
     desktop.addDialogThread(prompter);
@@ -1263,19 +1396,12 @@ public class Jalview
   }
 
   /**
-   * Quit method delegates to Desktop.quit - unless running in headless mode
-   * when it just ends the JVM
+   * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
    */
   public void quit()
   {
-    if (desktop != null)
-    {
-      desktop.quit();
-    }
-    else
-    {
-      System.exit(0);
-    }
+    // System.exit will run the shutdownHook first
+    System.exit(0);
   }
 
   public static AlignFrame getCurrentAlignFrame()