Merge branch 'improvement/JAL-3830_remove_dock_icon_in_headless_mode' into merge...
[jalview.git] / src / jalview / bin / Jalview.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.bin;
22
23 import java.awt.Color;
24 import java.io.BufferedReader;
25 import java.io.File;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStreamReader;
29 import java.io.OutputStream;
30 import java.io.OutputStreamWriter;
31 import java.io.PrintStream;
32 import java.io.PrintWriter;
33 import java.net.MalformedURLException;
34 import java.net.URI;
35 import java.net.URISyntaxException;
36 import java.net.URL;
37 import java.security.AllPermission;
38 import java.security.CodeSource;
39 import java.security.PermissionCollection;
40 import java.security.Permissions;
41 import java.security.Policy;
42 import java.util.ArrayList;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Locale;
46 import java.util.Map;
47 import java.util.Properties;
48 import java.util.Vector;
49 import java.util.logging.ConsoleHandler;
50 import java.util.logging.Level;
51 import java.util.logging.Logger;
52 import java.util.stream.Collectors;
53
54 import javax.swing.JDialog;
55 import javax.swing.JFrame;
56 import javax.swing.JInternalFrame;
57 import javax.swing.JOptionPane;
58 import javax.swing.SwingUtilities;
59 import javax.swing.UIManager;
60 import javax.swing.UIManager.LookAndFeelInfo;
61 import javax.swing.UnsupportedLookAndFeelException;
62
63 import com.formdev.flatlaf.FlatLightLaf;
64 import com.formdev.flatlaf.themes.FlatMacLightLaf;
65 import com.formdev.flatlaf.util.SystemInfo;
66 import com.threerings.getdown.util.LaunchUtil;
67
68 //import edu.stanford.ejalbert.launching.IBrowserLaunching;
69 import groovy.lang.Binding;
70 import groovy.util.GroovyScriptEngine;
71 import jalview.bin.argparser.Arg;
72 import jalview.bin.argparser.Arg.Opt;
73 import jalview.bin.argparser.Arg.Type;
74 import jalview.bin.argparser.ArgParser;
75 import jalview.bin.argparser.BootstrapArgs;
76 import jalview.ext.so.SequenceOntology;
77 import jalview.gui.AlignFrame;
78 import jalview.gui.Desktop;
79 import jalview.gui.PromptUserConfig;
80 import jalview.gui.QuitHandler;
81 import jalview.gui.QuitHandler.QResponse;
82 import jalview.gui.StructureViewerBase;
83 import jalview.io.AppletFormatAdapter;
84 import jalview.io.BioJsHTMLOutput;
85 import jalview.io.DataSourceType;
86 import jalview.io.FileFormat;
87 import jalview.io.FileFormatException;
88 import jalview.io.FileFormatI;
89 import jalview.io.FileFormats;
90 import jalview.io.FileLoader;
91 import jalview.io.HtmlSvgOutput;
92 import jalview.io.IdentifyFile;
93 import jalview.io.NewickFile;
94 import jalview.io.exceptions.ImageOutputException;
95 import jalview.io.gff.SequenceOntologyFactory;
96 import jalview.schemes.ColourSchemeI;
97 import jalview.schemes.ColourSchemeProperty;
98 import jalview.util.ChannelProperties;
99 import jalview.util.HttpUtils;
100 import jalview.util.LaunchUtils;
101 import jalview.util.MessageManager;
102 import jalview.util.Platform;
103 import jalview.ws.jws2.Jws2Discoverer;
104
105 /**
106  * Main class for Jalview Application <br>
107  * <br>
108  * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
109  * jalview.bin.Jalview
110  * 
111  * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
112  * jalview.bin.Jalview jalview.bin.Jalview
113  * 
114  * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
115  * embellish '*' to e.g. '*.jar')
116  * 
117  * @author $author$
118  * @version $Revision$
119  */
120 public class Jalview
121 {
122   static
123   {
124     Platform.getURLCommandArguments();
125     Platform.addJ2SDirectDatabaseCall("https://www.jalview.org");
126     Platform.addJ2SDirectDatabaseCall("http://www.jalview.org");
127     Platform.addJ2SDirectDatabaseCall("http://www.compbio.dundee.ac.uk");
128     Platform.addJ2SDirectDatabaseCall("https://www.compbio.dundee.ac.uk");
129   }
130
131   /*
132    * singleton instance of this class
133    */
134   private static Jalview instance;
135
136   private Desktop desktop;
137
138   protected Commands cmds;
139
140   public static AlignFrame currentAlignFrame;
141
142   public ArgParser argparser = null;
143
144   public BootstrapArgs bootstrapArgs = null;
145
146   private boolean QUIET = false;
147
148   public static boolean quiet()
149   {
150     return Jalview.getInstance() != null && Jalview.getInstance().QUIET;
151   }
152
153   static
154   {
155     if (!Platform.isJS())
156     /**
157      * Java only
158      * 
159      * @j2sIgnore
160      */
161     {
162       // grab all the rights we can for the JVM
163       Policy.setPolicy(new Policy()
164       {
165         @Override
166         public PermissionCollection getPermissions(CodeSource codesource)
167         {
168           Permissions perms = new Permissions();
169           perms.add(new AllPermission());
170           return (perms);
171         }
172
173         @Override
174         public void refresh()
175         {
176         }
177       });
178     }
179   }
180
181   /**
182    * keep track of feature fetching tasks.
183    * 
184    * @author JimP
185    * 
186    */
187   class FeatureFetcher
188   {
189     /*
190      * TODO: generalise to track all jalview events to orchestrate batch processing
191      * events.
192      */
193
194     private int queued = 0;
195
196     private int running = 0;
197
198     public FeatureFetcher()
199     {
200
201     }
202
203     public void addFetcher(final AlignFrame af,
204             final Vector<String> dasSources)
205     {
206       final long id = System.currentTimeMillis();
207       queued++;
208       final FeatureFetcher us = this;
209       new Thread(new Runnable()
210       {
211
212         @Override
213         public void run()
214         {
215           synchronized (us)
216           {
217             queued--;
218             running++;
219           }
220
221           af.setProgressBar(MessageManager
222                   .getString("status.das_features_being_retrived"), id);
223           af.featureSettings_actionPerformed(null);
224           af.setProgressBar(null, id);
225           synchronized (us)
226           {
227             running--;
228           }
229         }
230       }).start();
231     }
232
233     public synchronized boolean allFinished()
234     {
235       return queued == 0 && running == 0;
236     }
237
238   }
239
240   public static Jalview getInstance()
241   {
242     return instance;
243   }
244
245   /**
246    * main class for Jalview application
247    * 
248    * @param args
249    *          open <em>filename</em>
250    */
251   public static void main(String[] args)
252   {
253     // setLogging(); // BH - for event debugging in JavaScript
254     instance = new Jalview();
255     instance.doMain(args);
256   }
257
258   private static void logClass(String name)
259   {
260     // BH - for event debugging in JavaScript
261     ConsoleHandler consoleHandler = new ConsoleHandler();
262     consoleHandler.setLevel(Level.ALL);
263     Logger logger = Logger.getLogger(name);
264     logger.setLevel(Level.ALL);
265     logger.addHandler(consoleHandler);
266   }
267
268   @SuppressWarnings("unused")
269   private static void setLogging()
270   {
271
272     /**
273      * @j2sIgnore
274      * 
275      */
276     {
277       Console.outPrintln("not in js");
278     }
279
280     // BH - for event debugging in JavaScript (Java mode only)
281     if (!Platform.isJS())
282     /**
283      * Java only
284      * 
285      * @j2sIgnore
286      */
287     {
288       Logger.getLogger("").setLevel(Level.ALL);
289       logClass("java.awt.EventDispatchThread");
290       logClass("java.awt.EventQueue");
291       logClass("java.awt.Component");
292       logClass("java.awt.focus.Component");
293       logClass("java.awt.focus.DefaultKeyboardFocusManager");
294     }
295
296   }
297
298   /**
299    * @param args
300    */
301   void doMain(String[] args)
302   {
303     if (!Platform.isJS())
304     {
305       System.setSecurityManager(null);
306     }
307
308     if (args == null || args.length == 0 || (args.length == 1
309             && (args[0] == null || args[0].length() == 0)))
310     {
311       args = new String[] {};
312     }
313
314     // get args needed before proper ArgParser
315     bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
316
317     if (!Platform.isJS())
318     {
319       // are we being --quiet ?
320       if (bootstrapArgs.contains(Arg.QUIET))
321       {
322         QUIET = true;
323         OutputStream devNull = new OutputStream()
324         {
325
326           @Override
327           public void write(int b)
328           {
329             // DO NOTHING
330           }
331         };
332         System.setOut(new PrintStream(devNull));
333         // redirecting stderr not working
334         if (bootstrapArgs.getList(Arg.QUIET).size() > 1)
335         {
336           System.setErr(new PrintStream(devNull));
337         }
338       }
339
340       if (bootstrapArgs.contains(Arg.HELP)
341               || bootstrapArgs.contains(Arg.VERSION))
342       {
343         QUIET = true;
344       }
345     }
346
347     // set individual session preferences
348     if (bootstrapArgs.contains(Arg.P))
349     {
350       for (String kev : bootstrapArgs.getValueList(Arg.P))
351       {
352         if (kev == null)
353         {
354           continue;
355         }
356         int equalsIndex = kev.indexOf(ArgParser.EQUALS);
357         if (equalsIndex > -1)
358         {
359           String key = kev.substring(0, equalsIndex);
360           String val = kev.substring(equalsIndex + 1);
361           Cache.setSessionProperty(key, val);
362         }
363       }
364     }
365
366     // Move any new getdown-launcher-new.jar into place over old
367     // getdown-launcher.jar
368     String appdirString = System.getProperty("getdownappdir");
369     if (appdirString != null && appdirString.length() > 0)
370     {
371       final File appdir = new File(appdirString);
372       new Thread()
373       {
374
375         @Override
376         public void run()
377         {
378           LaunchUtil.upgradeGetdown(
379                   new File(appdir, "getdown-launcher-old.jar"),
380                   new File(appdir, "getdown-launcher.jar"),
381                   new File(appdir, "getdown-launcher-new.jar"));
382         }
383       }.start();
384     }
385
386     if (!quiet() || !bootstrapArgs.outputToStdout()
387             || bootstrapArgs.contains(Arg.VERSION))
388     {
389       Console.outPrintln(
390               "Java version: " + System.getProperty("java.version"));
391       Console.outPrintln("Java home: " + System.getProperty("java.home"));
392       Console.outPrintln("Java arch: " + System.getProperty("os.arch") + " "
393               + System.getProperty("os.name") + " "
394               + System.getProperty("os.version"));
395
396       String val = System.getProperty("sys.install4jVersion");
397       if (val != null)
398       {
399         Console.outPrintln("Install4j version: " + val);
400       }
401       val = System.getProperty("installer_template_version");
402       if (val != null)
403       {
404         Console.outPrintln("Install4j template version: " + val);
405       }
406       val = System.getProperty("launcher_version");
407       if (val != null)
408       {
409         Console.outPrintln("Launcher version: " + val);
410       }
411     }
412
413     if (Platform.isLinux() && LaunchUtils.getJavaVersion() < 11)
414     {
415       System.setProperty("flatlaf.uiScale", "1");
416     }
417
418     // get bootstrap properties (mainly for the logger level)
419     Properties bootstrapProperties = Cache
420             .bootstrapProperties(bootstrapArgs.getValue(Arg.PROPS));
421
422     // report Jalview version
423     Cache.loadBuildProperties(
424             !quiet() || bootstrapArgs.contains(Arg.VERSION));
425
426     // stop now if only after --version
427     if (bootstrapArgs.contains(Arg.VERSION))
428     {
429       Jalview.exit(null, 0);
430     }
431
432     // old ArgsParser
433     ArgsParser aparser = new ArgsParser(args);
434
435     // old
436     boolean headless = false;
437     // new
438     boolean headlessArg = false;
439
440     try
441     {
442       String logLevel = null;
443       if (bootstrapArgs.contains(Arg.TRACE))
444       {
445         logLevel = "TRACE";
446       }
447       else if (bootstrapArgs.contains(Arg.DEBUG))
448       {
449         logLevel = "DEBUG";
450       }
451       if (logLevel == null && !(bootstrapProperties == null))
452       {
453         logLevel = bootstrapProperties.getProperty(Cache.JALVIEWLOGLEVEL);
454       }
455       Console.initLogger(logLevel);
456     } catch (NoClassDefFoundError error)
457     {
458       error.printStackTrace();
459       String message = "\nEssential logging libraries not found."
460               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
461       Jalview.exit(message, 0);
462     }
463
464     // register SIGTERM listener
465     Runtime.getRuntime().addShutdownHook(new Thread()
466     {
467       @Override
468       public void run()
469       {
470         Console.debug("Running shutdown hook");
471         QuitHandler.startForceQuit();
472         boolean closeExternal = Cache
473                 .getDefault("DEFAULT_CLOSE_EXTERNAL_VIEWERS", false)
474                 || Cache.getDefault("ALWAYS_CLOSE_EXTERNAL_VIEWERS", false);
475         StructureViewerBase.setQuitClose(closeExternal);
476         if (desktop != null)
477         {
478           for (JInternalFrame frame : Desktop.desktop.getAllFrames())
479           {
480             if (frame instanceof StructureViewerBase)
481             {
482               ((StructureViewerBase) frame).closeViewer(closeExternal);
483             }
484           }
485         }
486
487         if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT)
488         {
489           // Got to here by a SIGTERM signal.
490           // Note we will not actually cancel the quit from here -- it's too
491           // late -- but we can wait for saving files and close external viewers
492           // if configured.
493           // Close viewers/Leave viewers open
494           Console.debug("Checking for saving files");
495           QuitHandler.getQuitResponse(false);
496         }
497         else
498         {
499           Console.debug("Nothing more to do");
500         }
501         Console.debug("Exiting, bye!");
502         // shutdownHook cannot be cancelled, JVM will now halt
503       }
504     });
505
506     String usrPropsFile = bootstrapArgs.contains(Arg.PROPS)
507             ? bootstrapArgs.getValue(Arg.PROPS)
508             : aparser.getValue("props");
509     // if usrPropsFile == null, loadProperties will use the Channel
510     // preferences.file
511     Cache.loadProperties(usrPropsFile);
512     if (usrPropsFile != null)
513     {
514       Console.outPrintln(
515               "CMD [-props " + usrPropsFile + "] executed successfully!");
516       testoutput(bootstrapArgs, Arg.PROPS,
517               "test/jalview/bin/testProps.jvprops", usrPropsFile);
518     }
519
520     // --argfile=... -- OVERRIDES ALL NON-BOOTSTRAP ARGS
521     if (bootstrapArgs.contains(Arg.ARGFILE))
522     {
523       argparser = ArgParser.parseArgFiles(
524               bootstrapArgs.getValueList(Arg.ARGFILE),
525               bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS),
526               bootstrapArgs);
527     }
528     else
529     {
530       argparser = new ArgParser(args,
531               bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS),
532               bootstrapArgs);
533     }
534
535     if (!Platform.isJS())
536     /**
537      * Java only
538      * 
539      * @j2sIgnore
540      */
541     {
542       if (bootstrapArgs.contains(Arg.HELP))
543       {
544         List<Map.Entry<Type, String>> helpArgs = bootstrapArgs
545                 .getList(Arg.HELP);
546         Console.outPrintln(Arg.usage(helpArgs.stream().map(e -> e.getKey())
547                 .collect(Collectors.toList())));
548         Jalview.exit(null, 0);
549       }
550       if (aparser.contains("help") || aparser.contains("h"))
551       {
552         /*
553          * Now using new usage statement.
554         showUsage();
555         */
556         Console.outPrintln(Arg.usage());
557         Jalview.exit(null, 0);
558       }
559
560       // new CLI
561       headlessArg = bootstrapArgs.isHeadless();
562       if (headlessArg)
563       {
564         System.setProperty("java.awt.headless", "true");
565       }
566       // old CLI
567       if (aparser.contains("nodisplay") || aparser.contains("nogui")
568               || aparser.contains("headless"))
569       {
570         System.setProperty("java.awt.headless", "true");
571         headless = true;
572       }
573       // anything else!
574
575       // allow https handshakes to download intermediate certs if necessary
576       System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
577
578       String jabawsUrl = bootstrapArgs.getValue(Arg.JABAWS);
579       if (jabawsUrl == null)
580         jabawsUrl = aparser.getValue("jabaws");
581       if (jabawsUrl != null)
582       {
583         try
584         {
585           Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
586           Console.outPrintln(
587                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
588           testoutput(bootstrapArgs, Arg.JABAWS,
589                   "http://www.compbio.dundee.ac.uk/jabaws", jabawsUrl);
590         } catch (MalformedURLException e)
591         {
592           jalview.bin.Console.errPrintln(
593                   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
594         }
595       }
596     }
597
598     List<String> setprops = new ArrayList<>();
599     if (bootstrapArgs.contains(Arg.SETPROP))
600     {
601       setprops = bootstrapArgs.getValueList(Arg.SETPROP);
602     }
603     else
604     {
605       String sp = aparser.getValue("setprop");
606       while (sp != null)
607       {
608         setprops.add(sp);
609         sp = aparser.getValue("setprop");
610       }
611     }
612     for (String setprop : setprops)
613     {
614       int p = setprop.indexOf('=');
615       if (p == -1)
616       {
617         System.err
618                 .println("Ignoring invalid setprop argument : " + setprop);
619       }
620       else
621       {
622         jalview.bin.Console
623                 .errPrintln("Executing setprop argument: " + setprop);
624         if (Platform.isJS())
625         {
626           Cache.setProperty(setprop.substring(0, p),
627                   setprop.substring(p + 1));
628         }
629         // DISABLED FOR SECURITY REASONS
630         // TODO: add a property to allow properties to be overriden by cli args
631         // Cache.setProperty(setprop.substring(0,p), setprop.substring(p+1));
632       }
633     }
634     if (System.getProperty("java.awt.headless") != null
635             && System.getProperty("java.awt.headless").equals("true"))
636     {
637       headless = true;
638     }
639     System.setProperty("http.agent", HttpUtils.getUserAgent());
640
641     try
642     {
643       Console.initLogger();
644     } catch (
645
646     NoClassDefFoundError error)
647     {
648       error.printStackTrace();
649       String message = "\nEssential logging libraries not found."
650               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview";
651       Jalview.exit(message, 0);
652     }
653     desktop = null;
654
655     if (!(headless || headlessArg))
656       setLookAndFeel();
657
658     /*
659      * configure 'full' SO model if preferences say to, else use the default (full SO)
660      * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
661      */
662     boolean soDefault = !Platform.isJS();
663     if (Cache.getDefault("USE_FULL_SO", soDefault))
664     {
665       SequenceOntologyFactory.setInstance(new SequenceOntology());
666     }
667
668     if (!(headless || headlessArg))
669     {
670       Desktop.nosplash = "false".equals(bootstrapArgs.getValue(Arg.SPLASH))
671               || aparser.contains("nosplash")
672               || Cache.getDefault("SPLASH", "true").equals("false");
673       desktop = new Desktop();
674       desktop.setInBatchMode(true); // indicate we are starting up
675
676       try
677       {
678         JalviewTaskbar.setTaskbar(this);
679       } catch (Exception e)
680       {
681         Console.info("Cannot set Taskbar");
682         Console.error(e.getMessage());
683         // e.printStackTrace();
684       } catch (Throwable t)
685       {
686         Console.info("Cannot set Taskbar");
687         Console.error(t.getMessage());
688         // t.printStackTrace();
689       }
690
691       // set Proxy settings before all the internet calls
692       Cache.setProxyPropertiesFromPreferences();
693
694       desktop.setVisible(true);
695
696       if (!Platform.isJS())
697       /**
698        * Java only
699        * 
700        * @j2sIgnore
701        */
702       {
703
704         /**
705          * Check to see that the JVM version being run is suitable for the Java
706          * version this Jalview was compiled for. Popup a warning if not.
707          */
708         if (!LaunchUtils.checkJavaVersion())
709         {
710           Console.warn("The Java version being used (Java "
711                   + LaunchUtils.getJavaVersion()
712                   + ") may lead to problems. This installation of Jalview should be used with Java "
713                   + LaunchUtils.getJavaCompileVersion() + ".");
714
715           if (!LaunchUtils
716                   .getBooleanUserPreference("IGNORE_JVM_WARNING_POPUP"))
717           {
718             Object[] options = {
719                 MessageManager.getString("label.continue") };
720             JOptionPane.showOptionDialog(null,
721                     MessageManager.formatMessage(
722                             "warning.wrong_jvm_version_message",
723                             LaunchUtils.getJavaVersion(),
724                             LaunchUtils.getJavaCompileVersion()),
725                     MessageManager
726                             .getString("warning.wrong_jvm_version_title"),
727                     JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
728                     null, options, options[0]);
729           }
730         }
731
732         boolean webservicediscovery = bootstrapArgs
733                 .getBoolean(Arg.WEBSERVICEDISCOVERY);
734         if (aparser.contains("nowebservicediscovery"))
735           webservicediscovery = false;
736         if (webservicediscovery)
737         {
738           desktop.startServiceDiscovery();
739         }
740         else
741         {
742           testoutput(argparser, Arg.WEBSERVICEDISCOVERY);
743         }
744
745         boolean usagestats = !bootstrapArgs.getBoolean(Arg.NOUSAGESTATS);
746         if (aparser.contains("nousagestats"))
747           usagestats = false;
748         if (usagestats)
749         {
750           startUsageStats(desktop);
751           testoutput(argparser, Arg.NOUSAGESTATS);
752         }
753         else
754         {
755           Console.outPrintln("CMD [-nousagestats] executed successfully!");
756           testoutput(argparser, Arg.NOUSAGESTATS);
757         }
758
759         boolean questionnaire = bootstrapArgs.getBoolean(Arg.QUESTIONNAIRE);
760         if (aparser.contains("noquestionnaire"))
761           questionnaire = false;
762         if (questionnaire)
763         {
764           String url = aparser.getValue("questionnaire");
765           if (url != null)
766           {
767             // Start the desktop questionnaire prompter with the specified
768             // questionnaire
769             Console.debug("Starting questionnaire url at " + url);
770             desktop.checkForQuestionnaire(url);
771             Console.outPrintln("CMD questionnaire[-" + url
772                     + "] executed successfully!");
773           }
774           else
775           {
776             if (Cache.getProperty("NOQUESTIONNAIRES") == null)
777             {
778               // Start the desktop questionnaire prompter with the specified
779               // questionnaire
780               // String defurl =
781               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
782               // //
783               String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
784               Console.debug(
785                       "Starting questionnaire with default url: " + defurl);
786               desktop.checkForQuestionnaire(defurl);
787             }
788           }
789         }
790         else
791         {
792           Console.outPrintln(
793                   "CMD [-noquestionnaire] executed successfully!");
794           testoutput(argparser, Arg.QUESTIONNAIRE);
795         }
796
797         if ((!aparser.contains("nonews")
798                 && Cache.getProperty("NONEWS") == null
799                 && !"false".equals(bootstrapArgs.getValue(Arg.NEWS)))
800                 || "true".equals(bootstrapArgs.getValue(Arg.NEWS)))
801         {
802           desktop.checkForNews();
803         }
804
805         if (!aparser.contains("nohtmltemplates")
806                 && Cache.getProperty("NOHTMLTEMPLATES") == null)
807         {
808           BioJsHTMLOutput.updateBioJS();
809         }
810       }
811     }
812     // Run Commands from cli
813     cmds = new Commands(argparser, headlessArg);
814     boolean commandsSuccess = cmds.argsWereParsed();
815
816     if (commandsSuccess)
817     {
818       if (headlessArg)
819       {
820         if (argparser.getBoolean(Arg.NOQUIT))
821         {
822           Console.warn(
823                   "Completed " + Arg.HEADLESS.getName() + " commands, but "
824                           + Arg.NOQUIT + " is set so not quitting!");
825         }
826         else
827         {
828           Jalview.exit("Successfully completed commands in headless mode",
829                   0);
830         }
831       }
832       Console.info("Successfully completed commands");
833     }
834     else
835     {
836       if (headlessArg)
837       {
838         Jalview.exit("Error when running Commands in headless mode", 1);
839       }
840       Console.warn("Error when running commands");
841     }
842
843     // Check if JVM and compile version might cause problems and log if it
844     // might.
845     if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
846     {
847       Console.warn("The Java version being used (Java "
848               + LaunchUtils.getJavaVersion()
849               + ") may lead to problems. This installation of Jalview should be used with Java "
850               + LaunchUtils.getJavaCompileVersion() + ".");
851     }
852
853     String file = null, data = null;
854
855     FileFormatI format = null;
856
857     DataSourceType protocol = null;
858
859     FileLoader fileLoader = new FileLoader(!headless);
860
861     String groovyscript = null; // script to execute after all loading is
862     // completed one way or another
863     // extract groovy argument and execute if necessary
864     groovyscript = aparser.getValue("groovy", true);
865     file = aparser.getValue("open", true);
866
867     if (file == null && desktop == null && !commandsSuccess)
868     {
869       Jalview.exit("No files to open!", 1);
870     }
871
872     long progress = -1;
873     // Finally, deal with the remaining input data.
874     if (file != null)
875     {
876       if (!headless)
877       {
878         desktop.setProgressBar(
879                 MessageManager
880                         .getString("status.processing_commandline_args"),
881                 progress = System.currentTimeMillis());
882       }
883       Console.outPrintln("CMD [-open " + file + "] executed successfully!");
884
885       if (!Platform.isJS())
886       /**
887        * ignore in JavaScript -- can't just file existence - could load it?
888        * 
889        * @j2sIgnore
890        */
891       {
892         if (!HttpUtils.startsWithHttpOrHttps(file))
893         {
894           if (!(new File(file)).exists())
895           {
896             if (headless)
897             {
898               Jalview.exit(
899                       "Can't find file '" + file + "' in headless mode", 1);
900             }
901             Console.warn("Can't find file'" + file + "'");
902           }
903         }
904       }
905
906       protocol = AppletFormatAdapter.checkProtocol(file);
907
908       try
909       {
910         format = new IdentifyFile().identify(file, protocol);
911       } catch (FileFormatException e1)
912       {
913         // TODO ?
914       }
915
916       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
917               format);
918       if (af == null)
919       {
920         Console.outPrintln("error");
921       }
922       else
923       {
924         setCurrentAlignFrame(af);
925         data = aparser.getValue("colour", true);
926         if (data != null)
927         {
928           data.replaceAll("%20", " ");
929
930           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
931                   af.getViewport(), af.getViewport().getAlignment(), data);
932
933           if (cs != null)
934           {
935             Console.outPrintln(
936                     "CMD [-colour " + data + "] executed successfully!");
937           }
938           af.changeColour(cs);
939         }
940
941         // Must maintain ability to use the groups flag
942         data = aparser.getValue("groups", true);
943         if (data != null)
944         {
945           af.parseFeaturesFile(data,
946                   AppletFormatAdapter.checkProtocol(data));
947           // Console.outPrintln("Added " + data);
948           Console.outPrintln(
949                   "CMD groups[-" + data + "]  executed successfully!");
950         }
951         data = aparser.getValue("features", true);
952         if (data != null)
953         {
954           af.parseFeaturesFile(data,
955                   AppletFormatAdapter.checkProtocol(data));
956           // Console.outPrintln("Added " + data);
957           Console.outPrintln(
958                   "CMD [-features " + data + "]  executed successfully!");
959         }
960
961         data = aparser.getValue("annotations", true);
962         if (data != null)
963         {
964           af.loadJalviewDataFile(data, null, null, null);
965           // Console.outPrintln("Added " + data);
966           Console.outPrintln(
967                   "CMD [-annotations " + data + "] executed successfully!");
968         }
969         // set or clear the sortbytree flag.
970         if (aparser.contains("sortbytree"))
971         {
972           af.getViewport().setSortByTree(true);
973           if (af.getViewport().getSortByTree())
974           {
975             Console.outPrintln("CMD [-sortbytree] executed successfully!");
976           }
977         }
978         if (aparser.contains("no-annotation"))
979         {
980           af.getViewport().setShowAnnotation(false);
981           if (!af.getViewport().isShowAnnotation())
982           {
983             Console.outPrintln("CMD no-annotation executed successfully!");
984           }
985         }
986         if (aparser.contains("nosortbytree"))
987         {
988           af.getViewport().setSortByTree(false);
989           if (!af.getViewport().getSortByTree())
990           {
991             Console.outPrintln(
992                     "CMD [-nosortbytree] executed successfully!");
993           }
994         }
995         data = aparser.getValue("tree", true);
996         if (data != null)
997         {
998           try
999           {
1000             Console.outPrintln(
1001                     "CMD [-tree " + data + "] executed successfully!");
1002             NewickFile nf = new NewickFile(data,
1003                     AppletFormatAdapter.checkProtocol(data));
1004             af.getViewport()
1005                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
1006           } catch (IOException ex)
1007           {
1008             jalview.bin.Console.errPrintln("Couldn't add tree " + data);
1009             ex.printStackTrace(System.err);
1010           }
1011         }
1012
1013         if (groovyscript != null)
1014         {
1015           // Execute the groovy script after we've done all the rendering stuff
1016           // and before any images or figures are generated.
1017           Console.outPrintln("Executing script " + groovyscript);
1018           executeGroovyScript(groovyscript, af);
1019           Console.outPrintln("CMD groovy[" + groovyscript
1020                   + "] executed successfully!");
1021           groovyscript = null;
1022         }
1023         String imageName = "unnamed.png";
1024         while (aparser.getSize() > 1)
1025         {
1026           try
1027           {
1028             String outputFormat = aparser.nextValue();
1029             file = aparser.nextValue();
1030
1031             if (outputFormat.equalsIgnoreCase("png"))
1032             {
1033               Console.outPrintln("Creating PNG image: " + file);
1034               af.createPNG(new File(file));
1035               imageName = (new File(file)).getName();
1036               continue;
1037             }
1038             else if (outputFormat.equalsIgnoreCase("svg"))
1039             {
1040               Console.outPrintln("Creating SVG image: " + file);
1041               File imageFile = new File(file);
1042               imageName = imageFile.getName();
1043               af.createSVG(imageFile);
1044               continue;
1045             }
1046             else if (outputFormat.equalsIgnoreCase("html"))
1047             {
1048               File imageFile = new File(file);
1049               imageName = imageFile.getName();
1050               HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
1051
1052               Console.outPrintln("Creating HTML image: " + file);
1053               htmlSVG.exportHTML(file);
1054               continue;
1055             }
1056             else if (outputFormat.equalsIgnoreCase("biojsmsa"))
1057             {
1058               if (file == null)
1059               {
1060                 jalview.bin.Console.errPrintln(
1061                         "The output html file must not be null");
1062                 return;
1063               }
1064               try
1065               {
1066                 BioJsHTMLOutput.refreshVersionInfo(
1067                         BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1068               } catch (URISyntaxException e)
1069               {
1070                 e.printStackTrace();
1071               }
1072               BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1073               Console.outPrintln(
1074                       "Creating BioJS MSA Viwer HTML file: " + file);
1075               bjs.exportHTML(file);
1076               continue;
1077             }
1078             else if (outputFormat.equalsIgnoreCase("imgMap"))
1079             {
1080               Console.outPrintln("Creating image map: " + file);
1081               af.createImageMap(new File(file), imageName);
1082               continue;
1083             }
1084             else if (outputFormat.equalsIgnoreCase("eps"))
1085             {
1086               File outputFile = new File(file);
1087               Console.outPrintln(
1088                       "Creating EPS file: " + outputFile.getAbsolutePath());
1089               af.createEPS(outputFile);
1090               continue;
1091             }
1092
1093             FileFormatI outFormat = null;
1094             try
1095             {
1096               outFormat = FileFormats.getInstance().forName(outputFormat);
1097             } catch (Exception formatP)
1098             {
1099               Console.outPrintln("Couldn't parse " + outFormat
1100                       + " as a valid Jalview format string.");
1101             }
1102             if (outFormat != null)
1103             {
1104               if (!outFormat.isWritable())
1105               {
1106                 Console.outPrintln(
1107                         "This version of Jalview does not support alignment export as "
1108                                 + outputFormat);
1109               }
1110               else
1111               {
1112                 af.saveAlignment(file, outFormat);
1113                 if (af.isSaveAlignmentSuccessful())
1114                 {
1115                   Console.outPrintln("Written alignment in "
1116                           + outFormat.getName() + " format to " + file);
1117                 }
1118                 else
1119                 {
1120                   Console.outPrintln("Error writing file " + file + " in "
1121                           + outFormat.getName() + " format!!");
1122                 }
1123               }
1124             }
1125           } catch (ImageOutputException ioexc)
1126           {
1127             Console.outPrintln(
1128                     "Unexpected error whilst exporting image to " + file);
1129             ioexc.printStackTrace();
1130           }
1131
1132         }
1133
1134         while (aparser.getSize() > 0)
1135         {
1136           Console.outPrintln("Unknown arg: " + aparser.nextValue());
1137         }
1138       }
1139     }
1140
1141     AlignFrame startUpAlframe = null;
1142     // We'll only open the default file if the desktop is visible.
1143     // And the user
1144     // ////////////////////
1145
1146     if (!Platform.isJS() && !headless && file == null
1147             && Cache.getDefault("SHOW_STARTUP_FILE", true)
1148             && !cmds.commandArgsProvided()
1149             && !bootstrapArgs.getBoolean(Arg.NOSTARTUPFILE))
1150     // don't open the startup file if command line args have been processed
1151     // (&& !Commands.commandArgsProvided())
1152     /**
1153      * Java only
1154      * 
1155      * @j2sIgnore
1156      */
1157     {
1158       file = Cache.getDefault("STARTUP_FILE",
1159               Cache.getDefault("www.jalview.org", "https://www.jalview.org")
1160                       + "/examples/exampleFile_2_7.jvp");
1161       if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
1162               || file.equals(
1163                       "http://www.jalview.org/examples/exampleFile_2_7.jar"))
1164       {
1165         file.replace("http:", "https:");
1166         // hardwire upgrade of the startup file
1167         file.replace("_2_3", "_2_7");
1168         file.replace("2_7.jar", "2_7.jvp");
1169         // and remove the stale setting
1170         Cache.removeProperty("STARTUP_FILE");
1171       }
1172
1173       protocol = AppletFormatAdapter.checkProtocol(file);
1174
1175       if (file.endsWith(".jar"))
1176       {
1177         format = FileFormat.Jalview;
1178       }
1179       else
1180       {
1181         try
1182         {
1183           format = new IdentifyFile().identify(file, protocol);
1184         } catch (FileFormatException e)
1185         {
1186           // TODO what?
1187         }
1188       }
1189
1190       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
1191               format);
1192       // don't ask to save when quitting if only the startup file has been
1193       // opened
1194       Console.debug("Resetting up-to-date flag for startup file");
1195       startUpAlframe.getViewport().setSavedUpToDate(true);
1196       // extract groovy arguments before anything else.
1197     }
1198
1199     // Once all other stuff is done, execute any groovy scripts (in order)
1200     if (groovyscript != null)
1201     {
1202       if (Cache.groovyJarsPresent())
1203       {
1204         Console.outPrintln("Executing script " + groovyscript);
1205         executeGroovyScript(groovyscript, startUpAlframe);
1206       }
1207       else
1208       {
1209         jalview.bin.Console.errPrintln(
1210                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
1211                         + groovyscript);
1212       }
1213     }
1214     // and finally, turn off batch mode indicator - if the desktop still exists
1215     if (desktop != null)
1216     {
1217       if (progress != -1)
1218       {
1219         desktop.setProgressBar(null, progress);
1220       }
1221       desktop.setInBatchMode(false);
1222     }
1223   }
1224
1225   private static void setLookAndFeel()
1226   {
1227     if (!Platform.isJS())
1228     /**
1229      * Java only
1230      * 
1231      * @j2sIgnore
1232      */
1233     {
1234       // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
1235       // "mac" or "flat"
1236       // If not set (or chosen laf fails), use the normal SystemLaF and if on
1237       // Mac,
1238       // try Quaqua/Vaqua.
1239       String lafProp = System.getProperty("laf");
1240       String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
1241       String laf = "none";
1242       if (lafProp != null)
1243       {
1244         laf = lafProp;
1245       }
1246       else if (lafSetting != null)
1247       {
1248         laf = lafSetting;
1249       }
1250       boolean lafSet = false;
1251       switch (laf)
1252       {
1253       case "crossplatform":
1254         lafSet = setCrossPlatformLookAndFeel();
1255         if (!lafSet)
1256         {
1257           Console.error("Could not set requested laf=" + laf);
1258         }
1259         break;
1260       case "system":
1261         lafSet = setSystemLookAndFeel();
1262         if (!lafSet)
1263         {
1264           Console.error("Could not set requested laf=" + laf);
1265         }
1266         break;
1267       case "gtk":
1268         lafSet = setGtkLookAndFeel();
1269         if (!lafSet)
1270         {
1271           Console.error("Could not set requested laf=" + laf);
1272         }
1273         break;
1274       case "metal":
1275         lafSet = setMetalLookAndFeel();
1276         if (!lafSet)
1277         {
1278           Console.error("Could not set requested laf=" + laf);
1279         }
1280         break;
1281       case "nimbus":
1282         lafSet = setNimbusLookAndFeel();
1283         if (!lafSet)
1284         {
1285           Console.error("Could not set requested laf=" + laf);
1286         }
1287         break;
1288       case "flat":
1289         lafSet = setFlatLookAndFeel();
1290         if (!lafSet)
1291         {
1292           Console.error("Could not set requested laf=" + laf);
1293         }
1294         break;
1295       case "mac":
1296         lafSet = setMacLookAndFeel();
1297         if (!lafSet)
1298         {
1299           Console.error("Could not set requested laf=" + laf);
1300         }
1301         break;
1302       case "none":
1303         break;
1304       default:
1305         Console.error("Requested laf=" + laf + " not implemented");
1306       }
1307       if (!lafSet)
1308       {
1309         // Flatlaf default for everyone!
1310         lafSet = setFlatLookAndFeel();
1311         if (!lafSet)
1312         {
1313           setSystemLookAndFeel();
1314         }
1315         if (Platform.isLinux())
1316         {
1317           setLinuxLookAndFeel();
1318         }
1319         if (Platform.isMac())
1320         {
1321           setMacLookAndFeel();
1322         }
1323       }
1324     }
1325   }
1326
1327   private static boolean setCrossPlatformLookAndFeel()
1328   {
1329     boolean set = false;
1330     try
1331     {
1332       UIManager.setLookAndFeel(
1333               UIManager.getCrossPlatformLookAndFeelClassName());
1334       set = true;
1335     } catch (Exception ex)
1336     {
1337       Console.error("Unexpected Look and Feel Exception");
1338       Console.error(ex.getMessage());
1339       Console.debug(Cache.getStackTraceString(ex));
1340     }
1341     return set;
1342   }
1343
1344   private static boolean setSystemLookAndFeel()
1345   {
1346     boolean set = false;
1347     try
1348     {
1349       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
1350       set = true;
1351     } catch (Exception ex)
1352     {
1353       Console.error("Unexpected Look and Feel Exception");
1354       Console.error(ex.getMessage());
1355       Console.debug(Cache.getStackTraceString(ex));
1356     }
1357     return set;
1358   }
1359
1360   private static boolean setSpecificLookAndFeel(String name,
1361           String className, boolean nameStartsWith)
1362   {
1363     boolean set = false;
1364     try
1365     {
1366       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
1367       {
1368         if (info.getName() != null && nameStartsWith
1369                 ? info.getName().toLowerCase(Locale.ROOT)
1370                         .startsWith(name.toLowerCase(Locale.ROOT))
1371                 : info.getName().toLowerCase(Locale.ROOT)
1372                         .equals(name.toLowerCase(Locale.ROOT)))
1373         {
1374           className = info.getClassName();
1375           break;
1376         }
1377       }
1378       UIManager.setLookAndFeel(className);
1379       set = true;
1380     } catch (Exception ex)
1381     {
1382       Console.error("Unexpected Look and Feel Exception");
1383       Console.error(ex.getMessage());
1384       Console.debug(Cache.getStackTraceString(ex));
1385     }
1386     return set;
1387   }
1388
1389   private static boolean setGtkLookAndFeel()
1390   {
1391     return setSpecificLookAndFeel("gtk",
1392             "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1393   }
1394
1395   private static boolean setMetalLookAndFeel()
1396   {
1397     return setSpecificLookAndFeel("metal",
1398             "javax.swing.plaf.metal.MetalLookAndFeel", false);
1399   }
1400
1401   private static boolean setNimbusLookAndFeel()
1402   {
1403     return setSpecificLookAndFeel("nimbus",
1404             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1405   }
1406
1407   private static boolean setFlatLookAndFeel()
1408   {
1409     boolean set = false;
1410     if (SystemInfo.isMacOS)
1411     {
1412       try
1413       {
1414         UIManager.setLookAndFeel(
1415                 "com.formdev.flatlaf.themes.FlatMacLightLaf");
1416         set = true;
1417         Console.debug("Using FlatMacLightLaf");
1418       } catch (ClassNotFoundException | InstantiationException
1419               | IllegalAccessException | UnsupportedLookAndFeelException e)
1420       {
1421         Console.debug("Exception loading FlatLightLaf", e);
1422       }
1423       System.setProperty("apple.laf.useScreenMenuBar", "true");
1424       System.setProperty("apple.awt.application.name",
1425               ChannelProperties.getProperty("app_name"));
1426       System.setProperty("apple.awt.application.appearance", "system");
1427       if (SystemInfo.isMacFullWindowContentSupported
1428               && Desktop.desktop != null)
1429       {
1430         Console.debug("Setting transparent title bar");
1431         Desktop.desktop.getRootPane()
1432                 .putClientProperty("apple.awt.fullWindowContent", true);
1433         Desktop.desktop.getRootPane()
1434                 .putClientProperty("apple.awt.transparentTitleBar", true);
1435         Desktop.desktop.getRootPane()
1436                 .putClientProperty("apple.awt.fullscreenable", true);
1437       }
1438       SwingUtilities.invokeLater(() -> {
1439         FlatMacLightLaf.setup();
1440       });
1441       Console.debug("Using FlatMacLightLaf");
1442       set = true;
1443     }
1444     if (!set)
1445     {
1446       try
1447       {
1448         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1449         set = true;
1450         Console.debug("Using FlatLightLaf");
1451       } catch (ClassNotFoundException | InstantiationException
1452               | IllegalAccessException | UnsupportedLookAndFeelException e)
1453       {
1454         Console.debug("Exception loading FlatLightLaf", e);
1455       }
1456       // Windows specific properties here
1457       SwingUtilities.invokeLater(() -> {
1458         FlatLightLaf.setup();
1459       });
1460       Console.debug("Using FlatLightLaf");
1461       set = true;
1462     }
1463     else if (SystemInfo.isLinux)
1464     {
1465       try
1466       {
1467         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1468         set = true;
1469         Console.debug("Using FlatLightLaf");
1470       } catch (ClassNotFoundException | InstantiationException
1471               | IllegalAccessException | UnsupportedLookAndFeelException e)
1472       {
1473         Console.debug("Exception loading FlatLightLaf", e);
1474       }
1475       // enable custom window decorations
1476       JFrame.setDefaultLookAndFeelDecorated(true);
1477       JDialog.setDefaultLookAndFeelDecorated(true);
1478       SwingUtilities.invokeLater(() -> {
1479         FlatLightLaf.setup();
1480       });
1481       Console.debug("Using FlatLightLaf");
1482       set = true;
1483     }
1484
1485     if (!set)
1486     {
1487       try
1488       {
1489         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1490         set = true;
1491         Console.debug("Using FlatLightLaf");
1492       } catch (ClassNotFoundException | InstantiationException
1493               | IllegalAccessException | UnsupportedLookAndFeelException e)
1494       {
1495         Console.debug("Exception loading FlatLightLaf", e);
1496       }
1497     }
1498
1499     if (set)
1500     {
1501       UIManager.put("TabbedPane.tabType", "card");
1502       UIManager.put("TabbedPane.showTabSeparators", true);
1503       UIManager.put("TabbedPane.showContentSeparator", true);
1504       // UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
1505       UIManager.put("TabbedPane.tabsOverlapBorder", true);
1506       UIManager.put("TabbedPane.hasFullBorder", true);
1507       UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
1508       UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
1509       UIManager.put("TabbedPane.smoothScrolling", true);
1510       UIManager.put("TabbedPane.tabWidthMode", "compact");
1511       UIManager.put("TabbedPane.selectedBackground", Color.white);
1512       UIManager.put("TabbedPane.background", new Color(236, 236, 236));
1513       UIManager.put("TabbedPane.hoverColor", Color.lightGray);
1514     }
1515
1516     Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
1517     return set;
1518   }
1519
1520   private static boolean setMacLookAndFeel()
1521   {
1522     boolean set = false;
1523     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1524             ChannelProperties.getProperty("app_name"));
1525     System.setProperty("apple.laf.useScreenMenuBar", "true");
1526     /*
1527      * broken native LAFs on (ARM?) macbooks
1528     set = setQuaquaLookAndFeel();
1529     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1530             .toLowerCase(Locale.ROOT).contains("quaqua"))
1531     {
1532       set = setVaquaLookAndFeel();
1533     }
1534      */
1535     set = setFlatLookAndFeel();
1536     return set;
1537   }
1538
1539   private static boolean setLinuxLookAndFeel()
1540   {
1541     boolean set = false;
1542     set = setFlatLookAndFeel();
1543     if (!set)
1544       set = setMetalLookAndFeel();
1545     // avoid GtkLookAndFeel -- not good results especially on HiDPI
1546     if (!set)
1547       set = setNimbusLookAndFeel();
1548     return set;
1549   }
1550
1551   /*
1552   private static void showUsage()
1553   {
1554     jalview.bin.Console.outPrintln(
1555             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1556                     + "-nodisplay\tRun Jalview without User Interface.\n"
1557                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1558                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1559                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1560                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1561                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1562                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1563                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1564                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1565                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1566                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1567                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1568                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1569                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1570                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1571                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1572                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1573                     + "-html FILE\tCreate HTML file from alignment.\n"
1574                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1575                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1576                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1577                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1578                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1579                     + "-nonews\tTurn off check for Jalview news.\n"
1580                     + "-nousagestats\tTurn off analytics tracking for this session.\n"
1581                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1582                     // +
1583                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1584                     // after all other properties files have been read\n\t
1585                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1586                     // passed in correctly)"
1587                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1588                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1589                     + "-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"
1590                     + "-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"
1591                     + "-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"
1592                     + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
1593   }
1594   */
1595
1596   private static void startUsageStats(final Desktop desktop)
1597   {
1598     /**
1599      * start a User Config prompt asking if we can log usage statistics.
1600      */
1601     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
1602             "USAGESTATS",
1603             MessageManager.getString("prompt.plausible_analytics_title"),
1604             MessageManager.getString("prompt.plausible_analytics"),
1605             new Runnable()
1606             {
1607               @Override
1608               public void run()
1609               {
1610                 Console.debug("Initialising analytics for usage stats.");
1611                 Cache.initAnalytics();
1612                 Console.debug("Tracking enabled.");
1613               }
1614             }, new Runnable()
1615             {
1616               @Override
1617               public void run()
1618               {
1619                 Console.debug("Not enabling analytics.");
1620               }
1621             }, null, true);
1622     desktop.addDialogThread(prompter);
1623   }
1624
1625   /**
1626    * Locate the given string as a file and pass it to the groovy interpreter.
1627    * 
1628    * @param groovyscript
1629    *          the script to execute
1630    * @param jalviewContext
1631    *          the Jalview Desktop object passed in to the groovy binding as the
1632    *          'Jalview' object.
1633    */
1634   protected void executeGroovyScript(String groovyscript, AlignFrame af)
1635   {
1636     /**
1637      * for scripts contained in files
1638      */
1639     File tfile = null;
1640     /**
1641      * script's URI
1642      */
1643     URL sfile = null;
1644     if (groovyscript.trim().equals("STDIN"))
1645     {
1646       // read from stdin into a tempfile and execute it
1647       try
1648       {
1649         tfile = File.createTempFile("jalview", "groovy");
1650         PrintWriter outfile = new PrintWriter(
1651                 new OutputStreamWriter(new FileOutputStream(tfile)));
1652         BufferedReader br = new BufferedReader(
1653                 new InputStreamReader(System.in));
1654         String line = null;
1655         while ((line = br.readLine()) != null)
1656         {
1657           outfile.write(line + "\n");
1658         }
1659         br.close();
1660         outfile.flush();
1661         outfile.close();
1662
1663       } catch (Exception ex)
1664       {
1665         jalview.bin.Console
1666                 .errPrintln("Failed to read from STDIN into tempfile "
1667                         + ((tfile == null) ? "(tempfile wasn't created)"
1668                                 : tfile.toString()));
1669         ex.printStackTrace();
1670         return;
1671       }
1672       try
1673       {
1674         sfile = tfile.toURI().toURL();
1675       } catch (Exception x)
1676       {
1677         jalview.bin.Console.errPrintln(
1678                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1679                         + tfile.toURI());
1680         x.printStackTrace();
1681         return;
1682       }
1683     }
1684     else
1685     {
1686       try
1687       {
1688         sfile = new URI(groovyscript).toURL();
1689       } catch (Exception x)
1690       {
1691         tfile = new File(groovyscript);
1692         if (!tfile.exists())
1693         {
1694           jalview.bin.Console.errPrintln(
1695                   "File '" + groovyscript + "' does not exist.");
1696           return;
1697         }
1698         if (!tfile.canRead())
1699         {
1700           jalview.bin.Console.errPrintln(
1701                   "File '" + groovyscript + "' cannot be read.");
1702           return;
1703         }
1704         if (tfile.length() < 1)
1705         {
1706           jalview.bin.Console
1707                   .errPrintln("File '" + groovyscript + "' is empty.");
1708           return;
1709         }
1710         try
1711         {
1712           sfile = tfile.getAbsoluteFile().toURI().toURL();
1713         } catch (Exception ex)
1714         {
1715           jalview.bin.Console.errPrintln("Failed to create a file URL for "
1716                   + tfile.getAbsoluteFile());
1717           return;
1718         }
1719       }
1720     }
1721     try
1722     {
1723       Map<String, java.lang.Object> vbinding = new HashMap<>();
1724       vbinding.put("Jalview", this);
1725       if (af != null)
1726       {
1727         vbinding.put("currentAlFrame", af);
1728       }
1729       Binding gbinding = new Binding(vbinding);
1730       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1731       gse.run(sfile.toString(), gbinding);
1732       if ("STDIN".equals(groovyscript))
1733       {
1734         // delete temp file that we made -
1735         // only if it was successfully executed
1736         tfile.delete();
1737       }
1738     } catch (Exception e)
1739     {
1740       jalview.bin.Console
1741               .errPrintln("Exception Whilst trying to execute file " + sfile
1742                       + " as a groovy script.");
1743       e.printStackTrace(System.err);
1744
1745     }
1746   }
1747
1748   public static boolean isHeadlessMode()
1749   {
1750     String isheadless = System.getProperty("java.awt.headless");
1751     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1752     {
1753       return true;
1754     }
1755     return false;
1756   }
1757
1758   public AlignFrame[] getAlignFrames()
1759   {
1760     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1761             : Desktop.getAlignFrames();
1762
1763   }
1764
1765   /**
1766    * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
1767    */
1768   public void quit()
1769   {
1770     // System.exit will run the shutdownHook first
1771     Jalview.exit("Quitting now. Bye!", 0);
1772   }
1773
1774   public static AlignFrame getCurrentAlignFrame()
1775   {
1776     return Jalview.currentAlignFrame;
1777   }
1778
1779   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1780   {
1781     Jalview.currentAlignFrame = currentAlignFrame;
1782   }
1783
1784   protected Commands getCommands()
1785   {
1786     return cmds;
1787   }
1788
1789   public static void exit(String message, int exitcode)
1790   {
1791     if (Console.log == null)
1792     {
1793       // Don't start the logger just to exit!
1794       if (message != null)
1795       {
1796         if (exitcode == 0)
1797         {
1798           Console.outPrintln(message);
1799         }
1800         else
1801         {
1802           jalview.bin.Console.errPrintln(message);
1803         }
1804       }
1805     }
1806     else
1807     {
1808       Console.debug("Using Jalview.exit");
1809       if (message != null)
1810       {
1811         if (exitcode == 0)
1812         {
1813           Console.info(message);
1814         }
1815         else
1816         {
1817           Console.error(message);
1818         }
1819       }
1820     }
1821     if (exitcode > -1)
1822     {
1823       System.exit(exitcode);
1824     }
1825   }
1826
1827   /******************************
1828    * 
1829    * TEST OUTPUT METHODS
1830    * 
1831    ******************************/
1832   /**
1833    * method for reporting string values parsed/processed during tests
1834    * 
1835    */
1836   protected static void testoutput(ArgParser ap, Arg a, String s1,
1837           String s2)
1838   {
1839     BootstrapArgs bsa = ap.getBootstrapArgs();
1840     if (!bsa.getBoolean(Arg.TESTOUTPUT))
1841       return;
1842     if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
1843     {
1844       Console.debug("testoutput with unmatching values '" + s1 + "' and '"
1845               + s2 + "' for arg " + a.argString());
1846       return;
1847     }
1848     boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
1849             : ap.isSet(a);
1850     if (!isset)
1851     {
1852       Console.warn("Arg '" + a.getName() + "' not set at all");
1853       return;
1854     }
1855     testoutput(true, a, s1, s2);
1856   }
1857
1858   /**
1859    * method for reporting string values parsed/processed during tests
1860    */
1861
1862   protected static void testoutput(BootstrapArgs bsa, Arg a, String s1,
1863           String s2)
1864   {
1865     if (!bsa.getBoolean(Arg.TESTOUTPUT))
1866       return;
1867     if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
1868     {
1869       Console.debug("testoutput with unmatching values '" + s1 + "' and '"
1870               + s2 + "' for arg " + a.argString());
1871       return;
1872     }
1873     if (!a.hasOption(Opt.BOOTSTRAP))
1874     {
1875       Console.error("Non-bootstrap Arg '" + a.getName()
1876               + "' given to testoutput(BootstrapArgs bsa, Arg a, String s1, String s2) with only BootstrapArgs");
1877     }
1878     if (!bsa.contains(a))
1879     {
1880       Console.warn("Arg '" + a.getName() + "' not set at all");
1881       return;
1882     }
1883     testoutput(true, a, s1, s2);
1884   }
1885
1886   /**
1887    * report value set for string values parsed/processed during tests
1888    */
1889   private static void testoutput(boolean yes, Arg a, String s1, String s2)
1890   {
1891     if (yes && ((s1 == null && s2 == null)
1892             || (s1 != null && s1.equals(s2))))
1893     {
1894       Console.outPrintln("[TESTOUTPUT] arg " + a.argString() + "='" + s1
1895               + "' was set");
1896     }
1897   }
1898
1899   /*
1900    * testoutput for boolean and unary values
1901    */
1902   protected static void testoutput(ArgParser ap, Arg a)
1903   {
1904     if (ap == null)
1905       return;
1906     BootstrapArgs bsa = ap.getBootstrapArgs();
1907     if (bsa == null)
1908       return;
1909     if (!bsa.getBoolean(Arg.TESTOUTPUT))
1910       return;
1911     boolean val = a.hasOption(Opt.BOOTSTRAP) ? bsa.getBoolean(a)
1912             : ap.getBoolean(a);
1913     boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
1914             : ap.isSet(a);
1915     if (!isset)
1916     {
1917       Console.warn("Arg '" + a.getName() + "' not set at all");
1918       return;
1919     }
1920     testoutput(val, a);
1921   }
1922
1923   protected static void testoutput(BootstrapArgs bsa, Arg a)
1924   {
1925     if (!bsa.getBoolean(Arg.TESTOUTPUT))
1926       return;
1927     if (!a.hasOption(Opt.BOOTSTRAP))
1928     {
1929       Console.warn("Non-bootstrap Arg '" + a.getName()
1930               + "' given to testoutput(BootstrapArgs bsa, Arg a) with only BootstrapArgs");
1931
1932     }
1933     if (!bsa.contains(a))
1934     {
1935       Console.warn("Arg '" + a.getName() + "' not set at all");
1936       return;
1937     }
1938     testoutput(bsa.getBoolean(a), a);
1939   }
1940
1941   private static void testoutput(boolean yes, Arg a)
1942   {
1943     String message = null;
1944     if (a.hasOption(Opt.BOOLEAN))
1945     {
1946       message = (yes ? a.argString() : a.negateArgString()) + " was set";
1947     }
1948     else if (a.hasOption(Opt.UNARY))
1949     {
1950       message = a.argString() + (yes ? " was set" : " was not set");
1951     }
1952     Console.outPrintln("[TESTOUTPUT] arg " + message);
1953   }
1954 }