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