JAL-3446 adds Jmol viewer embedding.
[jalview.git] / src / jalview / api / JalviewJSApp.java
1 package jalview.api;
2
3 import java.awt.EventQueue;
4 //import java.applet.AppletContext;
5 import java.io.IOException;
6 import java.net.URL;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.StringTokenizer;
10 import java.util.Vector;
11
12 import jalview.bin.ArgsParser;
13 import jalview.bin.Jalview;
14 import jalview.bin.JalviewJSApi;
15 import jalview.datamodel.Alignment;
16 import jalview.datamodel.AlignmentI;
17 import jalview.datamodel.AlignmentOrder;
18 import jalview.datamodel.ColumnSelection;
19 import jalview.datamodel.HiddenColumns;
20 import jalview.datamodel.PDBEntry;
21 import jalview.datamodel.Sequence;
22 import jalview.datamodel.SequenceGroup;
23 import jalview.datamodel.SequenceI;
24 import jalview.gui.AlignFrame;
25 import jalview.gui.AlignViewport;
26 import jalview.gui.CalculationChooser;
27 import jalview.gui.Desktop;
28 import jalview.gui.StructureViewer;
29 import jalview.io.AnnotationFile;
30 import jalview.io.AppletFormatAdapter;
31 import jalview.io.DataSourceType;
32 import jalview.io.FeaturesFile;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatI;
35 import jalview.io.FileFormats;
36 import jalview.io.IdentifyFile;
37 import jalview.io.JPredFile;
38 import jalview.io.JnetAnnotationMaker;
39 import jalview.io.NewickFile;
40 import jalview.structure.SelectionListener;
41 import jalview.structure.SelectionSource;
42 import jalview.structure.StructureSelectionManager;
43 import jalview.structure.VamsasSource;
44 import jalview.util.HttpUtils;
45 import jalview.util.MessageManager;
46 import jalview.util.Platform;
47
48 //import netscape.javascript.JSObject;
49
50 /**
51  * Basically the JalviewLite application, but without JalviewLite
52  * 
53  * @author hansonr
54  *
55  */
56 public class JalviewJSApp implements JalviewJSApi
57 {
58   private ArgsParser aparser;
59
60   private boolean debug;
61
62   private String[] ret = new String[1];
63
64   private String separator = "\u00AC"; // JalviewLite note: the default used to
65                                        // be '|', but many sequence IDS include
66                                        // pipes.
67
68
69   public JalviewJSApp(ArgsParser aparser)
70   {
71     this.aparser = aparser;
72     Platform.setAppClass(this);
73   }
74
75
76   public void load(AlignFrame af)
77   {
78     String sep = (String) getAppletParameter("separator", true);
79     if (sep != null)
80     {
81       if (sep.length() > 0)
82       {
83         separator = sep;
84       }
85       else
86       {
87         throw new Error(MessageManager
88                 .getString("error.invalid_separator_parameter"));
89       }
90     }
91
92     loadTree(af);
93     loadScoreFile();
94     loadFeatures(af);
95     loadAnnotations(af);
96     loadJnetFile(af);
97     loadPdbFiles(af);
98   }
99
100   // TODO BH 2019
101   //
102   // These are methods that are in JalviewLite that various classes call
103   // but are not in JalviewLiteJsApi. Or, even if they are, other classes
104   // call
105   // them to JalviewLite directly. Some may not be necessary, but they have
106   // to
107   // be at least mentioned here, or the classes calling them should
108   // reference
109   // JalviewLite itself.
110
111   // private boolean alignPDBStructures; // From JalviewLite; not implemented
112   //
113   
114   @Override
115   public String getParameter(String name)
116   {
117     return (String) getAppletParameter(name, true);
118   }
119
120   @Override
121   public Object getAppletParameter(String name, boolean asString)
122   {
123     return aparser.getAppletValue(name, null, asString);
124   }
125
126   /**
127    * Get the applet-like code base even though this is an application.
128    */
129
130   @Override
131   public URL getCodeBase()
132   {
133     return Platform.getCodeBase();
134   }
135
136   /**
137    * Get the applet-like document base even though this is an application.
138    */
139
140   @Override
141   public URL getDocumentBase()
142   {
143     return Platform.getDocumentBase();
144   }
145
146   @Override
147   public Object getFrameForSource(VamsasSource source)
148   {
149     if (source != null)
150     {
151       AlignFrame af;
152       if (source instanceof jalview.gui.AlignViewport
153               && source == (af = Jalview.getCurrentAlignFrame())
154                       .getViewport())
155       {
156         // should be valid if it just generated an event!
157         return af;
158       }
159       // TODO: ensure that if '_af' is specified along with a handler
160       // function, then only events from that alignFrame are sent to that
161       // function
162     }
163     return null;
164   }
165
166   @Override
167   public Object[] getSelectionForListener(SequenceGroup seqsel,
168           ColumnSelection colsel, HiddenColumns hidden,
169           SelectionSource source, Object alignFrame)
170   {
171     return getSelectionForListener(null, seqsel, colsel, hidden, source,
172             alignFrame);
173   }
174
175   /**
176    * scorefile
177    * 
178    */
179
180   @Override
181   public boolean loadScoreFile(String sScoreFile) throws IOException
182   {
183     return loadScoreFile(null, sScoreFile);
184   }
185   
186   public boolean loadScoreFile(AlignFrame af, String sScoreFile) throws IOException
187   {
188     (af == null ? Jalview.getCurrentAlignFrame() : af).loadJalviewDataFile(sScoreFile, null, null, null);
189     return true;
190   }
191
192   public void loadTree(AlignFrame af, NewickFile nf, String treeFile) throws IOException
193   {
194     if (af == null)
195       af = Jalview.getCurrentAlignFrame();
196     af.getViewport()
197             .setCurrentTree(af.showNewickTree(nf, treeFile).getTree());
198   }
199
200   @Override
201   public void newFeatureSettings()
202   {
203     System.err.println(
204             "Jalview applet interface newFeatureSettings not implemented");
205   }
206
207   //
208   //
209   // public void setAlignPdbStructures(boolean defaultParameter)
210   // {
211   // alignPDBStructures = true;
212   // }
213   //
214
215   @Override
216   public void newStructureView(PDBEntry pdb, SequenceI[] seqs,
217           String[] chains, DataSourceType protocol)
218   {
219     newStructureView(null, pdb, seqs, chains, protocol);
220     
221   }
222
223   /**
224    * @j2sAlias showStructure
225    */
226   @Override
227   public void showStructure(AlignFrame af, String pdbID, String fileType) {
228     if (af == null)
229       af = Jalview.getCurrentAlignFrame();
230     PDBEntry pe = null;
231     SequenceI[] seqs = null;
232     if (pdbID == null) {
233       seqs = af.getViewport().getSequenceSelection();
234       if (seqs.length == 0)
235         seqs = af.getViewport().getAlignment().getSequencesArray();
236       for (int i = 0; i < seqs.length; i++) {
237         Vector<PDBEntry> list = seqs[i].getAllPDBEntries();
238         if (list.size() > 0) {
239           pe = list.get(0);
240           break;
241         }
242       }
243     }
244     if (pe == null) {
245       if (pdbID == null)
246         return;
247       pe = new PDBEntry(pdbID, null, fileType);
248       List<SequenceI> list = af.getViewport().getAlignment().getSequences();
249       List<SequenceI> tmp = new ArrayList<SequenceI>();
250       for (int i = 0; i < list.size(); i++) {
251         SequenceI seq = list.get(i);
252         if (seq.getPDBEntry(pdbID) != null) {
253           tmp.add(seq);
254         }
255       }
256       seqs = tmp.toArray(new SequenceI[tmp.size()]);
257       af.alignPanel.selectSequences(tmp);
258     }
259     StructureViewer.launchStructureViewer(
260             af.alignPanel,
261             pe, seqs);
262   }
263   
264   public void newStructureView(AlignFrame af, PDBEntry pdb,
265           SequenceI[] seqs, String[] chains, DataSourceType protocol)
266   {
267     StructureViewer.launchStructureViewer(
268             (af == null ? Jalview.getCurrentAlignFrame() : af).alignPanel,
269             pdb, seqs);
270   }
271
272   /**
273    * features
274    * @param af 
275    * 
276    */
277
278   @Override
279   public boolean parseFeaturesFile(String filename, DataSourceType protocol)
280   {
281     return parseFeaturesFile(null, filename, protocol);
282   }
283   
284   /**
285    * @j2sAlias parseFeatureFile
286    * 
287    * @param af
288    * @param filename
289    * @param protocol
290    * @return
291    */
292   public boolean parseFeaturesFile(AlignFrame af, String filename, DataSourceType protocol)
293   {
294     return (af == null ? Jalview.getCurrentAlignFrame() : af).parseFeaturesFile(filename, protocol);
295   }
296
297   /**
298    * annotations, jpredfile, jnetfile
299    * 
300    */
301
302   @Override
303   public void updateForAnnotations()
304   {
305     updateForAnnotations(null);
306   }
307
308   public void updateForAnnotations(AlignFrame af)
309   {
310     (af == null ? Jalview.getCurrentAlignFrame() : af).updateForAnnotations();
311   }
312
313   @Override
314   public boolean addPdbFile(AlignFrame alf, String sequenceId, String pdbId,
315           String pdbFile)
316   {
317     if (alf == null)
318     {
319       alf = Jalview.getCurrentAlignFrame();
320     }
321     SequenceI seq = alf.getViewport().getAlignment().findName(sequenceId);
322     if (seq != null)
323     {
324       Vector<PDBEntry> pdbe = seq.getAllPDBEntries();
325       PDBEntry pdbentry = null;
326       if (pdbe != null && pdbe.size() > 0)
327       {
328         for (int pe = 0, peSize = pdbe.size(); pe < peSize; pe++)
329         {
330           pdbentry = pdbe.elementAt(pe);
331           if (!pdbentry.getId().equals(pdbId)
332                   || pdbFile != null && !pdbentry.getFile().equals(pdbFile))
333           {
334             pdbentry = null;
335           }
336         }
337       }
338       if (pdbentry == null)
339       {
340         pdbentry = new PDBEntry(pdbId, null, pdbFile);
341         if (pdbFile != null)
342         {
343           DataSourceType protocol = AppletFormatAdapter
344                   .resolveProtocol(pdbFile, FileFormat.PDB);
345           if (protocol == null)
346             return false;
347           pdbentry.setProperty("protocol", protocol);
348         }
349         seq.addPDBId(pdbentry);
350         alf.alignPanel.getStructureSelectionManager()
351                 .registerPDBEntry(pdbentry);
352       }
353     }
354     return true;
355   }
356
357 //  @Override
358 //  public String arrayToSeparatorList(String[] array)
359 //  {
360 //    return arrayToSeparatorList(array, separator);
361 //  }
362
363 //  /**
364 //   * concatenate the list with separator
365 //   * 
366 //   * @param list
367 //   * @param separator
368 //   * @return concatenated string
369 //   */
370 //  public static String arrayToSeparatorList(String[] list, String separator)
371 //  {
372 //    // TODO use StringUtils version
373 //    StringBuffer v = new StringBuffer();
374 //    if (list != null && list.length > 0)
375 //    {
376 //      for (int i = 0, iSize = list.length; i < iSize; i++)
377 //      {
378 //        if (list[i] != null)
379 //        {
380 //          if (i > 0)
381 //          {
382 //            v.append(separator);
383 //          }
384 //          v.append(list[i]);
385 //        }
386 //      }
387 //      // if (debug)
388 //      // {
389 //      // System.err
390 //      // .println("Returning '" + separator + "' separated List:\n");
391 //      // System.err.println(v);
392 //      // }
393 //      return v.toString();
394 //    }
395 //    // if (debug)
396 //    // {
397 //    // System.err.println(
398 //    // "Returning empty '" + separator + "' separated List\n");
399 //    // }
400 //    return "" + separator;
401 //  }
402
403   @Override
404   public String getAlignment(String format)
405   {
406     return getAlignmentFrom(null, format, null);
407   }
408
409   /**
410    * suffix string "true"/"false" (default true)
411    *          passed to AlnFile class controls whether /START-END is added to
412    *          sequence names
413    */
414   @Override
415   public String getAlignment(String format, String suffix)
416   {
417     return getAlignmentFrom(Jalview.getCurrentAlignFrame(), format, suffix);
418   }
419
420   @Override
421   public String getAlignmentFrom(AlignFrame alf, String format)
422   {
423     return getAlignmentFrom(alf, format, null);
424   }
425
426   @Override
427   public String getAlignmentFrom(AlignFrame alf, String format,
428           String suffix)
429   {
430     try
431     {
432       if (alf == null)
433       {
434         alf = Jalview.getCurrentAlignFrame();
435       }
436       boolean seqlimits = (suffix == null
437               || suffix.equalsIgnoreCase("true"));
438
439       FileFormatI theFormat = FileFormats.getInstance().forName(format);
440       String reply = new AppletFormatAdapter().formatSequences(theFormat,
441               alf.getViewport().getAlignment(), seqlimits);
442       return reply;
443     } catch (IllegalArgumentException ex)
444     {
445       ex.printStackTrace();
446       return "Error retrieving alignment, possibly invalid format specifier: "
447               + format;
448     }
449   }
450
451   @Override
452   public String getAlignmentOrder()
453   {
454     return getAlignmentFrom(Jalview.getCurrentAlignFrame(), null);
455   }
456
457   @Override
458   public String[] getAlignmentOrderFrom(AlignFrame alf)
459   {
460     if (alf == null)
461     {
462       alf = Jalview.getCurrentAlignFrame();
463     }
464     AlignmentI alorder = alf.getViewport().getAlignment();
465     String[] order = new String[alorder.getHeight()];
466     for (int i = 0; i < order.length; i++)
467     {
468       order[i] = alorder.getSequenceAt(i).getName();
469     }
470     return order;//  arrayToSeparatorList(order, sep);
471   }
472
473   @Override
474   public String getAnnotation()
475   {
476     return getAnnotationFrom(null);
477   }
478
479   @Override
480   public String getAnnotationFrom(AlignFrame alf)
481   {
482     if (alf == null)
483     {
484       alf = Jalview.getCurrentAlignFrame();
485     }
486     String annotation = new AnnotationFile()
487             .printAnnotationsForView(alf.getViewport());
488     return annotation;
489   }
490
491   @Override
492   public String[] getFeatureGroups()
493   {
494     return getFeatureGroupsOn(null);
495   }
496
497   @Override
498   public String[] getFeatureGroupsOfState(boolean visible)
499   {
500     return getFeatureGroupsOfStateOn(null, visible);
501   }
502
503   @Override
504   public String[] getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
505   {
506     if (alf == null)
507     {
508       alf = Jalview.getCurrentAlignFrame();
509     }
510     return alf.getFeatureGroupsOfState(visible);
511   }
512
513   @Override
514   public String[] getFeatureGroupsOn(AlignFrame alf)
515   {
516     if (alf == null)
517     {
518       alf = Jalview.getCurrentAlignFrame();
519     }
520     return alf.getFeatureGroups();
521   }
522
523   @Override
524   public String getFeatures(String format)
525   {
526     return getFeaturesFrom(null, format);
527   }
528
529   /**
530    * JavaScript interface to print the alignment frame
531    * 
532    * @param alf
533    * @param format
534    *          "jalview" or "gff" with or without ";includeComplement" or
535    *          ";includeNonpositional"; default with no ";" is
536    *          ";includeNonpositional"
537    * @return
538    */
539   @Override
540   public String getFeaturesFrom(AlignFrame alf, String format)
541   {
542     if (alf == null)
543     {
544       alf = Jalview.getCurrentAlignFrame();
545     }
546     String features;
547     FeaturesFile formatter = new FeaturesFile();
548     format = format.toLowerCase();
549     if (format.indexOf(";") < 0)
550       format += ";includenonpositional";
551     boolean nonpos = format.indexOf(";includenonpositional") > 0;
552     boolean compl = format.indexOf(";includecomplement") >= 0;
553     if (format.startsWith("jalview"))
554     {
555       features = formatter.printJalviewFormat(
556               alf.getViewport().getAlignment().getSequencesArray(),
557               alf.alignPanel.getFeatureRenderer(), nonpos, compl);
558     }
559     else
560     {
561       features = formatter.printGffFormat(
562               alf.getViewport().getAlignment().getSequencesArray(),
563               alf.alignPanel.getFeatureRenderer(), nonpos, compl);
564     }
565
566     if (features == null)
567     {
568       features = "";
569     }
570     return features;
571
572   }
573   
574   /**
575    * read sequence1...sequenceN as a raw alignment
576    * 
577    * @param jalviewApp
578    * @return
579    */
580   public String getPastedSequence(JalviewJSApp jalviewApp)
581   {
582     StringBuffer data = new StringBuffer("PASTE");
583     int i = 1;
584     String file = null;
585     while ((file = (String) getAppletParameter("sequence" + i,
586             true)) != null)
587     {
588       data.append(file.toString() + "\n");
589       i++;
590     }
591     if (data.length() > 5)
592     {
593       file = data.toString();
594     }
595     return file;
596   }
597
598   /**
599    * 
600    * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequences()
601    */
602   @Override
603   public SequenceI[] getSelectedSequences()
604   {
605     return getSelectedSequencesFrom(Jalview.getCurrentAlignFrame());
606   }
607
608   /**
609    * 
610    * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
611    */
612   @Override
613   public SequenceI[] getSelectedSequences(String sep)
614   {
615     return getSelectedSequencesFrom(Jalview.getCurrentAlignFrame(), sep);
616   }
617
618   @Override
619   public String getSelectedSequencesAsAlignment(String format,
620           String suffix)
621   {
622     return getSelectedSequencesAsAlignmentFrom(null, format, suffix);
623   }
624
625   @Override
626   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
627           String format, String suffix)
628   {
629
630     if (alf == null)
631     {
632       alf = Jalview.getCurrentAlignFrame();
633     }
634
635     boolean seqlimits = (suffix == null || suffix.equalsIgnoreCase("true"));
636     try
637     {
638       AlignViewport vp = alf.getViewport();
639       FileFormatI theFormat = FileFormats.getInstance().forName(format);
640       if (vp.getSelectionGroup() != null)
641       {
642         // JBPNote: getSelectionAsNewSequence behaviour has changed - this
643         // method now returns a full copy of sequence data
644         // TODO consider using getSequenceSelection instead here
645         String reply = new AppletFormatAdapter().formatSequences(theFormat,
646                 new Alignment(vp.getSelectionAsNewSequence()), seqlimits);
647         return reply;
648       }
649     } catch (IllegalArgumentException ex)
650     {
651       ex.printStackTrace();
652       return "Error retrieving alignment, possibly invalid format specifier: "
653               + format;
654     }
655     return "";
656   }
657
658   /**
659    * 
660    * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
661    *      .AlignFrame)
662    */
663   @Override
664   public SequenceI[] getSelectedSequencesFrom(AlignFrame alf)
665   {
666     return getSelectedSequencesFrom(alf, null);
667   }
668
669   @Override
670   public SequenceI[] getSelectedSequencesFrom(AlignFrame alf, String sep)
671   {
672     if (alf == null)
673     {
674       alf = Jalview.getCurrentAlignFrame();
675     }
676     AlignViewport v = alf.getViewport();
677     if (v.getSelectionGroup() != null)
678     {
679       return v.getSelectionGroup()
680               .getSequencesInOrder(v.getAlignment());
681     }
682
683     return null;
684   }
685
686   public Object[] getSelectionForListener(AlignFrame alf,
687           SequenceGroup seqsel, ColumnSelection colsel,
688           HiddenColumns hidden, SelectionSource source, Object alignFrame)
689   {
690     if (alf == null)
691     {
692       alf = Jalview.getCurrentAlignFrame();
693     }
694     // System.err.println("Testing selection event relay to
695     // jsfunction:"+_listener);
696     String setid = "";
697     AlignFrame src = (AlignFrame) alignFrame;
698     if (source != null)
699     {
700       if (source instanceof AlignViewport && alf.getViewport() == source)
701       {
702         // should be valid if it just generated an event!
703         src = alf;
704
705       }
706     }
707     String[] seqs = new String[] {};
708     String[] cols = new String[] {};
709     int strt = 0, end = (src == null) ? -1
710             : src.alignPanel.av.getAlignment().getWidth();
711     if (seqsel != null && seqsel.getSize() > 0)
712     {
713       seqs = new String[seqsel.getSize()];
714       for (int i = 0; i < seqs.length; i++)
715       {
716         seqs[i] = seqsel.getSequenceAt(i).getName();
717       }
718       if (strt < seqsel.getStartRes())
719       {
720         strt = seqsel.getStartRes();
721       }
722       if (end == -1 || end > seqsel.getEndRes())
723       {
724         end = seqsel.getEndRes();
725       }
726     }
727     if (colsel != null && !colsel.isEmpty())
728     {
729       if (end == -1)
730       {
731         end = colsel.getMax() + 1;
732       }
733       cols = new String[colsel.getSelected().size()];
734       for (int i = 0; i < cols.length; i++)
735       {
736         cols[i] = "" + (1 + colsel.getSelected().get(i).intValue());
737       }
738     }
739     else
740     {
741       if (seqsel != null && seqsel.getSize() > 0)
742       {
743         // send a valid range, otherwise we send the empty selection
744         cols = new String[2];
745         cols[0] = "" + (1 + strt) + "-" + (1 + end);
746       }
747     }
748     return new Object[] { src, setid, seqs, cols };
749   }
750
751   /**
752    * 
753    * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
754    *      .AlignFrame, java.lang.String)
755    */
756   @Override
757   public void highlight(String sequenceId, String position,
758           String alignedPosition)
759   {
760     highlightIn(null, sequenceId, position, alignedPosition);
761   }
762
763   @Override
764   public void highlightIn(AlignFrame alf, final String sequenceId,
765           final String position, final String alignedPosition)
766   {
767     if (alf == null)
768     {
769       alf = Jalview.getCurrentAlignFrame();
770     }
771     // TODO: could try to highlight in all alignments if alf==null
772     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
773             alf.getViewport().getAlignment().getSequencesArray());
774     final SequenceI sq = matcher.findIdMatch(sequenceId);
775     if (sq != null)
776     {
777       int apos = -1;
778       try
779       {
780         apos = Integer.valueOf(position).intValue();
781         apos--;
782       } catch (NumberFormatException ex)
783       {
784         return;
785       }
786       final int pos = apos;
787       // use vamsas listener to broadcast to all listeners in scope
788       if (alignedPosition != null && (alignedPosition.trim().length() == 0
789               || alignedPosition.toLowerCase().indexOf("false") > -1))
790       {
791         java.awt.EventQueue.invokeLater(new Runnable()
792         {
793           @Override
794           public void run()
795           {
796             StructureSelectionManager
797                     .getStructureSelectionManager(Desktop.getInstance())
798                     .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
799           }
800         });
801       }
802       else
803       {
804         java.awt.EventQueue.invokeLater(new Runnable()
805         {
806           @Override
807           public void run()
808           {
809             StructureSelectionManager
810                     .getStructureSelectionManager(Desktop.getInstance())
811                     .mouseOverVamsasSequence(sq, pos, null);
812           }
813         });
814       }
815     }
816   }
817
818   public AlignFrame loadAlignment(String text, int width, int height,
819           String title)
820   {
821     AlignmentI al = null;
822
823     try
824     {
825       FileFormatI format = new IdentifyFile().identify(text,
826               DataSourceType.PASTE);
827       al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE,
828               format);
829       if (al.getHeight() > 0)
830       {
831         return new AlignFrame(al, width, height, title);
832       }
833     } catch (IOException ex)
834     {
835       ex.printStackTrace();
836     }
837     return null;
838   }
839
840   // public void setMouseoverListener(String listener)
841   // {
842   // appLoader.setMouseoverListener(listener);
843   // }
844   //
845   //
846   // public void setMouseoverListener(AlignFrame af, String listener)
847   // {
848   // }
849   //
850
851   @Override
852   public AlignFrame loadAlignment(String text, String title)
853   {
854     return loadAlignment(text, AlignFrame.DEFAULT_WIDTH,
855             AlignFrame.DEFAULT_HEIGHT, title);
856   }
857
858   @Override
859   public void loadAnnotation(String annotation)
860   {
861     loadAnnotationFrom(null, annotation);
862   }
863
864   @Override
865   public void loadAnnotationFrom(AlignFrame alf, String annotation)
866   {
867     if (alf == null)
868     {
869       alf = Jalview.getCurrentAlignFrame();
870     }
871     if (new AnnotationFile().annotateAlignmentView(alf.getViewport(),
872             annotation, DataSourceType.PASTE))
873     {
874       alf.alignPanel.fontChanged();
875       alf.alignPanel.setScrollValues(0, 0);
876     }
877     else
878     {
879       alf.parseFeaturesFile(annotation, DataSourceType.PASTE);
880     }
881   }
882
883   /**
884    * Load annotations if specified by parameter. Returns true if loaded, else
885    * false.
886    * 
887    * 
888    * @param alignFrame
889    * @return
890    */
891   protected boolean loadAnnotations(AlignFrame af)
892   {
893     boolean result = false;
894     String param = (String) getAppletParameter("annotations", true);
895     if (param != null)
896     {
897       ret[0] = param;
898       DataSourceType protocol = resolveFileProtocol(ret);
899       param = ret[0];
900       if (new AnnotationFile().annotateAlignmentView(af.getViewport(), param,
901               protocol))
902       {
903         updateForAnnotations();
904         result = true;
905       }
906       else
907       {
908         System.err
909                 .println("Annotations were not added from annotation file '"
910                         + param + "'");
911       }
912     }
913     return result;
914   }
915
916   //// JalviewJSApi
917
918   /**
919    * Load features file and view settings as specified by parameters. Returns
920    * true if features were loaded, else false.
921    * @param  
922    * 
923    * @param alignFrame
924    * @return
925    */
926   protected boolean loadFeatures(AlignFrame af)
927   {
928     boolean result = false;
929     // ///////////////////////////
930     // modify display of features
931     // we do this before any features have been loaded, ensuring any hidden
932     // groups are hidden when features first displayed
933     //
934     // hide specific groups
935     //
936     String param = (String) getAppletParameter("hidefeaturegroups", true);
937     if (param != null)
938     {
939       setFeatureGroupState(af, separatorListToArray(param, separator), false);
940       // setFeatureGroupStateOn(newAlignFrame, param, false);
941     }
942     // show specific groups
943     param = (String) getAppletParameter("showfeaturegroups", true);
944     if (param != null)
945     {
946       setFeatureGroupState(af, separatorListToArray(param, separator), true);
947       // setFeatureGroupStateOn(newAlignFrame, param, true);
948     }
949     // and now load features
950     param = (String) getAppletParameter("features", true);
951     if (param != null)
952     {
953       ret[0] = param;
954       DataSourceType protocol = resolveFileProtocol(ret);
955
956       result = parseFeaturesFile(af, ret[0], protocol);
957     }
958
959     param = (String) getAppletParameter("showFeatureSettings", true);
960     if (param != null && param.equalsIgnoreCase("true"))
961     {
962       newFeatureSettings();
963     }
964     return result;
965   }
966
967   @Override
968   public void loadFeatures(String features, boolean autoenabledisplay)
969   {
970     loadFeaturesFrom(null, features, autoenabledisplay);
971   }
972
973   @Override
974   public boolean loadFeaturesFrom(AlignFrame alf, String features,
975           boolean autoenabledisplay)
976   {
977     if (alf == null)
978     {
979       alf = Jalview.getCurrentAlignFrame();
980     }
981     boolean ret = alf.parseFeaturesFile(features, DataSourceType.PASTE);
982     if (!ret)
983     {
984       return false;
985     }
986     if (autoenabledisplay)
987     {
988       alf.getViewport().setShowSequenceFeatures(true);
989       // this next was for a checkbox in JalviewLite
990       // ((AlignFrame) alf).getViewport().sequenceFeatures.setState(true);
991     }
992     return true;
993   }
994
995   /**
996    * Load in a Jnetfile if specified by parameter. Returns true if loaded, else
997    * false.
998    * 
999    * @param alignFrame
1000    * @return
1001    */
1002   protected boolean loadJnetFile(AlignFrame af)
1003   {
1004     boolean result = false;
1005     String param = (String) getAppletParameter("jnetfile", true);
1006     if (param == null)
1007     {
1008       // jnet became jpred around 2016
1009       param = (String) getAppletParameter("jpredfile", true);
1010     }
1011     if (param != null)
1012     {
1013       try
1014       {
1015         ret[0] = param;
1016         DataSourceType protocol = resolveFileProtocol(ret);
1017         JPredFile predictions = new JPredFile(ret[0], protocol);
1018         JnetAnnotationMaker.add_annotation(predictions,
1019                 af.getViewport().getAlignment(), 0, false);
1020         // false == do not add sequence profile from concise output
1021         af.getViewport().getAlignment().setupJPredAlignment();
1022         updateForAnnotations();
1023         result = true;
1024       } catch (Exception ex)
1025       {
1026         ex.printStackTrace();
1027       }
1028     }
1029     return result;
1030   }
1031
1032   /**
1033    * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
1034    * else false.
1035    * 
1036    * @param loaderFrame
1037    * @return
1038    */
1039   protected boolean loadPdbFiles(AlignFrame af)
1040   {
1041     boolean result = false;
1042     /*
1043      * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
1044      * related to JAL-434
1045      */
1046
1047     // not supported (as for JalviewLite)
1048     // boolean doAlign = false;//"true".equalsIgnoreCase("" +
1049     // getAppletParameter("alignpdbfiles", false));
1050     // setAlignPdbStructures(doAlign);
1051     /*
1052      * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
1053      * PDB|1GAQ|1GAQ|C">
1054      * 
1055      * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
1056      * 
1057      * <param name="PDBfile3" value="1q0o Q45135_9MICO">
1058      */
1059
1060     // Accumulate pdbs here if they are heading for the same view (if
1061     // alignPdbStructures is true)
1062     // ArrayList<Object[]> pdbs = new ArrayList<>();
1063     // create a lazy matcher if we're asked to
1064     boolean relaxed = "true".equalsIgnoreCase(
1065             "" + getAppletParameter("relaxedidmatch", false));
1066     jalview.analysis.SequenceIdMatcher matcher = relaxed
1067             ? new jalview.analysis.SequenceIdMatcher(
1068                     af.getViewport().getAlignment().getSequencesArray())
1069             : null;
1070
1071     int pdbFileCount = 0;
1072     String param;
1073     do
1074     {
1075       if (pdbFileCount > 0)
1076       {
1077         param = (String) getAppletParameter("PDBFILE" + pdbFileCount, true);
1078       }
1079       else
1080       {
1081         param = (String) getAppletParameter("PDBFILE", true);
1082       }
1083
1084       if (param != null)
1085       {
1086         PDBEntry pdb = new PDBEntry();
1087
1088         String seqstring;
1089         SequenceI[] seqs = null;
1090         String[] chains = null;
1091
1092         StringTokenizer st = new StringTokenizer(param, " ");
1093
1094         if (st.countTokens() < 2)
1095         {
1096           String sequence = (String) getAppletParameter("PDBSEQ", true);
1097           if (sequence != null)
1098           {
1099             seqs = new SequenceI[] { matcher == null
1100                     ? (Sequence) af.getViewport().getAlignment()
1101                             .findName(sequence)
1102                     : matcher.findIdMatch(sequence) };
1103           }
1104
1105         }
1106         else
1107         {
1108           param = st.nextToken();
1109           List<SequenceI> tmp = new ArrayList<>();
1110           List<String> tmp2 = new ArrayList<>();
1111
1112           while (st.hasMoreTokens())
1113           {
1114             seqstring = st.nextToken();
1115             StringTokenizer st2 = new StringTokenizer(seqstring, "=");
1116             if (st2.countTokens() > 1)
1117             {
1118               // This is the chain
1119               tmp2.add(st2.nextToken());
1120               seqstring = st2.nextToken();
1121             }
1122             tmp.add(matcher == null
1123                     ? (Sequence) af.getViewport().getAlignment()
1124                             .findName(seqstring)
1125                     : matcher.findIdMatch(seqstring));
1126           }
1127
1128           seqs = tmp.toArray(new SequenceI[tmp.size()]);
1129           if (tmp2.size() == tmp.size())
1130           {
1131             chains = tmp2.toArray(new String[tmp2.size()]);
1132           }
1133         }
1134         pdb.setId(param);
1135         ret[0] = param;
1136         DataSourceType protocol = resolveFileProtocol(ret);
1137         // TODO check JAL-357 for files in a jar (CLASSLOADER)
1138         pdb.setFile(ret[0]);
1139
1140         if (seqs != null)
1141         {
1142           for (int i = 0; i < seqs.length; i++)
1143           {
1144             if (seqs[i] != null)
1145             {
1146               ((Sequence) seqs[i]).addPDBId(pdb);
1147               StructureSelectionManager
1148                       .getStructureSelectionManager(
1149                               (StructureSelectionManagerProvider) this)
1150                       .registerPDBEntry(pdb);
1151             }
1152             else
1153             {
1154               if (debug)
1155               {
1156                 // this may not really be a problem but we give a warning
1157                 // anyway
1158                 System.err.println(
1159                         "Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
1160                                 + i + ")");
1161               }
1162             }
1163           }
1164
1165           // if (doAlign)
1166           // {
1167           // pdbs.add(new Object[] { pdb, seqs, chains, protocol });
1168           // }
1169           // else
1170           {
1171             newStructureView(af, pdb, seqs, chains, protocol);
1172           }
1173         }
1174       }
1175
1176       pdbFileCount++;
1177     } while (param != null || pdbFileCount < 10);
1178     //
1179     // if (doAlign && pdbs.size() > 0)
1180     // {
1181     // SequenceI[][] seqs = new SequenceI[pdbs.size()][];
1182     // PDBEntry[] pdb = new PDBEntry[pdbs.size()];
1183     // String[][] chains = new String[pdbs.size()][];
1184     // String[] protocols = new String[pdbs.size()];
1185     // for (int pdbsi = 0, pdbsiSize = pdbs
1186     // .size(); pdbsi < pdbsiSize; pdbsi++)
1187     // {
1188     // Object[] o = pdbs.get(pdbsi);
1189     // pdb[pdbsi] = (PDBEntry) o[0];
1190     // seqs[pdbsi] = (SequenceI[]) o[1];
1191     // chains[pdbsi] = (String[]) o[2];
1192     // protocols[pdbsi] = (String) o[3];
1193     // }
1194     //// alignedStructureView(pdb, seqs, chains, protocols);
1195     // result = true;
1196     // }
1197     return result;
1198   }
1199
1200   /**
1201    * Load a score file if specified by parameter. Returns true if file was
1202    * loaded, else false.
1203    * 
1204    * @param loaderFrame
1205    */
1206   protected boolean loadScoreFile()
1207   {
1208     boolean result = false;
1209     String sScoreFile = (String) getAppletParameter("scoreFile", true);
1210     if (sScoreFile != null && !"".equals(sScoreFile))
1211     {
1212       try
1213       {
1214         if (debug)
1215         {
1216           System.err.println(
1217                   "Attempting to load T-COFFEE score file from the scoreFile parameter");
1218         }
1219         result = loadScoreFile(sScoreFile);
1220         if (!result)
1221         {
1222           System.err.println(
1223                   "Failed to parse T-COFFEE parameter as a valid score file ('"
1224                           + sScoreFile + "')");
1225         }
1226       } catch (Exception e)
1227       {
1228         System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
1229                 sScoreFile, e.getMessage());
1230       }
1231     }
1232     return result;
1233   }
1234
1235   /**
1236    * Load a tree for the alignment if specified by parameter. Returns true if a
1237    * tree was loaded, else false.
1238    * 
1239    * @return
1240    */
1241   protected boolean loadTree(AlignFrame af)
1242   {
1243     boolean result = false;
1244     String treeFile = (String) getAppletParameter("tree", true);
1245     if (treeFile == null)
1246     {
1247       treeFile = (String) getAppletParameter("treefile", true);
1248     }
1249
1250     if (treeFile != null)
1251     {
1252       try
1253       {
1254         ret[0] = treeFile;
1255         NewickFile fin = new NewickFile(treeFile, resolveFileProtocol(ret));
1256         fin.parse();
1257
1258         if (fin.getTree() != null)
1259         {
1260           loadTree(af, fin, ret[0]);
1261           result = true;
1262           if (debug)
1263           {
1264             System.out.println("Successfully imported tree.");
1265           }
1266         }
1267         else
1268         {
1269           if (debug)
1270           {
1271             System.out.println(
1272                     "Tree parameter did not resolve to a valid tree.");
1273           }
1274         }
1275       } catch (Exception ex)
1276       {
1277         ex.printStackTrace();
1278       }
1279     }
1280     return result;
1281   }
1282
1283   /**
1284    * @j2sAlias openPcaPanel
1285    * 
1286    * public static method for JalviewJS API to open a PCAPanel without
1287    * necessarily using a dialog.
1288    * 
1289    * @param af
1290    * @param modelName
1291    * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
1292    *         if number of sequences selected is inappropriate
1293    */
1294   @Override
1295   public Object openPcaPanel(AlignFrame af, String modelName)
1296   {
1297     if (af == null)
1298     {
1299       af = Jalview.getCurrentAlignFrame();
1300     }
1301     return CalculationChooser.openPcaPanel(af, modelName, null);
1302   }
1303
1304   /**
1305    * @j2sAlias openTreePanel
1306    * 
1307    * Open a new Tree panel on the desktop statically. Params are standard (not
1308    * set by Groovy). No dialog is opened.
1309    * 
1310    * @param af
1311    * @param treeType
1312    * @param modelName
1313    * @return null, or the string "label.you_need_at_least_n_sequences" if number
1314    *         of sequences selected is inappropriate
1315    */
1316   @Override
1317   public Object openTreePanel(AlignFrame af, String treeType,
1318           String modelName)
1319   {
1320     if (af == null)
1321     {
1322       af = Jalview.getCurrentAlignFrame();
1323     }
1324     return CalculationChooser.openTreePanel(af, treeType, modelName, null);
1325   }
1326
1327
1328   @Override
1329   public String orderBy(String[] ids, String undoName)
1330   {
1331     return orderAlignmentBy(null, ids, undoName);
1332   }
1333
1334   @Override
1335   public String orderAlignmentBy(AlignFrame alf, String[] ids,
1336           String undoName)
1337   {
1338     if (alf == null)
1339       alf = Jalview.getCurrentAlignFrame();
1340     SequenceI[] sqs = null;
1341     if (ids != null && ids.length > 0)
1342     {
1343       jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
1344               alf.getViewport().getAlignment().getSequencesArray());
1345       int s = 0;
1346       sqs = new SequenceI[ids.length];
1347       for (int i = 0; i < ids.length; i++)
1348       {
1349         if (ids[i].trim().length() == 0)
1350         {
1351           continue;
1352         }
1353         SequenceI sq = matcher.findIdMatch(ids[i]);
1354         if (sq != null)
1355         {
1356           sqs[s++] = sq;
1357         }
1358       }
1359       if (s > 0)
1360       {
1361         SequenceI[] sqq = new SequenceI[s];
1362         System.arraycopy(sqs, 0, sqq, 0, s);
1363         sqs = sqq;
1364       }
1365       else
1366       {
1367         sqs = null;
1368       }
1369     }
1370     if (sqs == null)
1371     {
1372       return "";
1373     }
1374     ;
1375     final AlignmentOrder aorder = new AlignmentOrder(sqs);
1376
1377     if (undoName != null && undoName.trim().length() == 0)
1378     {
1379       undoName = null;
1380     }
1381     final String _undoName = undoName;
1382     // TODO: deal with synchronization here: cannot raise any events until after
1383     // this has returned.
1384     return alf.sortBy(aorder, _undoName) ? "true" : "";
1385   }
1386
1387   /**
1388    * Allow an outside entity to initiate the second half of argument parsing
1389    * (only).
1390    * 
1391    * @param args
1392    * @return null is good
1393    */
1394   @Override
1395   public Object parseArguments(String[] args)
1396   {
1397
1398     try
1399     {
1400       Jalview.getInstance().parseArguments(new ArgsParser(args), false);
1401       return null;
1402     } catch (Throwable t)
1403     {
1404       return t;
1405     }
1406   }
1407
1408   @Override
1409   public void removeSelectionListener(AlignFrame af, String listener)
1410   {
1411
1412     List<SelectionListener> listeners = Desktop
1413             .getStructureSelectionManager().getListeners();
1414     for (int i = listeners.size(); --i >= 0;)
1415     {
1416       SelectionListener l = listeners.get(i);
1417       if (l instanceof JsSelectionListener
1418               && ((JsSelectionListener) l).isFor(af, listener))
1419       {
1420         listeners.remove(i);
1421         break;
1422       }
1423     }
1424   }
1425
1426   @Override
1427   public void scrollViewToColumnIn(final AlignFrame alf,
1428           final String leftHandColumn)
1429   {
1430     java.awt.EventQueue.invokeLater(new Runnable()
1431     {
1432
1433       @Override
1434       public void run()
1435       {
1436         try
1437         {
1438           (alf == null ? Jalview.getCurrentAlignFrame() : alf)
1439                   .scrollToColumn(
1440                           Integer.valueOf(leftHandColumn).intValue());
1441
1442         } catch (Exception ex)
1443         {
1444           System.err.println(
1445                   "Couldn't parse integer arguments (leftHandColumn='"
1446                           + leftHandColumn + "')");
1447           ex.printStackTrace();
1448         }
1449       }
1450     });
1451
1452   }
1453
1454   @Override
1455   public void scrollViewToIn(final AlignFrame alf, final String topRow,
1456           final String leftHandColumn)
1457   {
1458     // TODO test
1459     java.awt.EventQueue.invokeLater(new Runnable()
1460     {
1461       @Override
1462       public void run()
1463       {
1464         try
1465         {
1466           (alf == null ? Jalview.getCurrentAlignFrame() : alf).scrollTo(
1467                   Integer.valueOf(topRow).intValue(),
1468                   Integer.valueOf(leftHandColumn).intValue());
1469
1470         } catch (Exception ex)
1471         {
1472           System.err.println("Couldn't parse integer arguments (topRow='"
1473                   + topRow + "' and leftHandColumn='" + leftHandColumn
1474                   + "')");
1475           ex.printStackTrace();
1476         }
1477       }
1478     });
1479   }
1480
1481   @Override
1482   public void scrollViewToRowIn(final AlignFrame alf, final String topRow)
1483   {
1484     // TODO test
1485
1486     java.awt.EventQueue.invokeLater(new Runnable()
1487     {
1488       @Override
1489       public void run()
1490       {
1491         try
1492         {
1493           (alf == null ? Jalview.getCurrentAlignFrame() : alf)
1494                   .scrollToRow(Integer.valueOf(topRow).intValue());
1495
1496         } catch (Exception ex)
1497         {
1498           System.err.println("Couldn't parse integer arguments (topRow='"
1499                   + topRow + "')");
1500           ex.printStackTrace();
1501         }
1502
1503       }
1504     });
1505   }
1506
1507   @Override
1508   public void select(String sequenceIds, String columns)
1509   {
1510     selectIn(Jalview.getCurrentAlignFrame(), sequenceIds, columns, null);
1511   }
1512
1513   @Override
1514   public void select(String sequenceIds, String columns, String sep)
1515   {
1516     selectIn(null, sequenceIds, columns, sep);
1517   }
1518
1519   // @Override
1520   // public AlignFrame newView()
1521   // {
1522   // return newViewFrom(null, null);
1523   // }
1524   //
1525   // @Override
1526   // public AlignFrame newView(String name)
1527   // {
1528   // return newViewFrom(null, name);
1529   // }
1530   //
1531   // @Override
1532   // public AlignFrame newViewFrom(AlignFrame alf)
1533   // {
1534   // return newViewFrom(alf, null);
1535   // }
1536   //
1537   // @Override
1538   // public AlignFrame newViewFrom(AlignFrame alf, String name)
1539   // {
1540   // if (alf == null)
1541   // {
1542   // alf = Jalview.getCurrentAlignFrame();
1543   // }
1544   // return appLoader.newViewFrom(alf, name);
1545   // }
1546
1547   @Override
1548   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
1549   {
1550     selectIn(alf, sequenceIds, columns, null);
1551   }
1552
1553   @Override
1554   public void selectIn(AlignFrame af, String sequenceIds, String columns,
1555           String sep)
1556   {
1557     AlignFrame alf = (af == null ? Jalview.getCurrentAlignFrame() : af);
1558
1559     if (sep == null || sep.length() == 0)
1560     {
1561       sep = separator;
1562     }
1563     else
1564     {
1565       if (debug)
1566       {
1567         System.err.println("Selecting region using separator string '"
1568                 + separator + "'");
1569       }
1570     }
1571     // deparse fields
1572     String[] ids = separatorListToArray(sequenceIds, sep);
1573     String[] cols = separatorListToArray(columns, sep);
1574     final SequenceGroup sel = new SequenceGroup();
1575     final ColumnSelection csel = new ColumnSelection();
1576     AlignmentI al = alf.getViewport().getAlignment();
1577     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
1578             alf.getViewport().getAlignment().getSequencesArray());
1579     int start = 0, end = al.getWidth(), alw = al.getWidth();
1580     boolean seqsfound = true;
1581     if (ids != null && ids.length > 0)
1582     {
1583       seqsfound = false;
1584       for (int i = 0; i < ids.length; i++)
1585       {
1586         if (ids[i].trim().length() == 0)
1587         {
1588           continue;
1589         }
1590         SequenceI sq = matcher.findIdMatch(ids[i]);
1591         if (sq != null)
1592         {
1593           seqsfound = true;
1594           sel.addSequence(sq, false);
1595         }
1596       }
1597     }
1598     boolean inseqpos = false;
1599     if (cols != null && cols.length > 0)
1600     {
1601       boolean seset = false;
1602       for (int i = 0; i < cols.length; i++)
1603       {
1604         String cl = cols[i].trim();
1605         if (cl.length() == 0)
1606         {
1607           continue;
1608         }
1609         int p;
1610         if ((p = cl.indexOf("-")) > -1)
1611         {
1612           int from = -1, to = -1;
1613           try
1614           {
1615             from = Integer.valueOf(cl.substring(0, p)).intValue();
1616             from--;
1617           } catch (NumberFormatException ex)
1618           {
1619             System.err.println(
1620                     "ERROR: Couldn't parse first integer in range element column selection string '"
1621                             + cl + "' - format is 'from-to'");
1622             return;
1623           }
1624           try
1625           {
1626             to = Integer.valueOf(cl.substring(p + 1)).intValue();
1627             to--;
1628           } catch (NumberFormatException ex)
1629           {
1630             System.err.println(
1631                     "ERROR: Couldn't parse second integer in range element column selection string '"
1632                             + cl + "' - format is 'from-to'");
1633             return;
1634           }
1635           if (from >= 0 && to >= 0)
1636           {
1637             // valid range
1638             if (from < to)
1639             {
1640               int t = to;
1641               to = from;
1642               to = t;
1643             }
1644             if (!seset)
1645             {
1646               start = from;
1647               end = to;
1648               seset = true;
1649             }
1650             else
1651             {
1652               // comment to prevent range extension
1653               if (start > from)
1654               {
1655                 start = from;
1656               }
1657               if (end < to)
1658               {
1659                 end = to;
1660               }
1661             }
1662             for (int r = from; r <= to; r++)
1663             {
1664               if (r >= 0 && r < alw)
1665               {
1666                 csel.addElement(r);
1667               }
1668             }
1669             if (debug)
1670             {
1671               System.err.println("Range '" + cl + "' deparsed as [" + from
1672                       + "," + to + "]");
1673             }
1674           }
1675           else
1676           {
1677             System.err.println("ERROR: Invalid Range '" + cl
1678                     + "' deparsed as [" + from + "," + to + "]");
1679           }
1680         }
1681         else
1682         {
1683           int r = -1;
1684           try
1685           {
1686             r = Integer.valueOf(cl).intValue();
1687             r--;
1688           } catch (NumberFormatException ex)
1689           {
1690             if (cl.toLowerCase().equals("sequence"))
1691             {
1692               // we are in the dataset sequence's coordinate frame.
1693               inseqpos = true;
1694             }
1695             else
1696             {
1697               System.err.println(
1698                       "ERROR: Couldn't parse integer from point selection element of column selection string '"
1699                               + cl + "'");
1700               return;
1701             }
1702           }
1703           if (r >= 0 && r <= alw)
1704           {
1705             if (!seset)
1706             {
1707               start = r;
1708               end = r;
1709               seset = true;
1710             }
1711             else
1712             {
1713               // comment to prevent range extension
1714               if (start > r)
1715               {
1716                 start = r;
1717               }
1718               if (end < r)
1719               {
1720                 end = r;
1721               }
1722             }
1723             csel.addElement(r);
1724             if (debug)
1725             {
1726               System.err.println("Point selection '" + cl
1727                       + "' deparsed as [" + r + "]");
1728             }
1729           }
1730           else
1731           {
1732             System.err.println("ERROR: Invalid Point selection '" + cl
1733                     + "' deparsed as [" + r + "]");
1734           }
1735         }
1736       }
1737     }
1738     if (seqsfound)
1739     {
1740       // we only propagate the selection when it was the null selection, or the
1741       // given sequences were found in the alignment.
1742       if (inseqpos && sel.getSize() > 0)
1743       {
1744         // assume first sequence provides reference frame ?
1745         SequenceI rs = sel.getSequenceAt(0);
1746         start = rs.findIndex(start);
1747         end = rs.findIndex(end);
1748         List<Integer> cs = new ArrayList<>(csel.getSelected());
1749         csel.clear();
1750         for (Integer selectedCol : cs)
1751         {
1752           csel.addElement(rs.findIndex(selectedCol));
1753         }
1754       }
1755       sel.setStartRes(start);
1756       sel.setEndRes(end);
1757       EventQueue.invokeLater(new Runnable()
1758       {
1759         @Override
1760         public void run()
1761         {
1762           alf.select(sel, csel,
1763                   alf.getCurrentView().getAlignment().getHiddenColumns());
1764         }
1765       });
1766     }
1767   }
1768
1769   @Override
1770   public void setFeatureGroupState(String[] groups, boolean state)
1771   {
1772     setFeatureGroupState(null, groups, state);
1773   }
1774
1775   @Override
1776   public void setFeatureGroupState(String groups, boolean state)
1777   { // JalviewLite API
1778     setFeatureGroupStateOn(null, groups, state);
1779   }
1780
1781   @Override
1782   public void setFeatureGroupStateOn(final AlignFrame alf,
1783           final String groups, boolean state)
1784   {
1785     setFeatureGroupState(alf, separatorListToArray(groups, separator), state);
1786 //    java.awt.EventQueue.invokeLater(new Runnable()
1787 //    {
1788 //      @Override
1789 //      public void run()
1790 //      {
1791 //        (alf == null ? Jalview.getCurrentAlignFrame() : alf)
1792 //                .setFeatureGroupState(
1793 //                        separatorListToArray(groups, separator), state);
1794 //      }
1795 //    });
1796   }
1797
1798   public void setFeatureGroupState(AlignFrame af, String[] groups, boolean state) {
1799     (af == null ? Jalview.getCurrentAlignFrame() : af).setFeatureGroupState(groups, state);
1800   }
1801
1802
1803   @Override
1804   public void setSelectionListener(AlignFrame af, String listener)
1805   {
1806     Desktop.getStructureSelectionManager()
1807             .addSelectionListener(new JsSelectionListener(af, listener));
1808   }
1809
1810   @Override
1811   public void setSelectionListener(String listener)
1812   {
1813     Desktop.getStructureSelectionManager()
1814             .addSelectionListener(new JsSelectionListener(null, listener));
1815   }
1816
1817   @Override
1818   public void showOverview()
1819   {
1820     Jalview.getCurrentAlignFrame().overviewMenuItem_actionPerformed(null);
1821   }
1822
1823   /**
1824    * Allowing for a JavaScript function here.
1825    */
1826   public void callInitCallback()
1827   {
1828     Object initjscallback = getAppletParameter("oninit", false);
1829     if (initjscallback != null)
1830     {
1831       try
1832       {
1833         doSendCallback(initjscallback, new Object[0]);
1834       } catch (Exception e)
1835       {
1836         System.err.println("Exception when executing _oninit callback '"
1837                 + initjscallback + "'.");
1838         e.printStackTrace();
1839       }
1840     }
1841   }
1842
1843   /**
1844    * Pass the provided array prepended with Jalview.this
1845    * 
1846    * Appropriated from org.jmol.appletjs.Jmol
1847    * 
1848    * @param callback
1849    *          a window function or "alert"
1850    * @param data
1851    * @return String return from the callback method.
1852    */
1853   public String doSendCallback(Object callback, Object[] data)
1854   {
1855     Jalview me = Jalview.getInstance();
1856
1857     if (me != null && callback != null)
1858     {
1859       /**
1860        * @j2sNative
1861        * 
1862        *            try{
1863        * 
1864        *            if (callback == "alert") { alert(data[0]); return ""; } var
1865        *            o; if (typeof callback == "function") { o = callback; } else
1866        *            { if (!callback)return; var tokens = callback.split("."); o
1867        *            = window[tokens[0]]; for (var i = 1; i < tokens.length; i++)
1868        *            o = o[tokens[i]]; } var a = [me]; for (var i = 0; i <
1869        *            data.length; i++) a.push(data[i] ? data[i].booleanValue &&
1870        *            (data[i] = data[i].booleanValue()) : data[i]); return
1871        *            o.apply(null,a) } catch (e) { System.out.println(callback +
1872        *            " failed " + e); }
1873        */
1874     }
1875     return "";
1876   }
1877
1878   private DataSourceType resolveFileProtocol(String[] retPath)
1879   {
1880     String path = retPath[0];
1881     /*
1882      * is it paste data?
1883      */
1884     if (path.startsWith("PASTE"))
1885     {
1886       retPath[0] = path.substring(5);
1887       return DataSourceType.PASTE;
1888     }
1889
1890     /*
1891      * is it a URL?
1892      */
1893     if (path.indexOf("://") >= 0)
1894     {
1895       return DataSourceType.URL;
1896     }
1897
1898     /*
1899      * try relative to document root
1900      */
1901     URL documentBase = getDocumentBase();
1902     String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
1903     if (HttpUtils.isValidUrl(withDocBase))
1904     {
1905       // if (debug)
1906       // {
1907       // System.err.println("Prepended document base '" + documentBase
1908       // + "' to make: '" + withDocBase + "'");
1909       // }
1910       retPath[0] = withDocBase;
1911       return DataSourceType.URL;
1912     }
1913
1914     /*
1915      * try relative to codebase (if different to document base)
1916      */
1917     URL codeBase = getCodeBase();
1918     String withCodeBase = resolveUrlForLocalOrAbsolute(path, codeBase);
1919     if (!withCodeBase.equals(withDocBase)
1920             && HttpUtils.isValidUrl(withCodeBase))
1921     {
1922       // if (debug)
1923       // {
1924       // System.err.println("Prepended codebase '" + codeBase
1925       // + "' to make: '" + withCodeBase + "'");
1926       // }
1927       retPath[0] = withCodeBase;
1928       return DataSourceType.URL;
1929     }
1930
1931     /*
1932      * try locating by classloader; try this last so files in the directory
1933      * are resolved using document base
1934      */
1935     if (inArchive(getClass(), path))
1936     {
1937       return DataSourceType.CLASSLOADER;
1938     }
1939     return null;
1940   }
1941
1942   /**
1943    * Discovers whether the given file is in the Applet Archive
1944    * 
1945    * @param f
1946    *          String
1947    * @return boolean
1948    */
1949   private static boolean inArchive(Class<?> c, String f)
1950   {
1951     // This might throw a security exception in certain browsers
1952     // Netscape Communicator for instance.
1953     try
1954     {
1955       boolean rtn = (c.getResourceAsStream("/" + f) != null);
1956       return rtn;
1957     } catch (Exception ex)
1958     {
1959       System.out.println("Exception checking resources: " + f + " " + ex);
1960       return false;
1961     }
1962   }
1963
1964   /**
1965    * form a complete URL given a path to a resource and a reference location on
1966    * the same server
1967    * 
1968    * @param targetPath
1969    *          - an absolute path on the same server as localref or a document
1970    *          located relative to localref
1971    * @param localref
1972    *          - a URL on the same server as url
1973    * @return a complete URL for the resource located by url
1974    */
1975   public static String resolveUrlForLocalOrAbsolute(String targetPath,
1976           URL localref)
1977   {
1978     String resolvedPath = "";
1979     if (targetPath.startsWith("/"))
1980     {
1981       String codebase = localref.toString();
1982       String localfile = localref.getFile();
1983       resolvedPath = codebase.substring(0,
1984               codebase.length() - localfile.length()) + targetPath;
1985       return resolvedPath;
1986     }
1987
1988     /*
1989      * get URL path and strip off any trailing file e.g.
1990      * www.jalview.org/examples/index.html#applets?a=b is trimmed to
1991      * www.jalview.org/examples/
1992      */
1993     String urlPath = localref.toString();
1994     String directoryPath = urlPath;
1995     int lastSeparator = directoryPath.lastIndexOf("/");
1996     if (lastSeparator > 0)
1997     {
1998       directoryPath = directoryPath.substring(0, lastSeparator + 1);
1999     }
2000
2001     if (targetPath.startsWith("/"))
2002     {
2003       /*
2004        * construct absolute URL to a file on the server - this is not allowed?
2005        */
2006       // String localfile = localref.getFile();
2007       // resolvedPath = urlPath.substring(0,
2008       // urlPath.length() - localfile.length())
2009       // + targetPath;
2010       resolvedPath = directoryPath + targetPath.substring(1);
2011     }
2012     else
2013     {
2014       resolvedPath = directoryPath + targetPath;
2015     }
2016     // if (debug)
2017     // {
2018     // System.err.println(
2019     // "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
2020     // }
2021     return resolvedPath;
2022   }
2023
2024   /**
2025    * parse the string into a list
2026    * 
2027    * @param list
2028    * @param separator
2029    * @return elements separated by separator
2030    */
2031   public static String[] separatorListToArray(String list, String separator)
2032   {
2033     // TODO use StringUtils version (slightly different...)
2034     int seplen = separator.length();
2035     if (list == null || list.equals("") || list.equals(separator))
2036     {
2037       return null;
2038     }
2039     Vector<String> jv = new Vector<>();
2040     int cp = 0, pos;
2041     while ((pos = list.indexOf(separator, cp)) > cp)
2042     {
2043       jv.addElement(list.substring(cp, pos));
2044       cp = pos + seplen;
2045     }
2046     if (cp < list.length())
2047     {
2048       String c = list.substring(cp);
2049       if (!c.equals(separator))
2050       {
2051         jv.addElement(c);
2052       }
2053     }
2054     if (jv.size() > 0)
2055     {
2056       String[] v = new String[jv.size()];
2057       for (int i = 0; i < v.length; i++)
2058       {
2059         v[i] = jv.elementAt(i);
2060       }
2061       jv.removeAllElements();
2062       return v;
2063     }
2064     return null;
2065   }
2066
2067   public class JsSelectionListener
2068           implements jalview.structure.SelectionListener
2069   {
2070
2071     AlignFrame _af;
2072
2073     String _listener;
2074
2075     public JsSelectionListener(AlignFrame af, String listener)
2076     {
2077       _af = af;
2078       _listener = listener;
2079     }
2080
2081     @Override
2082     public void selection(SequenceGroup seqsel, ColumnSelection colsel,
2083             HiddenColumns hidden, SelectionSource source)
2084     {
2085       // System.err.println("Testing selection event relay to
2086       // jsfunction:"+_listener);
2087       String setid = "";
2088       AlignFrame src = _af;
2089       if (source != null)
2090       {
2091         if (source instanceof AlignViewport
2092                 && Jalview.getCurrentAlignFrame().getViewport() == source)
2093         {
2094           src = Jalview.getCurrentAlignFrame();
2095           if (src != _af)
2096             return;
2097         }
2098       }
2099       String[] seqs = new String[] {};
2100       String[] cols = new String[] {};
2101       int strt = 0, end = (src == null) ? -1
2102               : src.alignPanel.av.getAlignment().getWidth();
2103       if (seqsel != null && seqsel.getSize() > 0)
2104       {
2105         seqs = new String[seqsel.getSize()];
2106         for (int i = 0; i < seqs.length; i++)
2107         {
2108           seqs[i] = seqsel.getSequenceAt(i).getName();
2109         }
2110         if (strt < seqsel.getStartRes())
2111         {
2112           strt = seqsel.getStartRes();
2113         }
2114         if (end == -1 || end > seqsel.getEndRes())
2115         {
2116           end = seqsel.getEndRes();
2117         }
2118       }
2119       if (colsel != null && !colsel.isEmpty())
2120       {
2121         if (end == -1)
2122         {
2123           end = colsel.getMax() + 1;
2124         }
2125         cols = new String[colsel.getSelected().size()];
2126         for (int i = 0; i < cols.length; i++)
2127         {
2128           cols[i] = "" + (1 + colsel.getSelected().get(i).intValue());
2129         }
2130       }
2131       else
2132       {
2133         if (seqsel != null && seqsel.getSize() > 0)
2134         {
2135           // send a valid range, otherwise we send the empty selection
2136           cols = new String[1];
2137           cols[0] = "" + (1 + strt) + "-" + (1 + end);
2138         }
2139         ;
2140
2141       }
2142
2143       doSendCallback(_listener,
2144               new Object[]
2145               { src, setid, seqs, cols });
2146     }
2147
2148     public boolean isFor(AlignFrame af, String listener)
2149     {
2150       return _af == af && _listener.contentEquals(listener);
2151     }
2152
2153   }
2154
2155   @Override
2156   public AlignViewportI getViewport()
2157   {
2158     return Jalview.getCurrentAlignFrame().getViewport();
2159   }
2160
2161 }