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