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