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