57f25752f05c3aa9f6b7837e0a0e0f63bf533476
[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           Jalview.exit("Successfully completed commands in headless mode",
826                   0);
827         }
828       }
829       Console.info("Successfully completed commands");
830     }
831     else
832     {
833       if (headlessArg)
834       {
835         Jalview.exit("Error when running Commands in headless mode", 1);
836       }
837       Console.warn("Error when running commands");
838     }
839
840     // Check if JVM and compile version might cause problems and log if it
841     // might.
842     if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
843     {
844       Console.warn("The Java version being used (Java "
845               + LaunchUtils.getJavaVersion()
846               + ") may lead to problems. This installation of Jalview should be used with Java "
847               + LaunchUtils.getJavaCompileVersion() + ".");
848     }
849
850     String file = null, data = null;
851
852     FileFormatI format = null;
853
854     DataSourceType protocol = null;
855
856     FileLoader fileLoader = new FileLoader(!headless);
857
858     String groovyscript = null; // script to execute after all loading is
859     // completed one way or another
860     // extract groovy argument and execute if necessary
861     groovyscript = aparser.getValue("groovy", true);
862     file = aparser.getValue("open", true);
863
864     if (file == null && desktop == null && !commandsSuccess)
865     {
866       Jalview.exit("No files to open!", 1);
867     }
868
869     long progress = -1;
870     // Finally, deal with the remaining input data.
871     if (file != null)
872     {
873       if (!headless)
874       {
875         desktop.setProgressBar(
876                 MessageManager
877                         .getString("status.processing_commandline_args"),
878                 progress = System.currentTimeMillis());
879       }
880       System.out.println("CMD [-open " + file + "] executed successfully!");
881
882       if (!Platform.isJS())
883       /**
884        * ignore in JavaScript -- can't just file existence - could load it?
885        * 
886        * @j2sIgnore
887        */
888       {
889         if (!HttpUtils.startsWithHttpOrHttps(file))
890         {
891           if (!(new File(file)).exists())
892           {
893             if (headless)
894             {
895               Jalview.exit(
896                       "Can't find file '" + file + "' in headless mode", 1);
897             }
898             Console.warn("Can't find file'" + file + "'");
899           }
900         }
901       }
902
903       protocol = AppletFormatAdapter.checkProtocol(file);
904
905       try
906       {
907         format = new IdentifyFile().identify(file, protocol);
908       } catch (FileFormatException e1)
909       {
910         // TODO ?
911       }
912
913       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
914               format);
915       if (af == null)
916       {
917         System.out.println("error");
918       }
919       else
920       {
921         setCurrentAlignFrame(af);
922         data = aparser.getValue("colour", true);
923         if (data != null)
924         {
925           data.replaceAll("%20", " ");
926
927           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
928                   af.getViewport(), af.getViewport().getAlignment(), data);
929
930           if (cs != null)
931           {
932             System.out.println(
933                     "CMD [-colour " + data + "] executed successfully!");
934           }
935           af.changeColour(cs);
936         }
937
938         // Must maintain ability to use the groups flag
939         data = aparser.getValue("groups", true);
940         if (data != null)
941         {
942           af.parseFeaturesFile(data,
943                   AppletFormatAdapter.checkProtocol(data));
944           // System.out.println("Added " + data);
945           System.out.println(
946                   "CMD groups[-" + data + "]  executed successfully!");
947         }
948         data = aparser.getValue("features", true);
949         if (data != null)
950         {
951           af.parseFeaturesFile(data,
952                   AppletFormatAdapter.checkProtocol(data));
953           // System.out.println("Added " + data);
954           System.out.println(
955                   "CMD [-features " + data + "]  executed successfully!");
956         }
957
958         data = aparser.getValue("annotations", true);
959         if (data != null)
960         {
961           af.loadJalviewDataFile(data, null, null, null);
962           // System.out.println("Added " + data);
963           System.out.println(
964                   "CMD [-annotations " + data + "] executed successfully!");
965         }
966         // set or clear the sortbytree flag.
967         if (aparser.contains("sortbytree"))
968         {
969           af.getViewport().setSortByTree(true);
970           if (af.getViewport().getSortByTree())
971           {
972             System.out.println("CMD [-sortbytree] executed successfully!");
973           }
974         }
975         if (aparser.contains("no-annotation"))
976         {
977           af.getViewport().setShowAnnotation(false);
978           if (!af.getViewport().isShowAnnotation())
979           {
980             System.out.println("CMD no-annotation executed successfully!");
981           }
982         }
983         if (aparser.contains("nosortbytree"))
984         {
985           af.getViewport().setSortByTree(false);
986           if (!af.getViewport().getSortByTree())
987           {
988             System.out
989                     .println("CMD [-nosortbytree] executed successfully!");
990           }
991         }
992         data = aparser.getValue("tree", true);
993         if (data != null)
994         {
995           try
996           {
997             System.out.println(
998                     "CMD [-tree " + data + "] executed successfully!");
999             NewickFile nf = new NewickFile(data,
1000                     AppletFormatAdapter.checkProtocol(data));
1001             af.getViewport()
1002                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
1003           } catch (IOException ex)
1004           {
1005             System.err.println("Couldn't add tree " + data);
1006             ex.printStackTrace(System.err);
1007           }
1008         }
1009
1010         if (groovyscript != null)
1011         {
1012           // Execute the groovy script after we've done all the rendering stuff
1013           // and before any images or figures are generated.
1014           System.out.println("Executing script " + groovyscript);
1015           executeGroovyScript(groovyscript, af);
1016           System.out.println("CMD groovy[" + groovyscript
1017                   + "] executed successfully!");
1018           groovyscript = null;
1019         }
1020         String imageName = "unnamed.png";
1021         while (aparser.getSize() > 1)
1022         {
1023           try
1024           {
1025             String outputFormat = aparser.nextValue();
1026             file = aparser.nextValue();
1027
1028             if (outputFormat.equalsIgnoreCase("png"))
1029             {
1030               System.out.println("Creating PNG image: " + file);
1031               af.createPNG(new File(file));
1032               imageName = (new File(file)).getName();
1033               continue;
1034             }
1035             else if (outputFormat.equalsIgnoreCase("svg"))
1036             {
1037               System.out.println("Creating SVG image: " + file);
1038               File imageFile = new File(file);
1039               imageName = imageFile.getName();
1040               af.createSVG(imageFile);
1041               continue;
1042             }
1043             else if (outputFormat.equalsIgnoreCase("html"))
1044             {
1045               File imageFile = new File(file);
1046               imageName = imageFile.getName();
1047               HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
1048
1049               System.out.println("Creating HTML image: " + file);
1050               htmlSVG.exportHTML(file);
1051               continue;
1052             }
1053             else if (outputFormat.equalsIgnoreCase("biojsmsa"))
1054             {
1055               if (file == null)
1056               {
1057                 System.err.println("The output html file must not be null");
1058                 return;
1059               }
1060               try
1061               {
1062                 BioJsHTMLOutput.refreshVersionInfo(
1063                         BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
1064               } catch (URISyntaxException e)
1065               {
1066                 e.printStackTrace();
1067               }
1068               BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
1069               System.out.println(
1070                       "Creating BioJS MSA Viwer HTML file: " + file);
1071               bjs.exportHTML(file);
1072               continue;
1073             }
1074             else if (outputFormat.equalsIgnoreCase("imgMap"))
1075             {
1076               System.out.println("Creating image map: " + file);
1077               af.createImageMap(new File(file), imageName);
1078               continue;
1079             }
1080             else if (outputFormat.equalsIgnoreCase("eps"))
1081             {
1082               File outputFile = new File(file);
1083               System.out.println(
1084                       "Creating EPS file: " + outputFile.getAbsolutePath());
1085               af.createEPS(outputFile);
1086               continue;
1087             }
1088
1089             FileFormatI outFormat = null;
1090             try
1091             {
1092               outFormat = FileFormats.getInstance().forName(outputFormat);
1093             } catch (Exception formatP)
1094             {
1095               System.out.println("Couldn't parse " + outFormat
1096                       + " as a valid Jalview format string.");
1097             }
1098             if (outFormat != null)
1099             {
1100               if (!outFormat.isWritable())
1101               {
1102                 System.out.println(
1103                         "This version of Jalview does not support alignment export as "
1104                                 + outputFormat);
1105               }
1106               else
1107               {
1108                 af.saveAlignment(file, outFormat);
1109                 if (af.isSaveAlignmentSuccessful())
1110                 {
1111                   System.out.println("Written alignment in "
1112                           + outFormat.getName() + " format to " + file);
1113                 }
1114                 else
1115                 {
1116                   System.out.println("Error writing file " + file + " in "
1117                           + outFormat.getName() + " format!!");
1118                 }
1119               }
1120             }
1121           } catch (ImageOutputException ioexc)
1122           {
1123             System.out.println(
1124                     "Unexpected error whilst exporting image to " + file);
1125             ioexc.printStackTrace();
1126           }
1127
1128         }
1129
1130         while (aparser.getSize() > 0)
1131         {
1132           System.out.println("Unknown arg: " + aparser.nextValue());
1133         }
1134       }
1135     }
1136
1137     AlignFrame startUpAlframe = null;
1138     // We'll only open the default file if the desktop is visible.
1139     // And the user
1140     // ////////////////////
1141
1142     if (!Platform.isJS() && !headless && file == null
1143             && Cache.getDefault("SHOW_STARTUP_FILE", true)
1144             && !cmds.commandArgsProvided()
1145             && !bootstrapArgs.getBoolean(Arg.NOSTARTUPFILE))
1146     // don't open the startup file if command line args have been processed
1147     // (&& !Commands.commandArgsProvided())
1148     /**
1149      * Java only
1150      * 
1151      * @j2sIgnore
1152      */
1153     {
1154       file = Cache.getDefault("STARTUP_FILE",
1155               Cache.getDefault("www.jalview.org", "https://www.jalview.org")
1156                       + "/examples/exampleFile_2_7.jvp");
1157       if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
1158               || file.equals(
1159                       "http://www.jalview.org/examples/exampleFile_2_7.jar"))
1160       {
1161         file.replace("http:", "https:");
1162         // hardwire upgrade of the startup file
1163         file.replace("_2_3", "_2_7");
1164         file.replace("2_7.jar", "2_7.jvp");
1165         // and remove the stale setting
1166         Cache.removeProperty("STARTUP_FILE");
1167       }
1168
1169       protocol = AppletFormatAdapter.checkProtocol(file);
1170
1171       if (file.endsWith(".jar"))
1172       {
1173         format = FileFormat.Jalview;
1174       }
1175       else
1176       {
1177         try
1178         {
1179           format = new IdentifyFile().identify(file, protocol);
1180         } catch (FileFormatException e)
1181         {
1182           // TODO what?
1183         }
1184       }
1185
1186       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
1187               format);
1188       // don't ask to save when quitting if only the startup file has been
1189       // opened
1190       Console.debug("Resetting up-to-date flag for startup file");
1191       startUpAlframe.getViewport().setSavedUpToDate(true);
1192       // extract groovy arguments before anything else.
1193     }
1194
1195     // Once all other stuff is done, execute any groovy scripts (in order)
1196     if (groovyscript != null)
1197     {
1198       if (Cache.groovyJarsPresent())
1199       {
1200         System.out.println("Executing script " + groovyscript);
1201         executeGroovyScript(groovyscript, startUpAlframe);
1202       }
1203       else
1204       {
1205         System.err.println(
1206                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
1207                         + groovyscript);
1208       }
1209     }
1210     // and finally, turn off batch mode indicator - if the desktop still exists
1211     if (desktop != null)
1212     {
1213       if (progress != -1)
1214       {
1215         desktop.setProgressBar(null, progress);
1216       }
1217       desktop.setInBatchMode(false);
1218     }
1219   }
1220
1221   private static void setLookAndFeel()
1222   {
1223     if (!Platform.isJS())
1224     /**
1225      * Java only
1226      * 
1227      * @j2sIgnore
1228      */
1229     {
1230       // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
1231       // "mac" or "flat"
1232       // If not set (or chosen laf fails), use the normal SystemLaF and if on
1233       // Mac,
1234       // try Quaqua/Vaqua.
1235       String lafProp = System.getProperty("laf");
1236       String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
1237       String laf = "none";
1238       if (lafProp != null)
1239       {
1240         laf = lafProp;
1241       }
1242       else if (lafSetting != null)
1243       {
1244         laf = lafSetting;
1245       }
1246       boolean lafSet = false;
1247       switch (laf)
1248       {
1249       case "crossplatform":
1250         lafSet = setCrossPlatformLookAndFeel();
1251         if (!lafSet)
1252         {
1253           Console.error("Could not set requested laf=" + laf);
1254         }
1255         break;
1256       case "system":
1257         lafSet = setSystemLookAndFeel();
1258         if (!lafSet)
1259         {
1260           Console.error("Could not set requested laf=" + laf);
1261         }
1262         break;
1263       case "gtk":
1264         lafSet = setGtkLookAndFeel();
1265         if (!lafSet)
1266         {
1267           Console.error("Could not set requested laf=" + laf);
1268         }
1269         break;
1270       case "metal":
1271         lafSet = setMetalLookAndFeel();
1272         if (!lafSet)
1273         {
1274           Console.error("Could not set requested laf=" + laf);
1275         }
1276         break;
1277       case "nimbus":
1278         lafSet = setNimbusLookAndFeel();
1279         if (!lafSet)
1280         {
1281           Console.error("Could not set requested laf=" + laf);
1282         }
1283         break;
1284       case "flat":
1285         lafSet = setFlatLookAndFeel();
1286         if (!lafSet)
1287         {
1288           Console.error("Could not set requested laf=" + laf);
1289         }
1290         break;
1291       case "mac":
1292         lafSet = setMacLookAndFeel();
1293         if (!lafSet)
1294         {
1295           Console.error("Could not set requested laf=" + laf);
1296         }
1297         break;
1298       case "none":
1299         break;
1300       default:
1301         Console.error("Requested laf=" + laf + " not implemented");
1302       }
1303       if (!lafSet)
1304       {
1305         // Flatlaf default for everyone!
1306         lafSet = setFlatLookAndFeel();
1307         if (!lafSet)
1308         {
1309           setSystemLookAndFeel();
1310         }
1311         if (Platform.isLinux())
1312         {
1313           setLinuxLookAndFeel();
1314         }
1315         if (Platform.isMac())
1316         {
1317           setMacLookAndFeel();
1318         }
1319       }
1320     }
1321   }
1322
1323   private static boolean setCrossPlatformLookAndFeel()
1324   {
1325     boolean set = false;
1326     try
1327     {
1328       UIManager.setLookAndFeel(
1329               UIManager.getCrossPlatformLookAndFeelClassName());
1330       set = true;
1331     } catch (Exception ex)
1332     {
1333       Console.error("Unexpected Look and Feel Exception");
1334       Console.error(ex.getMessage());
1335       Console.debug(Cache.getStackTraceString(ex));
1336     }
1337     return set;
1338   }
1339
1340   private static boolean setSystemLookAndFeel()
1341   {
1342     boolean set = false;
1343     try
1344     {
1345       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
1346       set = true;
1347     } catch (Exception ex)
1348     {
1349       Console.error("Unexpected Look and Feel Exception");
1350       Console.error(ex.getMessage());
1351       Console.debug(Cache.getStackTraceString(ex));
1352     }
1353     return set;
1354   }
1355
1356   private static boolean setSpecificLookAndFeel(String name,
1357           String className, boolean nameStartsWith)
1358   {
1359     boolean set = false;
1360     try
1361     {
1362       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
1363       {
1364         if (info.getName() != null && nameStartsWith
1365                 ? info.getName().toLowerCase(Locale.ROOT)
1366                         .startsWith(name.toLowerCase(Locale.ROOT))
1367                 : info.getName().toLowerCase(Locale.ROOT)
1368                         .equals(name.toLowerCase(Locale.ROOT)))
1369         {
1370           className = info.getClassName();
1371           break;
1372         }
1373       }
1374       UIManager.setLookAndFeel(className);
1375       set = true;
1376     } catch (Exception ex)
1377     {
1378       Console.error("Unexpected Look and Feel Exception");
1379       Console.error(ex.getMessage());
1380       Console.debug(Cache.getStackTraceString(ex));
1381     }
1382     return set;
1383   }
1384
1385   private static boolean setGtkLookAndFeel()
1386   {
1387     return setSpecificLookAndFeel("gtk",
1388             "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1389   }
1390
1391   private static boolean setMetalLookAndFeel()
1392   {
1393     return setSpecificLookAndFeel("metal",
1394             "javax.swing.plaf.metal.MetalLookAndFeel", false);
1395   }
1396
1397   private static boolean setNimbusLookAndFeel()
1398   {
1399     return setSpecificLookAndFeel("nimbus",
1400             "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1401   }
1402
1403   private static boolean setFlatLookAndFeel()
1404   {
1405     boolean set = false;
1406     if (SystemInfo.isMacOS)
1407     {
1408       try
1409       {
1410         UIManager.setLookAndFeel(
1411                 "com.formdev.flatlaf.themes.FlatMacLightLaf");
1412         set = true;
1413         Console.debug("Using FlatMacLightLaf");
1414       } catch (ClassNotFoundException | InstantiationException
1415               | IllegalAccessException | UnsupportedLookAndFeelException e)
1416       {
1417         Console.debug("Exception loading FlatLightLaf", e);
1418       }
1419       System.setProperty("apple.laf.useScreenMenuBar", "true");
1420       System.setProperty("apple.awt.application.name",
1421               ChannelProperties.getProperty("app_name"));
1422       System.setProperty("apple.awt.application.appearance", "system");
1423       if (SystemInfo.isMacFullWindowContentSupported
1424               && Desktop.desktop != null)
1425       {
1426         Console.debug("Setting transparent title bar");
1427         Desktop.desktop.getRootPane()
1428                 .putClientProperty("apple.awt.fullWindowContent", true);
1429         Desktop.desktop.getRootPane()
1430                 .putClientProperty("apple.awt.transparentTitleBar", true);
1431         Desktop.desktop.getRootPane()
1432                 .putClientProperty("apple.awt.fullscreenable", true);
1433       }
1434       SwingUtilities.invokeLater(() -> {
1435         FlatMacLightLaf.setup();
1436       });
1437       Console.debug("Using FlatMacLightLaf");
1438       set = true;
1439     }
1440     if (!set)
1441     {
1442       try
1443       {
1444         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1445         set = true;
1446         Console.debug("Using FlatLightLaf");
1447       } catch (ClassNotFoundException | InstantiationException
1448               | IllegalAccessException | UnsupportedLookAndFeelException e)
1449       {
1450         Console.debug("Exception loading FlatLightLaf", e);
1451       }
1452       // Windows specific properties here
1453       SwingUtilities.invokeLater(() -> {
1454         FlatLightLaf.setup();
1455       });
1456       Console.debug("Using FlatLightLaf");
1457       set = true;
1458     }
1459     else if (SystemInfo.isLinux)
1460     {
1461       try
1462       {
1463         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1464         set = true;
1465         Console.debug("Using FlatLightLaf");
1466       } catch (ClassNotFoundException | InstantiationException
1467               | IllegalAccessException | UnsupportedLookAndFeelException e)
1468       {
1469         Console.debug("Exception loading FlatLightLaf", e);
1470       }
1471       // enable custom window decorations
1472       JFrame.setDefaultLookAndFeelDecorated(true);
1473       JDialog.setDefaultLookAndFeelDecorated(true);
1474       SwingUtilities.invokeLater(() -> {
1475         FlatLightLaf.setup();
1476       });
1477       Console.debug("Using FlatLightLaf");
1478       set = true;
1479     }
1480
1481     if (!set)
1482     {
1483       try
1484       {
1485         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1486         set = true;
1487         Console.debug("Using FlatLightLaf");
1488       } catch (ClassNotFoundException | InstantiationException
1489               | IllegalAccessException | UnsupportedLookAndFeelException e)
1490       {
1491         Console.debug("Exception loading FlatLightLaf", e);
1492       }
1493     }
1494
1495     if (set)
1496     {
1497       UIManager.put("TabbedPane.tabType", "card");
1498       UIManager.put("TabbedPane.showTabSeparators", true);
1499       UIManager.put("TabbedPane.showContentSeparator", true);
1500       UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
1501       UIManager.put("TabbedPane.tabsOverlapBorder", true);
1502       UIManager.put("TabbedPane.hasFullBorder", true);
1503       UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
1504       UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
1505       UIManager.put("TabbedPane.smoothScrolling", true);
1506       UIManager.put("TabbedPane.tabWidthMode", "compact");
1507       UIManager.put("TabbedPane.selectedBackground", Color.white);
1508       UIManager.put("TabbedPane.background", new Color(236, 236, 236));
1509       UIManager.put("TabbedPane.hoverColor", Color.lightGray);
1510     }
1511
1512     Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
1513     return set;
1514   }
1515
1516   private static boolean setMacLookAndFeel()
1517   {
1518     boolean set = false;
1519     System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1520             ChannelProperties.getProperty("app_name"));
1521     System.setProperty("apple.laf.useScreenMenuBar", "true");
1522     /*
1523      * broken native LAFs on (ARM?) macbooks
1524     set = setQuaquaLookAndFeel();
1525     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1526             .toLowerCase(Locale.ROOT).contains("quaqua"))
1527     {
1528       set = setVaquaLookAndFeel();
1529     }
1530      */
1531     set = setFlatLookAndFeel();
1532     return set;
1533   }
1534
1535   private static boolean setLinuxLookAndFeel()
1536   {
1537     boolean set = false;
1538     set = setFlatLookAndFeel();
1539     if (!set)
1540       set = setMetalLookAndFeel();
1541     // avoid GtkLookAndFeel -- not good results especially on HiDPI
1542     if (!set)
1543       set = setNimbusLookAndFeel();
1544     return set;
1545   }
1546
1547   /*
1548   private static void showUsage()
1549   {
1550     System.out.println(
1551             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1552                     + "-nodisplay\tRun Jalview without User Interface.\n"
1553                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1554                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1555                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1556                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1557                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
1558                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1559                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1560                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1561                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1562                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1563                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1564                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1565                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1566                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1567                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
1568                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1569                     + "-html FILE\tCreate HTML file from alignment.\n"
1570                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1571                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1572                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1573                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1574                     + "-noquestionnaire\tTurn off questionnaire check.\n"
1575                     + "-nonews\tTurn off check for Jalview news.\n"
1576                     + "-nousagestats\tTurn off analytics tracking for this session.\n"
1577                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1578                     // +
1579                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1580                     // after all other properties files have been read\n\t
1581                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1582                     // passed in correctly)"
1583                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1584                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1585                     + "-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"
1586                     + "-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"
1587                     + "-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"
1588                     + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
1589   }
1590   */
1591
1592   private static void startUsageStats(final Desktop desktop)
1593   {
1594     /**
1595      * start a User Config prompt asking if we can log usage statistics.
1596      */
1597     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
1598             "USAGESTATS",
1599             MessageManager.getString("prompt.plausible_analytics_title"),
1600             MessageManager.getString("prompt.plausible_analytics"),
1601             new Runnable()
1602             {
1603               @Override
1604               public void run()
1605               {
1606                 Console.debug("Initialising analytics for usage stats.");
1607                 Cache.initAnalytics();
1608                 Console.debug("Tracking enabled.");
1609               }
1610             }, new Runnable()
1611             {
1612               @Override
1613               public void run()
1614               {
1615                 Console.debug("Not enabling analytics.");
1616               }
1617             }, null, true);
1618     desktop.addDialogThread(prompter);
1619   }
1620
1621   /**
1622    * Locate the given string as a file and pass it to the groovy interpreter.
1623    * 
1624    * @param groovyscript
1625    *          the script to execute
1626    * @param jalviewContext
1627    *          the Jalview Desktop object passed in to the groovy binding as the
1628    *          'Jalview' object.
1629    */
1630   protected void executeGroovyScript(String groovyscript, AlignFrame af)
1631   {
1632     /**
1633      * for scripts contained in files
1634      */
1635     File tfile = null;
1636     /**
1637      * script's URI
1638      */
1639     URL sfile = null;
1640     if (groovyscript.trim().equals("STDIN"))
1641     {
1642       // read from stdin into a tempfile and execute it
1643       try
1644       {
1645         tfile = File.createTempFile("jalview", "groovy");
1646         PrintWriter outfile = new PrintWriter(
1647                 new OutputStreamWriter(new FileOutputStream(tfile)));
1648         BufferedReader br = new BufferedReader(
1649                 new InputStreamReader(System.in));
1650         String line = null;
1651         while ((line = br.readLine()) != null)
1652         {
1653           outfile.write(line + "\n");
1654         }
1655         br.close();
1656         outfile.flush();
1657         outfile.close();
1658
1659       } catch (Exception ex)
1660       {
1661         System.err.println("Failed to read from STDIN into tempfile "
1662                 + ((tfile == null) ? "(tempfile wasn't created)"
1663                         : tfile.toString()));
1664         ex.printStackTrace();
1665         return;
1666       }
1667       try
1668       {
1669         sfile = tfile.toURI().toURL();
1670       } catch (Exception x)
1671       {
1672         System.err.println(
1673                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1674                         + tfile.toURI());
1675         x.printStackTrace();
1676         return;
1677       }
1678     }
1679     else
1680     {
1681       try
1682       {
1683         sfile = new URI(groovyscript).toURL();
1684       } catch (Exception x)
1685       {
1686         tfile = new File(groovyscript);
1687         if (!tfile.exists())
1688         {
1689           System.err.println("File '" + groovyscript + "' does not exist.");
1690           return;
1691         }
1692         if (!tfile.canRead())
1693         {
1694           System.err.println("File '" + groovyscript + "' cannot be read.");
1695           return;
1696         }
1697         if (tfile.length() < 1)
1698         {
1699           System.err.println("File '" + groovyscript + "' is empty.");
1700           return;
1701         }
1702         try
1703         {
1704           sfile = tfile.getAbsoluteFile().toURI().toURL();
1705         } catch (Exception ex)
1706         {
1707           System.err.println("Failed to create a file URL for "
1708                   + tfile.getAbsoluteFile());
1709           return;
1710         }
1711       }
1712     }
1713     try
1714     {
1715       Map<String, java.lang.Object> vbinding = new HashMap<>();
1716       vbinding.put("Jalview", this);
1717       if (af != null)
1718       {
1719         vbinding.put("currentAlFrame", af);
1720       }
1721       Binding gbinding = new Binding(vbinding);
1722       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1723       gse.run(sfile.toString(), gbinding);
1724       if ("STDIN".equals(groovyscript))
1725       {
1726         // delete temp file that we made -
1727         // only if it was successfully executed
1728         tfile.delete();
1729       }
1730     } catch (Exception e)
1731     {
1732       System.err.println("Exception Whilst trying to execute file " + sfile
1733               + " as a groovy script.");
1734       e.printStackTrace(System.err);
1735
1736     }
1737   }
1738
1739   public static boolean isHeadlessMode()
1740   {
1741     String isheadless = System.getProperty("java.awt.headless");
1742     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1743     {
1744       return true;
1745     }
1746     return false;
1747   }
1748
1749   public AlignFrame[] getAlignFrames()
1750   {
1751     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1752             : Desktop.getAlignFrames();
1753
1754   }
1755
1756   /**
1757    * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
1758    */
1759   public void quit()
1760   {
1761     // System.exit will run the shutdownHook first
1762     Jalview.exit("Quitting now. Bye!", 0);
1763   }
1764
1765   public static AlignFrame getCurrentAlignFrame()
1766   {
1767     return Jalview.currentAlignFrame;
1768   }
1769
1770   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1771   {
1772     Jalview.currentAlignFrame = currentAlignFrame;
1773   }
1774
1775   protected Commands getCommands()
1776   {
1777     return cmds;
1778   }
1779
1780   public static void exit(String message, int exitcode)
1781   {
1782     if (Console.log == null)
1783     {
1784       // Don't start the logger just to exit!
1785       if (message != null)
1786       {
1787         if (exitcode == 0)
1788         {
1789           System.out.println(message);
1790         }
1791         else
1792         {
1793           System.err.println(message);
1794         }
1795       }
1796     }
1797     else
1798     {
1799       Console.debug("Using Jalview.exit");
1800       if (message != null)
1801       {
1802         if (exitcode == 0)
1803         {
1804           Console.info(message);
1805         }
1806         else
1807         {
1808           Console.error(message);
1809         }
1810       }
1811     }
1812     if (exitcode > -1)
1813     {
1814       System.exit(exitcode);
1815     }
1816   }
1817
1818   /******************************
1819    * 
1820    * TEST OUTPUT METHODS
1821    * 
1822    ******************************/
1823   /**
1824    * method for reporting string values parsed/processed during tests
1825    * 
1826    */
1827   protected static void testoutput(ArgParser ap, Arg a, String s1,
1828           String s2)
1829   {
1830     BootstrapArgs bsa = ap.getBootstrapArgs();
1831     if (!bsa.getBoolean(Arg.TESTOUTPUT))
1832       return;
1833     if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
1834     {
1835       Console.debug("testoutput with unmatching values '" + s1 + "' and '"
1836               + s2 + "' for arg " + a.argString());
1837       return;
1838     }
1839     boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
1840             : ap.isSet(a);
1841     if (!isset)
1842     {
1843       Console.warn("Arg '" + a.getName() + "' not set at all");
1844       return;
1845     }
1846     testoutput(true, a, s1, s2);
1847   }
1848
1849   /**
1850    * method for reporting string values parsed/processed during tests
1851    */
1852
1853   protected static void testoutput(BootstrapArgs bsa, Arg a, String s1,
1854           String s2)
1855   {
1856     if (!bsa.getBoolean(Arg.TESTOUTPUT))
1857       return;
1858     if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2))))
1859     {
1860       Console.debug("testoutput with unmatching values '" + s1 + "' and '"
1861               + s2 + "' for arg " + a.argString());
1862       return;
1863     }
1864     if (!a.hasOption(Opt.BOOTSTRAP))
1865     {
1866       Console.error("Non-bootstrap Arg '" + a.getName()
1867               + "' given to testoutput(BootstrapArgs bsa, Arg a, String s1, String s2) with only BootstrapArgs");
1868     }
1869     if (!bsa.contains(a))
1870     {
1871       Console.warn("Arg '" + a.getName() + "' not set at all");
1872       return;
1873     }
1874     testoutput(true, a, s1, s2);
1875   }
1876
1877   /**
1878    * report value set for string values parsed/processed during tests
1879    */
1880   private static void testoutput(boolean yes, Arg a, String s1, String s2)
1881   {
1882     if (yes && ((s1 == null && s2 == null)
1883             || (s1 != null && s1.equals(s2))))
1884     {
1885       System.out.println("[TESTOUTPUT] arg " + a.argString() + "='" + s1
1886               + "' was set");
1887     }
1888   }
1889
1890   /*
1891    * testoutput for boolean and unary values
1892    */
1893   protected static void testoutput(ArgParser ap, Arg a)
1894   {
1895     if (ap == null)
1896       return;
1897     BootstrapArgs bsa = ap.getBootstrapArgs();
1898     if (bsa == null)
1899       return;
1900     if (!bsa.getBoolean(Arg.TESTOUTPUT))
1901       return;
1902     boolean val = a.hasOption(Opt.BOOTSTRAP) ? bsa.getBoolean(a)
1903             : ap.getBoolean(a);
1904     boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a)
1905             : ap.isSet(a);
1906     if (!isset)
1907     {
1908       Console.warn("Arg '" + a.getName() + "' not set at all");
1909       return;
1910     }
1911     testoutput(val, a);
1912   }
1913
1914   protected static void testoutput(BootstrapArgs bsa, Arg a)
1915   {
1916     if (!bsa.getBoolean(Arg.TESTOUTPUT))
1917       return;
1918     if (!a.hasOption(Opt.BOOTSTRAP))
1919     {
1920       Console.warn("Non-bootstrap Arg '" + a.getName()
1921               + "' given to testoutput(BootstrapArgs bsa, Arg a) with only BootstrapArgs");
1922
1923     }
1924     if (!bsa.contains(a))
1925     {
1926       Console.warn("Arg '" + a.getName() + "' not set at all");
1927       return;
1928     }
1929     testoutput(bsa.getBoolean(a), a);
1930   }
1931
1932   private static void testoutput(boolean yes, Arg a)
1933   {
1934     String message = null;
1935     if (a.hasOption(Opt.BOOLEAN))
1936     {
1937       message = (yes ? a.argString() : a.negateArgString()) + " was set";
1938     }
1939     else if (a.hasOption(Opt.UNARY))
1940     {
1941       message = a.argString() + (yes ? " was set" : " was not set");
1942     }
1943     System.out.println("[TESTOUTPUT] arg " + message);
1944   }
1945 }