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