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