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