JAL-3626 from JAL-3253-applet web page embedding
[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 jalview.api.AlignCalcWorkerI;
24 import jalview.api.AlignViewportI;
25 import jalview.api.JalviewApp;
26 import jalview.api.StructureSelectionManagerProvider;
27 import jalview.datamodel.ColumnSelection;
28 import jalview.datamodel.HiddenColumns;
29 import jalview.datamodel.PDBEntry;
30 import jalview.datamodel.SequenceGroup;
31 import jalview.datamodel.SequenceI;
32 import jalview.ext.so.SequenceOntology;
33 import jalview.gui.AlignFrame;
34 import jalview.gui.AlignmentPanel;
35 import jalview.gui.CalculationChooser;
36 import jalview.gui.Desktop;
37 import jalview.gui.PromptUserConfig;
38 import jalview.gui.StructureViewer;
39 import jalview.io.AppletFormatAdapter;
40 import jalview.io.BioJsHTMLOutput;
41 import jalview.io.DataSourceType;
42 import jalview.io.FileFormat;
43 import jalview.io.FileFormatException;
44 import jalview.io.FileFormatI;
45 import jalview.io.FileLoader;
46 import jalview.io.HtmlSvgOutput;
47 import jalview.io.IdentifyFile;
48 import jalview.io.NewickFile;
49 import jalview.io.gff.SequenceOntologyFactory;
50 import jalview.javascript.JSFunctionExec;
51 import jalview.javascript.MouseOverStructureListener;
52 import jalview.renderer.seqfeatures.FeatureRenderer;
53 import jalview.schemes.ColourSchemeI;
54 import jalview.schemes.ColourSchemeProperty;
55 import jalview.structure.SelectionSource;
56 import jalview.structure.VamsasSource;
57 import jalview.util.MessageManager;
58 import jalview.util.Platform;
59 import jalview.ws.jws2.Jws2Discoverer;
60 import netscape.javascript.JSObject;
61
62 import java.applet.AppletContext;
63 import java.io.BufferedReader;
64 import java.io.File;
65 import java.io.FileOutputStream;
66 import java.io.IOException;
67 import java.io.InputStreamReader;
68 import java.io.OutputStreamWriter;
69 import java.io.PrintWriter;
70 import java.net.MalformedURLException;
71 import java.net.URI;
72 import java.net.URISyntaxException;
73 import java.net.URL;
74 import java.security.AllPermission;
75 import java.security.CodeSource;
76 import java.security.PermissionCollection;
77 import java.security.Permissions;
78 import java.security.Policy;
79 import java.util.HashMap;
80 import java.util.Hashtable;
81 import java.util.Map;
82 import java.util.Vector;
83 import java.util.logging.ConsoleHandler;
84 import java.util.logging.Level;
85 import java.util.logging.Logger;
86
87 import javax.swing.LookAndFeel;
88 import javax.swing.UIManager;
89
90 import com.threerings.getdown.util.LaunchUtil;
91
92 import groovy.lang.Binding;
93 import groovy.util.GroovyScriptEngine;
94
95 /**
96  * Main class for Jalview Application <br>
97  * <br>
98  * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
99  * jalview.bin.Jalview
100  * 
101  * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
102  * jalview.bin.Jalview jalview.bin.Jalview
103  * 
104  * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
105  * embellish '*' to e.g. '*.jar')
106  * 
107  * @author $author$
108  * @version $Revision$
109  */
110 public class Jalview implements JalviewJSApi
111 {
112   static
113   {
114     Platform.getURLCommandArguments();
115   }
116
117   // singleton instance of this class
118
119   private static Jalview instance;
120
121   private Desktop desktop;
122
123   public static AlignFrame currentAlignFrame;
124
125   public boolean isJavaAppletTag;
126
127   public String appletResourcePath;
128
129   JalviewAppLoader appLoader;
130
131   protected JSFunctionExec jsFunctionExec;
132
133   private boolean noCalculation, noMenuBar, noStatus;
134
135   private boolean noAnnotation;
136
137   public boolean getStartCalculations()
138   {
139     return !noCalculation;
140   }
141
142   public boolean getAllowMenuBar()
143   {
144     return !noMenuBar;
145   }
146
147   public boolean getShowStatus()
148   {
149     return !noStatus;
150   }
151
152   public boolean getShowAnnotation()
153   {
154     return !noAnnotation;
155   }
156
157
158   static
159   {
160     if (!Platform.isJS())
161     /**
162      * Java only
163      * 
164      * @j2sIgnore
165      */
166     {
167       // grab all the rights we can for the JVM
168             Policy.setPolicy(new Policy()
169             {
170               @Override
171               public PermissionCollection getPermissions(CodeSource codesource)
172               {
173                 Permissions perms = new Permissions();
174                 perms.add(new AllPermission());
175                 return (perms);
176               }
177         
178               @Override
179               public void refresh()
180               {
181               }
182             });
183     }
184   }
185
186   /**
187    * keep track of feature fetching tasks.
188    * 
189    * @author JimP
190    * 
191    */
192   class FeatureFetcher
193   {
194     /*
195      * TODO: generalise to track all jalview events to orchestrate batch processing
196      * events.
197      */
198
199     private int queued = 0;
200
201     private int running = 0;
202
203     public FeatureFetcher()
204     {
205
206     }
207
208     public void addFetcher(final AlignFrame af,
209             final Vector<String> dasSources)
210     {
211       final long id = System.currentTimeMillis();
212       queued++;
213       final FeatureFetcher us = this;
214       new Thread(new Runnable()
215       {
216
217         @Override
218         public void run()
219         {
220           synchronized (us)
221           {
222             queued--;
223             running++;
224           }
225
226           af.setProgressBar(MessageManager
227                   .getString("status.das_features_being_retrived"), id);
228           af.featureSettings_actionPerformed(null);
229           af.setProgressBar(null, id);
230           synchronized (us)
231           {
232             running--;
233           }
234         }
235       }).start();
236     }
237
238     public synchronized boolean allFinished()
239     {
240       return queued == 0 && running == 0;
241     }
242
243   }
244
245   public static Jalview getInstance()
246   {
247     return instance;
248   }
249
250   /**
251    * main class for Jalview application
252    * 
253    * @param args
254    *               open <em>filename</em>
255    */
256   public static void main(String[] args)
257   {
258     if (false)
259     {
260       Platform.startJavaLogging();
261     }
262
263     instance = new Jalview();
264     instance.doMain(args);
265 }
266
267   /**
268    * @param args
269    */
270   void doMain(String[] args)
271   {
272
273     if (!Platform.isJS())
274     {
275       System.setSecurityManager(null);
276     }
277
278     System.out
279             .println("Java version: "
280                     + System.getProperty("java.version"));
281     System.out.println("Java Home: " + System.getProperty("java.home"));
282     System.out.println(System.getProperty("os.arch") + " "
283             + System.getProperty("os.name") + " "
284             + System.getProperty("os.version"));
285     String val = System.getProperty("sys.install4jVersion");
286     if (val != null) {
287     System.out.println("Install4j version: " + val);
288     }
289     val = System.getProperty("installer_template_version");
290     if (val != null) {
291       System.out.println("Install4j template version: " + val);
292     }
293     val = System.getProperty("launcher_version");
294     if (val != null) {
295       System.out.println("Launcher version: " + val);
296     }
297
298     // report Jalview version
299     Cache.loadBuildProperties(true);
300
301     ArgsParser aparser = new ArgsParser(args);
302     boolean headless = false;
303
304     String usrPropsFile = aparser.getValue("props");
305     Cache.loadProperties(usrPropsFile); // must do this before
306     if (usrPropsFile != null)
307     {
308       System.out.println(
309               "CMD [-props " + usrPropsFile + "] executed successfully!");
310     }
311
312     if (!Platform.isJS())
313     /**
314      * Java only
315      * 
316      * @j2sIgnore
317      */
318     {
319       if (aparser.contains("help") || aparser.contains("h"))
320       {
321         showUsage();
322         System.exit(0);
323       }
324       if (aparser.contains("nodisplay") || aparser.contains("nogui")
325               || aparser.contains("headless"))
326       {
327         System.setProperty("java.awt.headless", "true");
328         headless = true;
329       }
330       // anything else!
331
332       final String jabawsUrl = aparser.getValue("jabaws");
333       if (jabawsUrl != null)
334       {
335         try
336         {
337           Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
338           System.out.println(
339                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
340         } catch (MalformedURLException e)
341         {
342           System.err.println(
343                   "Invalid jabaws parameter: " + jabawsUrl + " ignored");
344         }
345       }
346
347     }
348     String defs = aparser.getValue("setprop");
349     while (defs != null)
350     {
351       int p = defs.indexOf('=');
352       if (p == -1)
353       {
354         System.err.println("Ignoring invalid setprop argument : " + defs);
355       }
356       else
357       {
358         System.out.println("Executing setprop argument: " + defs);
359         if (Platform.isJS())
360         {
361           Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
362         }
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     try
374     {
375       Cache.initLogger();
376     } catch (NoClassDefFoundError error)
377     {
378       error.printStackTrace();
379       System.out.println("\nEssential logging libraries not found."
380               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
381       System.exit(0);
382     }
383
384     desktop = null;
385
386     try
387     {
388       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
389     } catch (Exception ex)
390     {
391       System.err.println("Unexpected Look and Feel Exception");
392       ex.printStackTrace();
393     }
394     if (Platform.isAMacAndNotJS())
395     {
396
397       LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager
398               .getLookAndFeel();
399       System.setProperty("com.apple.mrj.application.apple.menu.about.name",
400               "Jalview");
401       System.setProperty("apple.laf.useScreenMenuBar", "true");
402       if (lookAndFeel != null)
403       {
404         try
405         {
406           UIManager.setLookAndFeel(lookAndFeel);
407         } catch (Throwable e)
408         {
409           System.err.println(
410                   "Failed to set QuaQua look and feel: " + e.toString());
411         }
412       }
413       if (lookAndFeel == null
414               || !(lookAndFeel.getClass().isAssignableFrom(
415                       UIManager.getLookAndFeel().getClass()))
416               || !UIManager.getLookAndFeel().getClass().toString()
417                       .toLowerCase().contains("quaqua"))
418       {
419         try
420         {
421           System.err.println(
422                   "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976");
423           UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
424         } catch (Throwable e)
425         {
426           System.err.println(
427                   "Failed to reset look and feel: " + e.toString());
428         }
429       }
430     }
431
432     /*
433      * configure 'full' SO model if preferences say to, else use the default (full SO)
434      * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
435      */
436     boolean soDefault = !Platform.isJS();
437     if (Cache.getDefault("USE_FULL_SO", soDefault))
438     {
439       SequenceOntologyFactory.setInstance(new SequenceOntology());
440     }
441
442     if (!headless)
443     {
444       desktop = new Desktop();
445       desktop.setInBatchMode(true); // indicate we are starting up
446
447       try
448       {
449         JalviewTaskbar.setTaskbar(this);
450       } catch (Throwable t)
451       {
452         System.out.println("Error setting Taskbar: " + t.getMessage());
453       }
454
455       desktop.setVisible(true);
456
457       if (!Platform.isJS())
458       /**
459        * Java only
460        * 
461        * @j2sIgnore
462        */
463       {
464         desktop.startServiceDiscovery();
465         if (!aparser.contains("nousagestats"))
466         {
467           startUsageStats(desktop);
468         }
469         else
470         {
471           System.err.println("CMD [-nousagestats] executed successfully!");
472         }
473
474         if (!aparser.contains("noquestionnaire"))
475         {
476           String url = aparser.getValue("questionnaire");
477           if (url != null)
478           {
479             // Start the desktop questionnaire prompter with the specified
480             // questionnaire
481             Cache.log.debug("Starting questionnaire url at " + url);
482             desktop.checkForQuestionnaire(url);
483             System.out.println("CMD questionnaire[-" + url
484                     + "] executed successfully!");
485           }
486           else
487           {
488             if (Cache.getProperty("NOQUESTIONNAIRES") == null)
489             {
490               // Start the desktop questionnaire prompter with the specified
491               // questionnaire
492               // String defurl =
493               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
494               // //
495               String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl";
496               Cache.log.debug(
497                       "Starting questionnaire with default url: " + defurl);
498               desktop.checkForQuestionnaire(defurl);
499             }
500           }
501         }
502         else
503         {
504           System.err
505                   .println("CMD [-noquestionnaire] executed successfully!");
506         }
507
508         if (!aparser.contains("nonews"))
509         {
510           desktop.checkForNews();
511         }
512
513         BioJsHTMLOutput.updateBioJS();
514       }
515     }
516
517     // Move any new getdown-launcher-new.jar into place over old
518     // getdown-launcher.jar
519     String appdirString = System.getProperty("getdownappdir");
520     if (appdirString != null && appdirString.length() > 0)
521     {
522       final File appdir = new File(appdirString);
523       new Thread()
524       {
525         @Override
526         public void run()
527         {
528           LaunchUtil.upgradeGetdown(
529                   new File(appdir, "getdown-launcher-old.jar"),
530                   new File(appdir, "getdown-launcher.jar"),
531                   new File(appdir, "getdown-launcher-new.jar"));
532         }
533       }.start();
534     }
535
536     String file = null, data = null;
537     FileFormatI format = null;
538     DataSourceType protocol = null;
539     FileLoader fileLoader = new FileLoader(!headless);
540
541     String groovyscript = null; // script to execute after all loading is
542     // completed one way or another
543     // extract groovy argument and execute if necessary
544     groovyscript = aparser.getValue("groovy", true);
545     file = aparser.getValue("open", true);
546
547     if (file == null && desktop == null)
548     {
549       System.out.println("No files to open!");
550       System.exit(1);
551     }
552     long progress = -1;
553     // Finally, deal with the remaining input data.
554     if (file != null)
555     {
556       if (!headless)
557       {
558         desktop.setProgressBar(
559                 MessageManager
560                         .getString("status.processing_commandline_args"),
561                 progress = System.currentTimeMillis());
562       }
563       System.out.println("CMD [-open " + file + "] executed successfully!");
564
565       if (!Platform.isJS())
566         /**
567          * ignore in JavaScript -- can't just file existence - could load it?
568          * 
569          * @j2sIgnore
570          */
571       {
572         if (!file.startsWith("http://") && !file.startsWith("https://"))
573         // BH 2019 added https check for Java
574         {
575           if (!(new File(file)).exists())
576           {
577             System.out.println("Can't find " + file);
578             if (headless)
579             {
580               System.exit(1);
581             }
582           }
583         }
584       }
585
586         protocol = AppletFormatAdapter.checkProtocol(file);
587
588       try
589       {
590         format = new IdentifyFile().identify(file, protocol);
591       } catch (FileFormatException e1)
592       {
593         // TODO ?
594       }
595
596       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
597               format);
598       if (af == null)
599       {
600         System.out.println("error");
601       }
602       else
603       {
604         setCurrentAlignFrame(af);
605         data = aparser.getValue("colour", true);
606         if (data != null)
607         {
608           data.replaceAll("%20", " ");
609
610           ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
611                   af.getViewport(), af.getViewport().getAlignment(), data);
612
613           if (cs != null)
614           {
615             System.out.println(
616                     "CMD [-color " + data + "] executed successfully!");
617           }
618           af.changeColour(cs);
619         }
620
621         // Must maintain ability to use the groups flag
622         data = aparser.getValue("groups", true);
623         if (data != null)
624         {
625           af.parseFeaturesFile(data,
626                   AppletFormatAdapter.checkProtocol(data));
627           // System.out.println("Added " + data);
628           System.out.println(
629                   "CMD groups[-" + data + "]  executed successfully!");
630         }
631         data = aparser.getValue("features", true);
632         if (data != null)
633         {
634           af.parseFeaturesFile(data,
635                   AppletFormatAdapter.checkProtocol(data));
636           // System.out.println("Added " + data);
637           System.out.println(
638                   "CMD [-features " + data + "]  executed successfully!");
639         }
640
641         data = aparser.getValue("annotations", true);
642         if (data != null)
643         {
644           af.loadJalviewDataFile(data, null, null, null);
645           // System.out.println("Added " + data);
646           System.out.println(
647                   "CMD [-annotations " + data + "] executed successfully!");
648         }
649         // set or clear the sortbytree flag.
650         if (aparser.contains("sortbytree"))
651         {
652           af.getViewport().setSortByTree(true);
653           if (af.getViewport().getSortByTree())
654           {
655             System.out.println("CMD [-sortbytree] executed successfully!");
656           }
657         }
658         if (aparser.contains("no-annotation"))
659         {
660           af.getViewport().setShowAnnotation(false);
661           if (!af.getViewport().isShowAnnotation())
662           {
663             System.out.println("CMD no-annotation executed successfully!");
664           }
665         }
666         if (aparser.contains("nosortbytree"))
667         {
668           af.getViewport().setSortByTree(false);
669           if (!af.getViewport().getSortByTree())
670           {
671             System.out
672                     .println("CMD [-nosortbytree] executed successfully!");
673           }
674         }
675         data = aparser.getValue("tree", true);
676         if (data != null)
677         {
678           try
679           {
680             System.out.println(
681                     "CMD [-tree " + data + "] executed successfully!");
682             NewickFile nf = new NewickFile(data,
683                     AppletFormatAdapter.checkProtocol(data));
684             af.getViewport()
685                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
686           } catch (IOException ex)
687           {
688             System.err.println("Couldn't add tree " + data);
689             ex.printStackTrace(System.err);
690           }
691         }
692         // TODO - load PDB structure(s) to alignment JAL-629
693         // (associate with identical sequence in alignment, or a specified
694         // sequence)
695         if (groovyscript != null)
696         {
697           // Execute the groovy script after we've done all the rendering stuff
698           // and before any images or figures are generated.
699           System.out.println("Executing script " + groovyscript);
700           executeGroovyScript(groovyscript, af);
701           System.out.println("CMD groovy[" + groovyscript
702                   + "] executed successfully!");
703           groovyscript = null;
704         }
705         String imageName = "unnamed.png";
706         while (aparser.getSize() > 1)
707         {
708           String outputFormat = aparser.nextValue();
709           file = aparser.nextValue();
710
711           if (outputFormat.equalsIgnoreCase("png"))
712           {
713             af.createPNG(new File(file));
714             imageName = (new File(file)).getName();
715             System.out.println("Creating PNG image: " + file);
716             continue;
717           }
718           else if (outputFormat.equalsIgnoreCase("svg"))
719           {
720             File imageFile = new File(file);
721             imageName = imageFile.getName();
722             af.createSVG(imageFile);
723             System.out.println("Creating SVG image: " + file);
724             continue;
725           }
726           else if (outputFormat.equalsIgnoreCase("html"))
727           {
728             File imageFile = new File(file);
729             imageName = imageFile.getName();
730             HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
731             htmlSVG.exportHTML(file);
732
733             System.out.println("Creating HTML image: " + file);
734             continue;
735           }
736           else if (outputFormat.equalsIgnoreCase("biojsmsa"))
737           {
738             if (file == null)
739             {
740               System.err.println("The output html file must not be null");
741               return;
742             }
743             try
744             {
745               BioJsHTMLOutput.refreshVersionInfo(
746                       BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
747             } catch (URISyntaxException e)
748             {
749               e.printStackTrace();
750             }
751             BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
752             bjs.exportHTML(file);
753             System.out
754                     .println("Creating BioJS MSA Viwer HTML file: " + file);
755             continue;
756           }
757           else if (outputFormat.equalsIgnoreCase("imgMap"))
758           {
759             af.createImageMap(new File(file), imageName);
760             System.out.println("Creating image map: " + file);
761             continue;
762           }
763           else if (outputFormat.equalsIgnoreCase("eps"))
764           {
765             File outputFile = new File(file);
766             System.out.println(
767                     "Creating EPS file: " + outputFile.getAbsolutePath());
768             af.createEPS(outputFile);
769             continue;
770           }
771
772           af.saveAlignment(file, format);
773           if (af.isSaveAlignmentSuccessful())
774           {
775             System.out.println("Written alignment in " + format
776                     + " format to " + file);
777           }
778           else
779           {
780             System.out.println("Error writing file " + file + " in "
781                     + format + " format!!");
782           }
783
784         }
785
786         while (aparser.getSize() > 0)
787         {
788           System.out.println("Unknown arg: " + aparser.nextValue());
789         }
790       }
791     }
792     AlignFrame startUpAlframe = null;
793     // We'll only open the default file if the desktop is visible.
794     // And the user
795     // ////////////////////
796
797     if (!Platform.isJS() && !headless && file == null
798             && Cache.getDefault("SHOW_STARTUP_FILE", true))
799     /**
800      * Java only
801      * 
802      * @j2sIgnore
803      */
804     {
805       file = Cache.getDefault("STARTUP_FILE",
806               Cache.getDefault("www.jalview.org",
807                       "http://www.jalview.org")
808                       + "/examples/exampleFile_2_7.jar");
809       if (file.equals(
810               "http://www.jalview.org/examples/exampleFile_2_3.jar"))
811       {
812         // hardwire upgrade of the startup file
813         file.replace("_2_3.jar", "_2_7.jar");
814         // and remove the stale setting
815         Cache.removeProperty("STARTUP_FILE");
816       }
817
818       protocol = DataSourceType.FILE;
819
820       if (file.indexOf("http:") > -1)
821       {
822         protocol = DataSourceType.URL;
823       }
824
825       if (file.endsWith(".jar"))
826       {
827         format = FileFormat.Jalview;
828       }
829       else
830       {
831         try
832         {
833           format = new IdentifyFile().identify(file, protocol);
834         } catch (FileFormatException e)
835         {
836           // TODO what?
837         }
838       }
839
840       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
841               format);
842       // extract groovy arguments before anything else.
843     }
844
845     // Once all other stuff is done, execute any groovy scripts (in order)
846     if (groovyscript != null)
847     {
848       if (Cache.groovyJarsPresent())
849       {
850         System.out.println("Executing script " + groovyscript);
851         executeGroovyScript(groovyscript, startUpAlframe);
852       }
853       else
854       {
855         System.err.println(
856                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
857                         + groovyscript);
858       }
859     }
860     // and finally, turn off batch mode indicator - if the desktop still exists
861     if (desktop != null)
862     {
863       if (progress != -1)
864       {
865         desktop.setProgressBar(null, progress);
866       }
867       desktop.setInBatchMode(false);
868     }
869   }
870
871   private static void showUsage()
872   {
873     System.out.println(
874             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
875                     + "-nodisplay\tRun Jalview without User Interface.\n"
876                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
877                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
878                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
879                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
880                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
881                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
882                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
883                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
884                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
885                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
886                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
887                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
888                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
889                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
890                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
891                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
892                     + "-html FILE\tCreate HTML file from alignment.\n"
893                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
894                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
895                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
896                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
897                     + "-noquestionnaire\tTurn off questionnaire check.\n"
898                     + "-nonews\tTurn off check for Jalview news.\n"
899                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
900                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
901                     // +
902                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
903                     // after all other properties files have been read\n\t
904                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
905                     // passed in correctly)"
906                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
907                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
908                     + "-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"
909                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
910   }
911
912   private static void startUsageStats(final Desktop desktop)
913   {
914     /**
915      * start a User Config prompt asking if we can log usage statistics.
916      */
917     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
918             "USAGESTATS", "Jalview Usage Statistics",
919             "Do you want to help make Jalview better by enabling "
920                     + "the collection of usage statistics with Google Analytics ?"
921                     + "\n\n(you can enable or disable usage tracking in the preferences)",
922             new Runnable()
923             {
924               @Override
925               public void run()
926               {
927                 Cache.log.debug(
928                         "Initialising googletracker for usage stats.");
929                 Cache.initGoogleTracker();
930                 Cache.log.debug("Tracking enabled.");
931               }
932             }, new Runnable()
933             {
934               @Override
935               public void run()
936               {
937                 Cache.log.debug("Not enabling Google Tracking.");
938               }
939             }, null, true);
940     desktop.addDialogThread(prompter);
941   }
942
943   /**
944    * Locate the given string as a file and pass it to the groovy interpreter.
945    * 
946    * @param groovyscript
947    *                         the script to execute
948    * @param jalviewContext
949    *                         the Jalview Desktop object passed in to the groovy
950    *                         binding as the 'Jalview' object.
951    */
952   private void executeGroovyScript(String groovyscript, AlignFrame af)
953   {
954     /**
955      * for scripts contained in files
956      */
957     File tfile = null;
958     /**
959      * script's URI
960      */
961     URL sfile = null;
962     if (groovyscript.trim().equals("STDIN"))
963     {
964       // read from stdin into a tempfile and execute it
965       try
966       {
967         tfile = File.createTempFile("jalview", "groovy");
968         PrintWriter outfile = new PrintWriter(
969                 new OutputStreamWriter(new FileOutputStream(tfile)));
970         BufferedReader br = new BufferedReader(
971                 new InputStreamReader(System.in));
972         String line = null;
973         while ((line = br.readLine()) != null)
974         {
975           outfile.write(line + "\n");
976         }
977         br.close();
978         outfile.flush();
979         outfile.close();
980
981       } catch (Exception ex)
982       {
983         System.err.println("Failed to read from STDIN into tempfile "
984                 + ((tfile == null) ? "(tempfile wasn't created)"
985                         : tfile.toString()));
986         ex.printStackTrace();
987         return;
988       }
989       try
990       {
991         sfile = tfile.toURI().toURL();
992       } catch (Exception x)
993       {
994         System.err.println(
995                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
996                         + tfile.toURI());
997         x.printStackTrace();
998         return;
999       }
1000     }
1001     else
1002     {
1003       try
1004       {
1005         sfile = new URI(groovyscript).toURL();
1006       } catch (Exception x)
1007       {
1008         tfile = new File(groovyscript);
1009         if (!tfile.exists())
1010         {
1011           System.err.println("File '" + groovyscript + "' does not exist.");
1012           return;
1013         }
1014         if (!tfile.canRead())
1015         {
1016           System.err.println("File '" + groovyscript + "' cannot be read.");
1017           return;
1018         }
1019         if (tfile.length() < 1)
1020         {
1021           System.err.println("File '" + groovyscript + "' is empty.");
1022           return;
1023         }
1024         try
1025         {
1026           sfile = tfile.getAbsoluteFile().toURI().toURL();
1027         } catch (Exception ex)
1028         {
1029           System.err.println("Failed to create a file URL for "
1030                   + tfile.getAbsoluteFile());
1031           return;
1032         }
1033       }
1034     }
1035     try
1036     {
1037       Map<String, java.lang.Object> vbinding = new HashMap<>();
1038       vbinding.put("Jalview", this);
1039       if (af != null)
1040       {
1041         vbinding.put("currentAlFrame", af);
1042       }
1043       Binding gbinding = new Binding(vbinding);
1044       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1045       gse.run(sfile.toString(), gbinding);
1046       if ("STDIN".equals(groovyscript))
1047       {
1048         // delete temp file that we made -
1049         // only if it was successfully executed
1050         tfile.delete();
1051       }
1052     } catch (Exception e)
1053     {
1054       System.err.println("Exception Whilst trying to execute file " + sfile
1055               + " as a groovy script.");
1056       e.printStackTrace(System.err);
1057
1058     }
1059   }
1060
1061   public static boolean isHeadlessMode()
1062   {
1063     String isheadless = System.getProperty("java.awt.headless");
1064     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1065     {
1066       return true;
1067     }
1068     return false;
1069   }
1070
1071   public AlignFrame[] getAlignFrames()
1072   {
1073     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1074             : Desktop.getAlignFrames();
1075
1076   }
1077
1078   /**
1079    * Quit method delegates to Desktop.quit - unless running in headless mode when
1080    * it just ends the JVM
1081    */
1082   public void quit()
1083   {
1084     if (desktop != null)
1085     {
1086       desktop.quit();
1087     }
1088     else
1089     {
1090       System.exit(0);
1091     }
1092   }
1093
1094   public static AlignFrame getCurrentAlignFrame()
1095   {
1096     return Jalview.currentAlignFrame;
1097   }
1098
1099   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1100   {
1101     Jalview.currentAlignFrame = currentAlignFrame;
1102   }
1103   
1104   /**
1105    * Get the SwingJS applet ID and combine that with the frameType
1106    * 
1107    * @param frameType
1108    *          "alignment", "desktop", etc., or null
1109    * @return
1110    */
1111   public static String getAppID(String frameType)
1112   {
1113     String id = Cache.getProperty("Info.j2sAppletID");
1114     if (id == null)
1115     {
1116       id = "jalview";
1117     }
1118     return id + (frameType == null ? "" : "-" + frameType);
1119   }
1120
1121   /**
1122    * Handle all JalviewLite applet parameters
1123    * 
1124    * @param aparser
1125    * @param af
1126    */
1127   private void loadAppletParams(ArgsParser aparser, AlignFrame af)
1128   {
1129     JalviewApp app = new JalviewApp()
1130     {
1131
1132       // TODO BH 2019
1133       //
1134       // These are methods that are in JalviewLite that various classes call
1135       // but are not in JalviewLiteJsApi. Or, even if they are, other classes
1136       // call
1137       // them to JalviewLite directly. Some may not be necessary, but they have
1138       // to
1139       // be at least mentioned here, or the classes calling them should
1140       // reference
1141       // JalviewLite itself.
1142
1143       private boolean alignPDBStructures; // From JalviewLite; not implemented
1144
1145       private Hashtable<String, Hashtable<String, String[]>> jsmessages;
1146
1147       private Hashtable<String, int[]> jshashes;
1148
1149       @Override
1150       public String getParameter(String name)
1151       {
1152         return aparser.getAppletValue(name, null);
1153       }
1154
1155       @Override
1156       public boolean getDefaultParameter(String name, boolean def)
1157       {
1158         String stn;
1159         return ((stn = getParameter(name)) == null ? def
1160                 : "true".equalsIgnoreCase(stn));
1161       }
1162
1163       /**
1164        * Get the applet-like document base even though this is an application.
1165        */
1166       @Override
1167       public URL getDocumentBase()
1168       {
1169         return Platform.getDocumentBase();
1170       }
1171
1172       /**
1173        * Get the applet-like code base even though this is an application.
1174        */
1175       @Override
1176       public URL getCodeBase()
1177       {
1178         return Platform.getCodeBase();
1179       }
1180
1181       @Override
1182       public AlignViewportI getViewport()
1183       {
1184         return af.getViewport();
1185       }
1186
1187       /**
1188        * features
1189        * 
1190        */
1191       @Override
1192       public boolean parseFeaturesFile(String filename,
1193               DataSourceType protocol)
1194       {
1195         return af.parseFeaturesFile(filename, protocol);
1196       }
1197
1198       /**
1199        * scorefile
1200        * 
1201        */
1202       @Override
1203       public boolean loadScoreFile(String sScoreFile) throws IOException
1204       {
1205         af.loadJalviewDataFile(sScoreFile, null, null, null);
1206         return true;
1207       }
1208
1209       /**
1210        * annotations, jpredfile, jnetfile
1211        * 
1212        */
1213       @Override
1214       public void updateForAnnotations()
1215       {
1216         af.updateForAnnotations();
1217       }
1218
1219       @Override
1220       public void loadTree(NewickFile fin, String treeFile)
1221               throws IOException
1222       {
1223         // n/a -- already done by standard Jalview command line processing
1224       }
1225
1226       @Override
1227       public void setAlignPdbStructures(boolean defaultParameter)
1228       {
1229         alignPDBStructures = true;
1230       }
1231
1232       @Override
1233       public void newStructureView(PDBEntry pdb, SequenceI[] seqs,
1234               String[] chains, DataSourceType protocol)
1235       {
1236         StructureViewer.launchStructureViewer(af.alignPanel, pdb, seqs);
1237       }
1238
1239       @Override
1240       public void setFeatureGroupState(String[] groups, boolean state)
1241       {
1242         af.setFeatureGroupState(groups, state);
1243       }
1244
1245       @Override
1246       public void alignedStructureView(PDBEntry[] pdb, SequenceI[][] seqs,
1247               String[][] chains, String[] protocols)
1248       {
1249         System.err.println(
1250                 "Jalview applet interface alignedStructureView not implemented");
1251       }
1252
1253       @Override
1254       public void newFeatureSettings()
1255       {
1256         System.err.println(
1257                 "Jalview applet interface newFeatureSettings not implemented");
1258       }
1259
1260       private Vector<Runnable> jsExecQueue;
1261
1262       @Override
1263       public Vector<Runnable> getJsExecQueue(JSFunctionExec exec)
1264       {
1265         jsFunctionExec = exec;
1266         return (jsExecQueue == null ? (jsExecQueue = new Vector<>())
1267                 : jsExecQueue);
1268       }
1269
1270       @Override
1271       public AppletContext getAppletContext()
1272       {
1273         // TODO Auto-generated method stub
1274         return null;
1275       }
1276
1277       @Override
1278       public boolean isJsfallbackEnabled()
1279       {
1280         // TODO Auto-generated method stub
1281         return false;
1282       }
1283
1284       @Override
1285       public JSObject getJSObject()
1286       {
1287         // TODO Auto-generated method stub
1288         return null;
1289       }
1290
1291       @Override
1292       public StructureSelectionManagerProvider getStructureSelectionManagerProvider()
1293       {
1294         // TODO Q: what exactly is this? BH
1295         return null;
1296       }
1297
1298       @Override
1299       public void updateColoursFromMouseOver(Object source,
1300               MouseOverStructureListener mouseOverStructureListener)
1301       {
1302         // TODO Auto-generated method stub
1303
1304       }
1305
1306       @Override
1307       public Object[] getSelectionForListener(SequenceGroup seqsel,
1308               ColumnSelection colsel, HiddenColumns hidden,
1309               SelectionSource source, Object alignFrame)
1310       {
1311         return appLoader.getSelectionForListener(getCurrentAlignFrame(),
1312                 seqsel, colsel, hidden, source, alignFrame);
1313       }
1314
1315       @Override
1316       public String arrayToSeparatorList(String[] array)
1317       {
1318         return appLoader.arrayToSeparatorList(array);
1319       }
1320
1321       @Override
1322       public Hashtable<String, int[]> getJSHashes()
1323       {
1324         return (jshashes == null ? (jshashes = new Hashtable<>())
1325                 : jshashes);
1326       }
1327
1328       @Override
1329       public Hashtable<String, Hashtable<String, String[]>> getJSMessages()
1330       {
1331         return (jsmessages == null ? (jsmessages = new Hashtable<>())
1332                 : jsmessages);
1333       }
1334
1335       @Override
1336       public Object getFrameForSource(VamsasSource source)
1337       {
1338         if (source != null)
1339         {
1340           AlignFrame af;
1341           if (source instanceof jalview.gui.AlignViewport
1342                   && source == (af = getCurrentAlignFrame()).getViewport())
1343           {
1344             // should be valid if it just generated an event!
1345             return af;
1346           }
1347           // TODO: ensure that if '_af' is specified along with a handler
1348           // function, then only events from that alignFrame are sent to that
1349           // function
1350         }
1351         return null;
1352       }
1353
1354       @Override
1355       public FeatureRenderer getNewFeatureRenderer(AlignViewportI vp)
1356       {
1357         return new jalview.gui.FeatureRenderer((AlignmentPanel) vp);
1358       }
1359
1360     };
1361
1362     appLoader = new JalviewAppLoader(true);
1363     appLoader.load(app);
1364   }
1365
1366   /**
1367    * 
1368    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
1369    */
1370   @Override
1371   public String getSelectedSequences()
1372   {
1373     return getSelectedSequencesFrom(getCurrentAlignFrame());
1374   }
1375
1376   /**
1377    * 
1378    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
1379    */
1380   @Override
1381   public String getSelectedSequences(String sep)
1382   {
1383     return getSelectedSequencesFrom(getCurrentAlignFrame(), sep);
1384   }
1385
1386   /**
1387    * 
1388    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1389    *      .AlignFrame)
1390    */
1391   @Override
1392   public String getSelectedSequencesFrom(AlignFrame alf)
1393   {
1394     if (alf == null)
1395     {
1396       alf = getCurrentAlignFrame();
1397     }
1398     return getSelectedSequencesFrom(alf, null);
1399   }
1400
1401   /**
1402    * 
1403    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1404    *      .AlignFrame, java.lang.String)
1405    */
1406   @Override
1407   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
1408   {
1409     if (alf == null)
1410     {
1411       alf = getCurrentAlignFrame();
1412     }
1413     return appLoader.getSelectedSequencesFrom(alf, sep);
1414   }
1415
1416   /**
1417    * 
1418    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
1419    *      .AlignFrame, java.lang.String)
1420    */
1421   @Override
1422   public void highlight(String sequenceId, String position,
1423           String alignedPosition)
1424   {
1425     highlightIn(null, sequenceId, position,
1426             alignedPosition);
1427   }
1428
1429   @Override
1430   public void highlightIn(AlignFrame alf, String sequenceId,
1431           String position, String alignedPosition)
1432   {
1433     if (alf == null)
1434     {
1435       alf = getCurrentAlignFrame();
1436     }
1437     appLoader.highlightIn(alf, sequenceId, position, alignedPosition);
1438   }
1439
1440   @Override
1441   public void select(String sequenceIds, String columns)
1442   {
1443     selectIn(getCurrentAlignFrame(), sequenceIds, columns, null);
1444   }
1445
1446   @Override
1447   public void select(String sequenceIds, String columns, String sep)
1448   {
1449     selectIn(null, sequenceIds, columns, sep);
1450   }
1451
1452   @Override
1453   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
1454   {
1455     selectIn(alf, sequenceIds, columns, null);
1456   }
1457
1458   @Override
1459   public void selectIn(AlignFrame alf, String sequenceIds, String columns,
1460           String sep)
1461   {
1462     if (alf == null)
1463     {
1464       alf = getCurrentAlignFrame();
1465     }
1466     appLoader.selectIn(alf, sequenceIds, columns, sep);
1467   }
1468
1469   @Override
1470   public String getSelectedSequencesAsAlignment(String format,
1471           String suffix)
1472   {
1473     return getSelectedSequencesAsAlignmentFrom(null,
1474             format, suffix);
1475   }
1476
1477   @Override
1478   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
1479           String format, String sep)
1480   {
1481     if (alf == null)
1482     {
1483       alf = getCurrentAlignFrame();
1484     }
1485     return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format, sep);
1486   }
1487
1488   @Override
1489   public String getAlignmentOrder()
1490   {
1491     return getAlignmentFrom(getCurrentAlignFrame(), null);
1492   }
1493
1494   @Override
1495   public String getAlignmentOrderFrom(AlignFrame alf)
1496   {
1497     return getAlignmentFrom(alf, null);
1498   }
1499
1500   @Override
1501   public String getAlignmentOrderFrom(AlignFrame alf, String sep)
1502   {
1503     if (alf == null)
1504     {
1505       alf = getCurrentAlignFrame();
1506     }
1507     return appLoader.getAlignmentOrderFrom(alf, sep);
1508   }
1509
1510   @Override
1511   public String orderBy(String order, String undoName)
1512   {
1513     return orderBy(order, undoName, null);
1514   }
1515
1516   @Override
1517   public String orderBy(String order, String undoName, String sep)
1518   {
1519     return orderAlignmentBy(getCurrentAlignFrame(), order, undoName, sep);
1520   }
1521
1522   @Override
1523   public String orderAlignmentBy(AlignFrame alf, String order,
1524           String undoName, String sep)
1525   {
1526     if (alf == null)
1527     {
1528       alf = getCurrentAlignFrame();
1529     }
1530     return appLoader.orderAlignmentBy(alf, order, undoName, sep);
1531   }
1532
1533   @Override
1534   public String getAlignment(String format)
1535   {
1536     return getAlignmentFrom(null, format, null);
1537   }
1538
1539   @Override
1540   public String getAlignmentFrom(AlignFrame alf, String format)
1541   {
1542     return getAlignmentFrom(alf, format, null);
1543   }
1544
1545   @Override
1546   public String getAlignment(String format, String suffix)
1547   {
1548     return getAlignmentFrom(getCurrentAlignFrame(), format, suffix);
1549   }
1550
1551   @Override
1552   public String getAlignmentFrom(AlignFrame alf, String format,
1553           String suffix)
1554   {
1555     return appLoader.getAlignmentFrom(alf, format, suffix);
1556   }
1557
1558   @Override
1559   public void loadAnnotation(String annotation)
1560   {
1561     loadAnnotationFrom(getCurrentAlignFrame(), annotation);
1562   }
1563
1564   @Override
1565   public void loadAnnotationFrom(AlignFrame alf, String annotation)
1566   {
1567     if (alf == null)
1568     {
1569       alf = getCurrentAlignFrame();
1570     }
1571     appLoader.loadAnnotationFrom(alf, annotation);
1572   }
1573
1574   @Override
1575   public void loadFeatures(String features, boolean autoenabledisplay)
1576   {
1577     loadFeaturesFrom(currentAlignFrame, features, autoenabledisplay);
1578   }
1579
1580   @Override
1581   public boolean loadFeaturesFrom(AlignFrame alf, String features,
1582           boolean autoenabledisplay)
1583   {
1584     if (alf == null)
1585     {
1586       alf = getCurrentAlignFrame();
1587     }
1588     return appLoader.loadFeaturesFrom(alf, features, autoenabledisplay);
1589   }
1590
1591   @Override
1592   public String getFeatures(String format)
1593   {
1594     return getFeaturesFrom(null, format);
1595   }
1596
1597   @Override
1598   public String getFeaturesFrom(AlignFrame alf, String format)
1599   {
1600     if (alf == null)
1601     {
1602       alf = getCurrentAlignFrame();
1603     }
1604     return appLoader.getFeaturesFrom(alf, format);
1605   }
1606
1607   @Override
1608   public String getAnnotation()
1609   {
1610     return getAnnotationFrom(null);
1611   }
1612
1613   @Override
1614   public String getAnnotationFrom(AlignFrame alf)
1615   {
1616     if (alf == null)
1617     {
1618       alf = getCurrentAlignFrame();
1619     }
1620     return appLoader.getAnnotationFrom(alf);
1621   }
1622
1623 //  @Override
1624 //  public AlignFrame newView()
1625 //  {
1626 //    return newViewFrom(null, null);
1627 //  }
1628 //
1629 //  @Override
1630 //  public AlignFrame newView(String name)
1631 //  {
1632 //    return newViewFrom(null, name);
1633 //  }
1634 //
1635 //  @Override
1636 //  public AlignFrame newViewFrom(AlignFrame alf)
1637 //  {
1638 //    return newViewFrom(alf, null);
1639 //  }
1640 //
1641 //  @Override
1642 //  public AlignFrame newViewFrom(AlignFrame alf, String name)
1643 //  {
1644 //    if (alf == null)
1645 //    {
1646 //      alf = getCurrentAlignFrame();
1647 //    }
1648 //    return appLoader.newViewFrom(alf, name);
1649 //  }
1650
1651   @Override
1652   public AlignFrame loadAlignment(String text, String title)
1653   {
1654     return appLoader.loadAlignment(text, AlignFrame.DEFAULT_WIDTH,
1655             AlignFrame.DEFAULT_HEIGHT, title);
1656   }
1657
1658   @Override
1659   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
1660           String pdbEntryString, String pdbFile)
1661   {
1662     if (alFrame == null)
1663     {
1664       alFrame = getCurrentAlignFrame();
1665     }
1666     return appLoader.addPdbFile(alFrame, sequenceId, pdbEntryString,
1667             pdbFile);
1668   }
1669
1670   @Override
1671   public void scrollViewToIn(AlignFrame alf, String topRow,
1672           String leftHandColumn)
1673   {
1674     if (alf == null)
1675     {
1676       alf = getCurrentAlignFrame();
1677     }
1678     appLoader.scrollViewToIn(alf, topRow, leftHandColumn);
1679   }
1680
1681   @Override
1682   public void scrollViewToRowIn(AlignFrame alf, String topRow)
1683   {
1684     if (alf == null)
1685     {
1686       alf = getCurrentAlignFrame();
1687     }
1688     appLoader.scrollViewToRowIn(alf, topRow);
1689   }
1690
1691   @Override
1692   public void scrollViewToColumnIn(AlignFrame alf, String leftHandColumn)
1693   {
1694     if (alf == null)
1695     {
1696       alf = getCurrentAlignFrame();
1697     }
1698     appLoader.scrollViewToColumnIn(alf, leftHandColumn);
1699   }
1700
1701   @Override
1702   public String getFeatureGroups()
1703   {
1704     return getFeatureGroupsOn(null);
1705   }
1706
1707   @Override
1708   public String getFeatureGroupsOn(AlignFrame alf)
1709   {
1710     if (alf == null)
1711     {
1712       alf = getCurrentAlignFrame();
1713     }
1714     return appLoader.getFeatureGroupsOn(alf);
1715   }
1716
1717   @Override
1718   public String getFeatureGroupsOfState(boolean visible)
1719   {
1720     return getFeatureGroupsOfStateOn(null, visible);
1721   }
1722
1723   @Override
1724   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
1725   {
1726     if (alf == null)
1727     {
1728       alf = getCurrentAlignFrame();
1729     }
1730     return appLoader.getFeatureGroupsOfStateOn(alf, visible);
1731   }
1732
1733   @Override
1734   public void setFeatureGroupState(String groups, boolean state)
1735   { // JalviewLite API
1736     setFeatureGroupStateOn(null, groups, state);
1737   }
1738
1739   @Override
1740   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
1741           boolean state)
1742   {
1743     if (alf == null)
1744     {
1745       alf = getCurrentAlignFrame();
1746     }
1747     appLoader.setFeatureGroupStateOn(alf, groups, state);
1748   }
1749
1750   @Override
1751   public String getSeparator()
1752   {
1753     return appLoader.getSeparator();
1754   }
1755
1756   @Override
1757   public void setSeparator(String separator)
1758   {
1759     appLoader.setSeparator(separator);
1760   }
1761
1762   @Override
1763   public String getJsMessage(String messageclass, String viewId)
1764   {
1765     // see http://www.jalview.org/examples/jalviewLiteJs.html
1766     return null;
1767   }
1768
1769   /**
1770    * Open a new Tree panel on the desktop statically. Params are standard (not
1771    * set by Groovy). No dialog is opened.
1772    * 
1773    * @param af
1774    * @param treeType
1775    * @param modelName
1776    * @return null, or the string "label.you_need_at_least_n_sequences" if number
1777    *         of sequences selected is inappropriate
1778    */
1779   @Override
1780   public Object openTreePanel(AlignFrame af, String treeType,
1781           String modelName)
1782   { // JalviewJS api
1783     if (af == null)
1784     {
1785       af = getCurrentAlignFrame();
1786     }
1787     return CalculationChooser.openTreePanel(af, treeType, modelName, null);
1788   }
1789
1790   /**
1791    * public static method for JalviewJS API to open a PCAPanel without
1792    * necessarily using a dialog.
1793    * 
1794    * @param af
1795    * @param modelName
1796    * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
1797    *         if number of sequences selected is inappropriate
1798    */
1799   @Override
1800   public Object openPcaPanel(AlignFrame af, String modelName)
1801   {
1802     if (af == null)
1803     {
1804       af = getCurrentAlignFrame();
1805     }
1806     return CalculationChooser.openPcaPanel(af, modelName, null);
1807   }
1808
1809   @Override
1810   public String getSelectedSequencesAsAlignment(String format,
1811           boolean suffix)
1812   {
1813     return getSelectedSequencesAsAlignmentFrom(null,
1814             format, suffix);
1815   }
1816
1817   @Override
1818   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
1819           String format, boolean suffix)
1820   {
1821     if (alf == null)
1822     {
1823       alf = getCurrentAlignFrame();
1824     }
1825     return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format,
1826             "" + suffix);
1827   }
1828
1829   @Override
1830   public String arrayToSeparatorList(String[] array)
1831   {
1832     return appLoader.arrayToSeparatorList(array);
1833   }
1834
1835   @Override
1836   public String[] separatorListToArray(String list)
1837   {
1838     return appLoader.separatorListToArray(list);
1839   }
1840
1841   //// probably not needed in JalviewJS -- From when Jmol and Jalview did not
1842   //// have a direct connection?
1843
1844   @Override
1845   public void setMouseoverListener(String listener)
1846   {
1847     // TODO Auto-generated method stub
1848
1849   }
1850
1851   @Override
1852   public void setMouseoverListener(AlignFrame af, String listener)
1853   {
1854     // TODO Auto-generated method stub
1855
1856   }
1857
1858   @Override
1859   public void setSelectionListener(String listener)
1860   {
1861     // TODO Auto-generated method stub
1862
1863   }
1864
1865   @Override
1866   public void setSelectionListener(AlignFrame af, String listener)
1867   {
1868     // TODO Auto-generated method stub
1869
1870   }
1871
1872   @Override
1873   public void setStructureListener(String listener, String modelSet)
1874   {
1875     // TODO Auto-generated method stub
1876
1877   }
1878
1879   @Override
1880   public void removeJavascriptListener(AlignFrame af, String listener)
1881   {
1882     // TODO Auto-generated method stub
1883
1884   }
1885
1886   @Override
1887   public void mouseOverStructure(String pdbResNum, String chain,
1888           String pdbfile)
1889   {
1890     // TODO Auto-generated method stub
1891
1892   }
1893
1894   @Override
1895   public void showOverview()
1896   {
1897     currentAlignFrame.overviewMenuItem_actionPerformed(null);
1898   }
1899
1900   public void notifyWorker(AlignCalcWorkerI worker, String status)
1901   {
1902     // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
1903     // + " " + status);
1904   }
1905
1906   @Override
1907   public Object parseArguments(String[] args)
1908   {
1909     // TODO Auto-generated method stub
1910     return null;
1911   }
1912
1913
1914   
1915   
1916 }