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