JAL-3438 spotless for 2.11.2.0
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.bin;
22
23 import java.util.Locale;
24
25 import jalview.analysis.AlignmentUtils;
26 import jalview.api.StructureSelectionManagerProvider;
27 import jalview.appletgui.AlignFrame;
28 import jalview.appletgui.AlignViewport;
29 import jalview.appletgui.EmbmenuFrame;
30 import jalview.appletgui.FeatureSettings;
31 import jalview.appletgui.SplitFrame;
32 import jalview.datamodel.Alignment;
33 import jalview.datamodel.AlignmentI;
34 import jalview.datamodel.AlignmentOrder;
35 import jalview.datamodel.ColumnSelection;
36 import jalview.datamodel.PDBEntry;
37 import jalview.datamodel.Sequence;
38 import jalview.datamodel.SequenceGroup;
39 import jalview.datamodel.SequenceI;
40 import jalview.io.AnnotationFile;
41 import jalview.io.AppletFormatAdapter;
42 import jalview.io.DataSourceType;
43 import jalview.io.FileFormatI;
44 import jalview.io.FileFormats;
45 import jalview.io.FileParse;
46 import jalview.io.IdentifyFile;
47 import jalview.io.JPredFile;
48 import jalview.io.JnetAnnotationMaker;
49 import jalview.io.NewickFile;
50 import jalview.javascript.JSFunctionExec;
51 import jalview.javascript.JalviewLiteJsApi;
52 import jalview.javascript.JsCallBack;
53 import jalview.javascript.MouseOverStructureListener;
54 import jalview.structure.SelectionListener;
55 import jalview.structure.StructureSelectionManager;
56 import jalview.util.ColorUtils;
57 import jalview.util.HttpUtils;
58 import jalview.util.MessageManager;
59
60 import java.applet.Applet;
61 import java.awt.Button;
62 import java.awt.Color;
63 import java.awt.Component;
64 import java.awt.EventQueue;
65 import java.awt.Font;
66 import java.awt.Frame;
67 import java.awt.Graphics;
68 import java.awt.event.ActionEvent;
69 import java.awt.event.WindowAdapter;
70 import java.awt.event.WindowEvent;
71 import java.io.BufferedReader;
72 import java.io.IOException;
73 import java.io.InputStreamReader;
74 import java.net.URL;
75 import java.util.ArrayList;
76 import java.util.Hashtable;
77 import java.util.List;
78 import java.util.StringTokenizer;
79 import java.util.Vector;
80
81 import netscape.javascript.JSObject;
82
83 /**
84  * Jalview Applet. Runs in Java 1.18 runtime
85  * 
86  * @author $author$
87  * @version $Revision: 1.92 $
88  */
89 public class JalviewLite extends Applet
90         implements StructureSelectionManagerProvider, JalviewLiteJsApi
91 {
92
93   private static final String TRUE = "true";
94
95   private static final String FALSE = "false";
96
97   public StructureSelectionManager getStructureSelectionManager()
98   {
99     return StructureSelectionManager.getStructureSelectionManager(this);
100   }
101
102   // /////////////////////////////////////////
103   // The following public methods may be called
104   // externally, eg via javascript in HTML page
105   /*
106    * (non-Javadoc)
107    * 
108    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
109    */
110   @Override
111   public String getSelectedSequences()
112   {
113     return getSelectedSequencesFrom(getDefaultTargetFrame());
114   }
115
116   /*
117    * (non-Javadoc)
118    * 
119    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
120    */
121   @Override
122   public String getSelectedSequences(String sep)
123   {
124     return getSelectedSequencesFrom(getDefaultTargetFrame(), sep);
125   }
126
127   /*
128    * (non-Javadoc)
129    * 
130    * @see
131    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
132    * .AlignFrame)
133    */
134   @Override
135   public String getSelectedSequencesFrom(AlignFrame alf)
136   {
137     return getSelectedSequencesFrom(alf, separator); // ""+0x00AC);
138   }
139
140   /*
141    * (non-Javadoc)
142    * 
143    * @see
144    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
145    * .AlignFrame, java.lang.String)
146    */
147   @Override
148   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
149   {
150     StringBuffer result = new StringBuffer("");
151     if (sep == null || sep.length() == 0)
152     {
153       sep = separator; // "+0x00AC;
154     }
155     if (alf.viewport.getSelectionGroup() != null)
156     {
157       SequenceI[] seqs = alf.viewport.getSelectionGroup()
158               .getSequencesInOrder(alf.viewport.getAlignment());
159
160       for (int i = 0; i < seqs.length; i++)
161       {
162         result.append(seqs[i].getName());
163         result.append(sep);
164       }
165     }
166
167     return result.toString();
168   }
169
170   /*
171    * (non-Javadoc)
172    * 
173    * @see jalview.bin.JalviewLiteJsApi#highlight(java.lang.String,
174    * java.lang.String, java.lang.String)
175    */
176   @Override
177   public void highlight(String sequenceId, String position,
178           String alignedPosition)
179   {
180     highlightIn(getDefaultTargetFrame(), sequenceId, position,
181             alignedPosition);
182   }
183
184   /*
185    * (non-Javadoc)
186    * 
187    * @see jalview.bin.JalviewLiteJsApi#highlightIn(jalview.appletgui.AlignFrame,
188    * java.lang.String, java.lang.String, java.lang.String)
189    */
190   @Override
191   public void highlightIn(final AlignFrame alf, final String sequenceId,
192           final String position, final String alignedPosition)
193   {
194     // TODO: could try to highlight in all alignments if alf==null
195     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
196             alf.viewport.getAlignment().getSequencesArray());
197     final SequenceI sq = matcher.findIdMatch(sequenceId);
198     if (sq != null)
199     {
200       int apos = -1;
201       try
202       {
203         apos = Integer.valueOf(position).intValue();
204         apos--;
205       } catch (NumberFormatException ex)
206       {
207         return;
208       }
209       final StructureSelectionManagerProvider me = this;
210       final int pos = apos;
211       // use vamsas listener to broadcast to all listeners in scope
212       if (alignedPosition != null
213               && (alignedPosition.trim().length() == 0 || alignedPosition
214                       .toLowerCase(Locale.ROOT).indexOf("false") > -1))
215       {
216         java.awt.EventQueue.invokeLater(new Runnable()
217         {
218           @Override
219           public void run()
220           {
221             StructureSelectionManager.getStructureSelectionManager(me)
222                     .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
223           }
224         });
225       }
226       else
227       {
228         java.awt.EventQueue.invokeLater(new Runnable()
229         {
230           @Override
231           public void run()
232           {
233             StructureSelectionManager.getStructureSelectionManager(me)
234                     .mouseOverVamsasSequence(sq, pos, null);
235           }
236         });
237       }
238     }
239   }
240
241   /*
242    * (non-Javadoc)
243    * 
244    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
245    * java.lang.String)
246    */
247   @Override
248   public void select(String sequenceIds, String columns)
249   {
250     selectIn(getDefaultTargetFrame(), sequenceIds, columns, separator);
251   }
252
253   /*
254    * (non-Javadoc)
255    * 
256    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
257    * java.lang.String, java.lang.String)
258    */
259   @Override
260   public void select(String sequenceIds, String columns, String sep)
261   {
262     selectIn(getDefaultTargetFrame(), sequenceIds, columns, sep);
263   }
264
265   /*
266    * (non-Javadoc)
267    * 
268    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
269    * java.lang.String, java.lang.String)
270    */
271   @Override
272   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
273   {
274     selectIn(alf, sequenceIds, columns, separator);
275   }
276
277   /*
278    * (non-Javadoc)
279    * 
280    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
281    * java.lang.String, java.lang.String, java.lang.String)
282    */
283   @Override
284   public void selectIn(final AlignFrame alf, String sequenceIds,
285           String columns, String sep)
286   {
287     if (sep == null || sep.length() == 0)
288     {
289       sep = separator;
290     }
291     else
292     {
293       if (debug)
294       {
295         System.err.println("Selecting region using separator string '"
296                 + separator + "'");
297       }
298     }
299     // deparse fields
300     String[] ids = separatorListToArray(sequenceIds, sep);
301     String[] cols = separatorListToArray(columns, sep);
302     final SequenceGroup sel = new SequenceGroup();
303     final ColumnSelection csel = new ColumnSelection();
304     AlignmentI al = alf.viewport.getAlignment();
305     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
306             alf.viewport.getAlignment().getSequencesArray());
307     int start = 0, end = al.getWidth(), alw = al.getWidth();
308     boolean seqsfound = true;
309     if (ids != null && ids.length > 0)
310     {
311       seqsfound = false;
312       for (int i = 0; i < ids.length; i++)
313       {
314         if (ids[i].trim().length() == 0)
315         {
316           continue;
317         }
318         SequenceI sq = matcher.findIdMatch(ids[i]);
319         if (sq != null)
320         {
321           seqsfound = true;
322           sel.addSequence(sq, false);
323         }
324       }
325     }
326     boolean inseqpos = false;
327     if (cols != null && cols.length > 0)
328     {
329       boolean seset = false;
330       for (int i = 0; i < cols.length; i++)
331       {
332         String cl = cols[i].trim();
333         if (cl.length() == 0)
334         {
335           continue;
336         }
337         int p;
338         if ((p = cl.indexOf("-")) > -1)
339         {
340           int from = -1, to = -1;
341           try
342           {
343             from = Integer.valueOf(cl.substring(0, p)).intValue();
344             from--;
345           } catch (NumberFormatException ex)
346           {
347             System.err.println(
348                     "ERROR: Couldn't parse first integer in range element column selection string '"
349                             + cl + "' - format is 'from-to'");
350             return;
351           }
352           try
353           {
354             to = Integer.valueOf(cl.substring(p + 1)).intValue();
355             to--;
356           } catch (NumberFormatException ex)
357           {
358             System.err.println(
359                     "ERROR: Couldn't parse second integer in range element column selection string '"
360                             + cl + "' - format is 'from-to'");
361             return;
362           }
363           if (from >= 0 && to >= 0)
364           {
365             // valid range
366             if (from < to)
367             {
368               int t = to;
369               to = from;
370               to = t;
371             }
372             if (!seset)
373             {
374               start = from;
375               end = to;
376               seset = true;
377             }
378             else
379             {
380               // comment to prevent range extension
381               if (start > from)
382               {
383                 start = from;
384               }
385               if (end < to)
386               {
387                 end = to;
388               }
389             }
390             for (int r = from; r <= to; r++)
391             {
392               if (r >= 0 && r < alw)
393               {
394                 csel.addElement(r);
395               }
396             }
397             if (debug)
398             {
399               System.err.println("Range '" + cl + "' deparsed as [" + from
400                       + "," + to + "]");
401             }
402           }
403           else
404           {
405             System.err.println("ERROR: Invalid Range '" + cl
406                     + "' deparsed as [" + from + "," + to + "]");
407           }
408         }
409         else
410         {
411           int r = -1;
412           try
413           {
414             r = Integer.valueOf(cl).intValue();
415             r--;
416           } catch (NumberFormatException ex)
417           {
418             if (cl.toLowerCase(Locale.ROOT).equals("sequence"))
419             {
420               // we are in the dataset sequence's coordinate frame.
421               inseqpos = true;
422             }
423             else
424             {
425               System.err.println(
426                       "ERROR: Couldn't parse integer from point selection element of column selection string '"
427                               + cl + "'");
428               return;
429             }
430           }
431           if (r >= 0 && r <= alw)
432           {
433             if (!seset)
434             {
435               start = r;
436               end = r;
437               seset = true;
438             }
439             else
440             {
441               // comment to prevent range extension
442               if (start > r)
443               {
444                 start = r;
445               }
446               if (end < r)
447               {
448                 end = r;
449               }
450             }
451             csel.addElement(r);
452             if (debug)
453             {
454               System.err.println("Point selection '" + cl
455                       + "' deparsed as [" + r + "]");
456             }
457           }
458           else
459           {
460             System.err.println("ERROR: Invalid Point selection '" + cl
461                     + "' deparsed as [" + r + "]");
462           }
463         }
464       }
465     }
466     if (seqsfound)
467     {
468       // we only propagate the selection when it was the null selection, or the
469       // given sequences were found in the alignment.
470       if (inseqpos && sel.getSize() > 0)
471       {
472         // assume first sequence provides reference frame ?
473         SequenceI rs = sel.getSequenceAt(0);
474         start = rs.findIndex(start);
475         end = rs.findIndex(end);
476         List<Integer> cs = new ArrayList<>(csel.getSelected());
477         csel.clear();
478         for (Integer selectedCol : cs)
479         {
480           csel.addElement(rs.findIndex(selectedCol));
481         }
482       }
483       sel.setStartRes(start);
484       sel.setEndRes(end);
485       EventQueue.invokeLater(new Runnable()
486       {
487         @Override
488         public void run()
489         {
490           alf.select(sel, csel,
491                   alf.getAlignViewport().getAlignment().getHiddenColumns());
492         }
493       });
494     }
495   }
496
497   /*
498    * (non-Javadoc)
499    * 
500    * @see
501    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesAsAlignment(java.lang.
502    * String, java.lang.String)
503    */
504   @Override
505   public String getSelectedSequencesAsAlignment(String format,
506           String suffix)
507   {
508     return getSelectedSequencesAsAlignmentFrom(getDefaultTargetFrame(),
509             format, suffix);
510   }
511
512   /*
513    * (non-Javadoc)
514    * 
515    * @see
516    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesAsAlignmentFrom(jalview
517    * .appletgui.AlignFrame, java.lang.String, java.lang.String)
518    */
519   @Override
520   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
521           String format, String suffix)
522   {
523     try
524     {
525       FileFormatI theFormat = FileFormats.getInstance().forName(format);
526       boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
527       if (alf.viewport.getSelectionGroup() != null)
528       {
529         // JBPNote: getSelectionAsNewSequence behaviour has changed - this
530         // method now returns a full copy of sequence data
531         // TODO consider using getSequenceSelection instead here
532         String reply = new AppletFormatAdapter().formatSequences(theFormat,
533                 new Alignment(alf.viewport.getSelectionAsNewSequence()),
534                 seqlimits);
535         return reply;
536       }
537     } catch (IllegalArgumentException ex)
538     {
539       ex.printStackTrace();
540       return "Error retrieving alignment, possibly invalid format specifier: "
541               + format;
542     }
543     return "";
544   }
545
546   /*
547    * (non-Javadoc)
548    * 
549    * @see jalview.bin.JalviewLiteJsApi#getAlignmentOrder()
550    */
551   @Override
552   public String getAlignmentOrder()
553   {
554     return getAlignmentOrderFrom(getDefaultTargetFrame());
555   }
556
557   /*
558    * (non-Javadoc)
559    * 
560    * @see
561    * jalview.bin.JalviewLiteJsApi#getAlignmentOrderFrom(jalview.appletgui.AlignFrame
562    * )
563    */
564   @Override
565   public String getAlignmentOrderFrom(AlignFrame alf)
566   {
567     return getAlignmentOrderFrom(alf, separator);
568   }
569
570   /*
571    * (non-Javadoc)
572    * 
573    * @see
574    * jalview.bin.JalviewLiteJsApi#getAlignmentOrderFrom(jalview.appletgui.AlignFrame
575    * , java.lang.String)
576    */
577   @Override
578   public String getAlignmentOrderFrom(AlignFrame alf, String sep)
579   {
580     AlignmentI alorder = alf.getAlignViewport().getAlignment();
581     String[] order = new String[alorder.getHeight()];
582     for (int i = 0; i < order.length; i++)
583     {
584       order[i] = alorder.getSequenceAt(i).getName();
585     }
586     return arrayToSeparatorList(order);
587   }
588
589   /*
590    * (non-Javadoc)
591    * 
592    * @see jalview.bin.JalviewLiteJsApi#orderBy(java.lang.String,
593    * java.lang.String)
594    */
595   @Override
596   public String orderBy(String order, String undoName)
597   {
598     return orderBy(order, undoName, separator);
599   }
600
601   /*
602    * (non-Javadoc)
603    * 
604    * @see jalview.bin.JalviewLiteJsApi#orderBy(java.lang.String,
605    * java.lang.String, java.lang.String)
606    */
607   @Override
608   public String orderBy(String order, String undoName, String sep)
609   {
610     return orderAlignmentBy(getDefaultTargetFrame(), order, undoName, sep);
611   }
612
613   /*
614    * (non-Javadoc)
615    * 
616    * @see
617    * jalview.bin.JalviewLiteJsApi#orderAlignmentBy(jalview.appletgui.AlignFrame,
618    * java.lang.String, java.lang.String, java.lang.String)
619    */
620   @Override
621   public String orderAlignmentBy(AlignFrame alf, String order,
622           String undoName, String sep)
623   {
624     String[] ids = separatorListToArray(order, sep);
625     SequenceI[] sqs = null;
626     if (ids != null && ids.length > 0)
627     {
628       jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
629               alf.viewport.getAlignment().getSequencesArray());
630       int s = 0;
631       sqs = new SequenceI[ids.length];
632       for (int i = 0; i < ids.length; i++)
633       {
634         if (ids[i].trim().length() == 0)
635         {
636           continue;
637         }
638         SequenceI sq = matcher.findIdMatch(ids[i]);
639         if (sq != null)
640         {
641           sqs[s++] = sq;
642         }
643       }
644       if (s > 0)
645       {
646         SequenceI[] sqq = new SequenceI[s];
647         System.arraycopy(sqs, 0, sqq, 0, s);
648         sqs = sqq;
649       }
650       else
651       {
652         sqs = null;
653       }
654     }
655     if (sqs == null)
656     {
657       return "";
658     }
659     ;
660     final AlignmentOrder aorder = new AlignmentOrder(sqs);
661
662     if (undoName != null && undoName.trim().length() == 0)
663     {
664       undoName = null;
665     }
666     final String _undoName = undoName;
667     // TODO: deal with synchronization here: cannot raise any events until after
668     // this has returned.
669     return alf.sortBy(aorder, _undoName) ? TRUE : "";
670   }
671
672   /*
673    * (non-Javadoc)
674    * 
675    * @see jalview.bin.JalviewLiteJsApi#getAlignment(java.lang.String)
676    */
677   @Override
678   public String getAlignment(String format)
679   {
680     return getAlignmentFrom(getDefaultTargetFrame(), format, TRUE);
681   }
682
683   /*
684    * (non-Javadoc)
685    * 
686    * @see
687    * jalview.bin.JalviewLiteJsApi#getAlignmentFrom(jalview.appletgui.AlignFrame,
688    * java.lang.String)
689    */
690   @Override
691   public String getAlignmentFrom(AlignFrame alf, String format)
692   {
693     return getAlignmentFrom(alf, format, TRUE);
694   }
695
696   /*
697    * (non-Javadoc)
698    * 
699    * @see jalview.bin.JalviewLiteJsApi#getAlignment(java.lang.String,
700    * java.lang.String)
701    */
702   @Override
703   public String getAlignment(String format, String suffix)
704   {
705     return getAlignmentFrom(getDefaultTargetFrame(), format, suffix);
706   }
707
708   /*
709    * (non-Javadoc)
710    * 
711    * @see
712    * jalview.bin.JalviewLiteJsApi#getAlignmentFrom(jalview.appletgui.AlignFrame,
713    * java.lang.String, java.lang.String)
714    */
715   @Override
716   public String getAlignmentFrom(AlignFrame alf, String format,
717           String suffix)
718   {
719     try
720     {
721       boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
722
723       FileFormatI theFormat = FileFormats.getInstance().forName(format);
724       String reply = new AppletFormatAdapter().formatSequences(theFormat,
725               alf.viewport.getAlignment(), seqlimits);
726       return reply;
727     } catch (IllegalArgumentException ex)
728     {
729       ex.printStackTrace();
730       return "Error retrieving alignment, possibly invalid format specifier: "
731               + format;
732     }
733   }
734
735   /*
736    * (non-Javadoc)
737    * 
738    * @see jalview.bin.JalviewLiteJsApi#loadAnnotation(java.lang.String)
739    */
740   @Override
741   public void loadAnnotation(String annotation)
742   {
743     loadAnnotationFrom(getDefaultTargetFrame(), annotation);
744   }
745
746   /*
747    * (non-Javadoc)
748    * 
749    * @see
750    * jalview.bin.JalviewLiteJsApi#loadAnnotationFrom(jalview.appletgui.AlignFrame
751    * , java.lang.String)
752    */
753   @Override
754   public void loadAnnotationFrom(AlignFrame alf, String annotation)
755   {
756     if (new AnnotationFile().annotateAlignmentView(alf.getAlignViewport(),
757             annotation, DataSourceType.PASTE))
758     {
759       alf.alignPanel.fontChanged();
760       alf.alignPanel.setScrollValues(0, 0);
761     }
762     else
763     {
764       alf.parseFeaturesFile(annotation, DataSourceType.PASTE);
765     }
766   }
767
768   /*
769    * (non-Javadoc)
770    * 
771    * @see jalview.bin.JalviewLiteJsApi#loadAnnotation(java.lang.String)
772    */
773   @Override
774   public void loadFeatures(String features, boolean autoenabledisplay)
775   {
776     loadFeaturesFrom(getDefaultTargetFrame(), features, autoenabledisplay);
777   }
778
779   /*
780    * (non-Javadoc)
781    * 
782    * @see
783    * jalview.bin.JalviewLiteJsApi#loadAnnotationFrom(jalview.appletgui.AlignFrame
784    * , java.lang.String)
785    */
786   @Override
787   public boolean loadFeaturesFrom(AlignFrame alf, String features,
788           boolean autoenabledisplay)
789   {
790     return alf.parseFeaturesFile(features, DataSourceType.PASTE,
791             autoenabledisplay);
792   }
793
794   /*
795    * (non-Javadoc)
796    * 
797    * @see jalview.bin.JalviewLiteJsApi#getFeatures(java.lang.String)
798    */
799   @Override
800   public String getFeatures(String format)
801   {
802     return getFeaturesFrom(getDefaultTargetFrame(), format);
803   }
804
805   /*
806    * (non-Javadoc)
807    * 
808    * @see
809    * jalview.bin.JalviewLiteJsApi#getFeaturesFrom(jalview.appletgui.AlignFrame,
810    * java.lang.String)
811    */
812   @Override
813   public String getFeaturesFrom(AlignFrame alf, String format)
814   {
815     return alf.outputFeatures(false, format);
816   }
817
818   /*
819    * (non-Javadoc)
820    * 
821    * @see jalview.bin.JalviewLiteJsApi#getAnnotation()
822    */
823   @Override
824   public String getAnnotation()
825   {
826     return getAnnotationFrom(getDefaultTargetFrame());
827   }
828
829   /*
830    * (non-Javadoc)
831    * 
832    * @see
833    * jalview.bin.JalviewLiteJsApi#getAnnotationFrom(jalview.appletgui.AlignFrame
834    * )
835    */
836   @Override
837   public String getAnnotationFrom(AlignFrame alf)
838   {
839     return alf.outputAnnotations(false);
840   }
841
842   /*
843    * (non-Javadoc)
844    * 
845    * @see jalview.bin.JalviewLiteJsApi#newView()
846    */
847   @Override
848   public AlignFrame newView()
849   {
850     return newViewFrom(getDefaultTargetFrame());
851   }
852
853   /*
854    * (non-Javadoc)
855    * 
856    * @see jalview.bin.JalviewLiteJsApi#newView(java.lang.String)
857    */
858   @Override
859   public AlignFrame newView(String name)
860   {
861     return newViewFrom(getDefaultTargetFrame(), name);
862   }
863
864   /*
865    * (non-Javadoc)
866    * 
867    * @see jalview.bin.JalviewLiteJsApi#newViewFrom(jalview.appletgui.AlignFrame)
868    */
869   @Override
870   public AlignFrame newViewFrom(AlignFrame alf)
871   {
872     return alf.newView(null);
873   }
874
875   /*
876    * (non-Javadoc)
877    * 
878    * @see jalview.bin.JalviewLiteJsApi#newViewFrom(jalview.appletgui.AlignFrame,
879    * java.lang.String)
880    */
881   @Override
882   public AlignFrame newViewFrom(AlignFrame alf, String name)
883   {
884     return alf.newView(name);
885   }
886
887   /*
888    * (non-Javadoc)
889    * 
890    * @see jalview.bin.JalviewLiteJsApi#loadAlignment(java.lang.String,
891    * java.lang.String)
892    */
893   @Override
894   public AlignFrame loadAlignment(String text, String title)
895   {
896     AlignmentI al = null;
897
898     try
899     {
900       FileFormatI format = new IdentifyFile().identify(text,
901               DataSourceType.PASTE);
902       al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE,
903               format);
904       if (al.getHeight() > 0)
905       {
906         return new AlignFrame(al, this, title, false);
907       }
908     } catch (IOException ex)
909     {
910       ex.printStackTrace();
911     }
912     return null;
913   }
914
915   /*
916    * (non-Javadoc)
917    * 
918    * @see jalview.bin.JalviewLiteJsApi#setMouseoverListener(java.lang.String)
919    */
920   @Override
921   public void setMouseoverListener(String listener)
922   {
923     setMouseoverListener(currentAlignFrame, listener);
924   }
925
926   private Vector<jalview.javascript.JSFunctionExec> javascriptListeners = new Vector<>();
927
928   /*
929    * (non-Javadoc)
930    * 
931    * @see
932    * jalview.bin.JalviewLiteJsApi#setMouseoverListener(jalview.appletgui.AlignFrame
933    * , java.lang.String)
934    */
935   @Override
936   public void setMouseoverListener(AlignFrame af, String listener)
937   {
938     if (listener != null)
939     {
940       listener = listener.trim();
941       if (listener.length() == 0)
942       {
943         System.err.println(
944                 "jalview Javascript error: Ignoring empty function for mouseover listener.");
945         return;
946       }
947     }
948     jalview.javascript.MouseOverListener mol = new jalview.javascript.MouseOverListener(
949             this, af, listener);
950     javascriptListeners.addElement(mol);
951     StructureSelectionManager.getStructureSelectionManager(this)
952             .addStructureViewerListener(mol);
953     if (debug)
954     {
955       System.err.println("Added a mouseover listener for "
956               + ((af == null) ? "All frames"
957                       : "Just views for "
958                               + af.getAlignViewport().getSequenceSetId()));
959       System.err.println("There are now " + javascriptListeners.size()
960               + " listeners in total.");
961     }
962   }
963
964   /*
965    * (non-Javadoc)
966    * 
967    * @see jalview.bin.JalviewLiteJsApi#setSelectionListener(java.lang.String)
968    */
969   @Override
970   public void setSelectionListener(String listener)
971   {
972     setSelectionListener(null, listener);
973   }
974
975   /*
976    * (non-Javadoc)
977    * 
978    * @see
979    * jalview.bin.JalviewLiteJsApi#setSelectionListener(jalview.appletgui.AlignFrame
980    * , java.lang.String)
981    */
982   @Override
983   public void setSelectionListener(AlignFrame af, String listener)
984   {
985     if (listener != null)
986     {
987       listener = listener.trim();
988       if (listener.length() == 0)
989       {
990         System.err.println(
991                 "jalview Javascript error: Ignoring empty function for selection listener.");
992         return;
993       }
994     }
995     jalview.javascript.JsSelectionSender mol = new jalview.javascript.JsSelectionSender(
996             this, af, listener);
997     javascriptListeners.addElement(mol);
998     StructureSelectionManager.getStructureSelectionManager(this)
999             .addSelectionListener(mol);
1000     if (debug)
1001     {
1002       System.err.println("Added a selection listener for "
1003               + ((af == null) ? "All frames"
1004                       : "Just views for "
1005                               + af.getAlignViewport().getSequenceSetId()));
1006       System.err.println("There are now " + javascriptListeners.size()
1007               + " listeners in total.");
1008     }
1009   }
1010
1011   /**
1012    * Callable from javascript to register a javascript function to pass events
1013    * to a structure viewer.
1014    *
1015    * @param listener
1016    *          the name of a javascript function
1017    * @param modelSet
1018    *          a token separated list of PDB file names listened for
1019    * @see jalview.bin.JalviewLiteJsApi#setStructureListener(java.lang.String,
1020    *      java.lang.String)
1021    */
1022   @Override
1023   public void setStructureListener(String listener, String modelSet)
1024   {
1025     if (listener != null)
1026     {
1027       listener = listener.trim();
1028       if (listener.length() == 0)
1029       {
1030         System.err.println(
1031                 "jalview Javascript error: Ignoring empty function for selection listener.");
1032         return;
1033       }
1034     }
1035     MouseOverStructureListener mol = new MouseOverStructureListener(this,
1036             listener, separatorListToArray(modelSet));
1037     javascriptListeners.addElement(mol);
1038     StructureSelectionManager.getStructureSelectionManager(this)
1039             .addStructureViewerListener(mol);
1040     if (debug)
1041     {
1042       System.err.println("Added a javascript structure viewer listener '"
1043               + listener + "'");
1044       System.err.println("There are now " + javascriptListeners.size()
1045               + " listeners in total.");
1046     }
1047   }
1048
1049   /*
1050    * (non-Javadoc)
1051    * 
1052    * @see
1053    * jalview.bin.JalviewLiteJsApi#removeJavascriptListener(jalview.appletgui
1054    * .AlignFrame, java.lang.String)
1055    */
1056   @Override
1057   public void removeJavascriptListener(AlignFrame af, String listener)
1058   {
1059     if (listener != null)
1060     {
1061       listener = listener.trim();
1062       if (listener.length() == 0)
1063       {
1064         listener = null;
1065       }
1066     }
1067     boolean rprt = false;
1068     for (int ms = 0, msSize = javascriptListeners.size(); ms < msSize;)
1069     {
1070       Object lstn = javascriptListeners.elementAt(ms);
1071       JsCallBack lstner = (JsCallBack) lstn;
1072       if ((af == null || lstner.getAlignFrame() == af) && (listener == null
1073               || lstner.getListenerFunction().equals(listener)))
1074       {
1075         javascriptListeners.removeElement(lstner);
1076         msSize--;
1077         if (lstner instanceof SelectionListener)
1078         {
1079           StructureSelectionManager.getStructureSelectionManager(this)
1080                   .removeSelectionListener((SelectionListener) lstner);
1081         }
1082         else
1083         {
1084           StructureSelectionManager.getStructureSelectionManager(this)
1085                   .removeStructureViewerListener(lstner, null);
1086         }
1087         rprt = debug;
1088         if (debug)
1089         {
1090           System.err.println("Removed listener '" + listener + "'");
1091         }
1092       }
1093       else
1094       {
1095         ms++;
1096       }
1097     }
1098     if (rprt)
1099     {
1100       System.err.println("There are now " + javascriptListeners.size()
1101               + " listeners in total.");
1102     }
1103   }
1104
1105   @Override
1106   public void stop()
1107   {
1108     System.err.println("Applet " + getName() + " stop().");
1109     tidyUp();
1110   }
1111
1112   @Override
1113   public void destroy()
1114   {
1115     System.err.println("Applet " + getName() + " destroy().");
1116     tidyUp();
1117   }
1118
1119   private void tidyUp()
1120   {
1121     removeAll();
1122     if (currentAlignFrame != null && currentAlignFrame.viewport != null
1123             && currentAlignFrame.viewport.applet != null)
1124     {
1125       AlignViewport av = currentAlignFrame.viewport;
1126       currentAlignFrame.closeMenuItem_actionPerformed();
1127       av.applet = null;
1128       currentAlignFrame = null;
1129     }
1130     if (javascriptListeners != null)
1131     {
1132       while (javascriptListeners.size() > 0)
1133       {
1134         jalview.javascript.JSFunctionExec mol = javascriptListeners
1135                 .elementAt(0);
1136         javascriptListeners.removeElement(mol);
1137         if (mol instanceof SelectionListener)
1138         {
1139           StructureSelectionManager.getStructureSelectionManager(this)
1140                   .removeSelectionListener((SelectionListener) mol);
1141         }
1142         else
1143         {
1144           StructureSelectionManager.getStructureSelectionManager(this)
1145                   .removeStructureViewerListener(mol, null);
1146         }
1147         mol.jvlite = null;
1148       }
1149     }
1150     if (jsFunctionExec != null)
1151     {
1152       jsFunctionExec.stopQueue();
1153       jsFunctionExec.jvlite = null;
1154     }
1155     initialAlignFrame = null;
1156     jsFunctionExec = null;
1157     javascriptListeners = null;
1158     StructureSelectionManager.release(this);
1159   }
1160
1161   private jalview.javascript.JSFunctionExec jsFunctionExec;
1162
1163   /*
1164    * (non-Javadoc)
1165    * 
1166    * @see jalview.bin.JalviewLiteJsApi#mouseOverStructure(java.lang.String,
1167    * java.lang.String, java.lang.String)
1168    */
1169   @Override
1170   public void mouseOverStructure(final String pdbResNum, final String chain,
1171           final String pdbfile)
1172   {
1173     final StructureSelectionManagerProvider me = this;
1174     java.awt.EventQueue.invokeLater(new Runnable()
1175     {
1176       @Override
1177       public void run()
1178       {
1179         try
1180         {
1181           StructureSelectionManager.getStructureSelectionManager(me)
1182                   .mouseOverStructure(Integer.valueOf(pdbResNum).intValue(),
1183                           chain, pdbfile);
1184           if (debug)
1185           {
1186             System.err
1187                     .println("mouseOver for '" + pdbResNum + "' in chain '"
1188                             + chain + "' in structure '" + pdbfile + "'");
1189           }
1190         } catch (NumberFormatException e)
1191         {
1192           System.err.println("Ignoring invalid residue number string '"
1193                   + pdbResNum + "'");
1194         }
1195
1196       }
1197     });
1198   }
1199
1200   /*
1201    * (non-Javadoc)
1202    * 
1203    * @see
1204    * jalview.bin.JalviewLiteJsApi#scrollViewToIn(jalview.appletgui.AlignFrame,
1205    * java.lang.String, java.lang.String)
1206    */
1207   @Override
1208   public void scrollViewToIn(final AlignFrame alf, final String topRow,
1209           final String leftHandColumn)
1210   {
1211     java.awt.EventQueue.invokeLater(new Runnable()
1212     {
1213       @Override
1214       public void run()
1215       {
1216         try
1217         {
1218           alf.scrollTo(Integer.valueOf(topRow).intValue(),
1219                   Integer.valueOf(leftHandColumn).intValue());
1220
1221         } catch (Exception ex)
1222         {
1223           System.err.println("Couldn't parse integer arguments (topRow='"
1224                   + topRow + "' and leftHandColumn='" + leftHandColumn
1225                   + "')");
1226           ex.printStackTrace();
1227         }
1228       }
1229     });
1230   }
1231
1232   /*
1233    * (non-Javadoc)
1234    * 
1235    * @see
1236    * jalview.javascript.JalviewLiteJsApi#scrollViewToRowIn(jalview.appletgui
1237    * .AlignFrame, java.lang.String)
1238    */
1239   @Override
1240   public void scrollViewToRowIn(final AlignFrame alf, final String topRow)
1241   {
1242
1243     java.awt.EventQueue.invokeLater(new Runnable()
1244     {
1245       @Override
1246       public void run()
1247       {
1248         try
1249         {
1250           alf.scrollToRow(Integer.valueOf(topRow).intValue());
1251
1252         } catch (Exception ex)
1253         {
1254           System.err.println("Couldn't parse integer arguments (topRow='"
1255                   + topRow + "')");
1256           ex.printStackTrace();
1257         }
1258
1259       }
1260     });
1261   }
1262
1263   /*
1264    * (non-Javadoc)
1265    * 
1266    * @see
1267    * jalview.javascript.JalviewLiteJsApi#scrollViewToColumnIn(jalview.appletgui
1268    * .AlignFrame, java.lang.String)
1269    */
1270   @Override
1271   public void scrollViewToColumnIn(final AlignFrame alf,
1272           final String leftHandColumn)
1273   {
1274     java.awt.EventQueue.invokeLater(new Runnable()
1275     {
1276
1277       @Override
1278       public void run()
1279       {
1280         try
1281         {
1282           alf.scrollToColumn(Integer.valueOf(leftHandColumn).intValue());
1283
1284         } catch (Exception ex)
1285         {
1286           System.err.println(
1287                   "Couldn't parse integer arguments (leftHandColumn='"
1288                           + leftHandColumn + "')");
1289           ex.printStackTrace();
1290         }
1291       }
1292     });
1293
1294   }
1295
1296   // //////////////////////////////////////////////
1297   // //////////////////////////////////////////////
1298
1299   public static int lastFrameX = 200;
1300
1301   public static int lastFrameY = 200;
1302
1303   boolean fileFound = true;
1304
1305   String file = "No file";
1306
1307   String file2 = null;
1308
1309   Button launcher = new Button(
1310           MessageManager.getString("label.start_jalview"));
1311
1312   /**
1313    * The currentAlignFrame is static, it will change if and when the user
1314    * selects a new window. Note that it will *never* point back to the embedded
1315    * AlignFrame if the applet is started as embedded on the page and then
1316    * afterwards a new view is created.
1317    */
1318   public AlignFrame currentAlignFrame = null;
1319
1320   /**
1321    * This is the first frame to be displayed, and does not change. API calls
1322    * will default to this instance if currentAlignFrame is null.
1323    */
1324   AlignFrame initialAlignFrame = null;
1325
1326   boolean embedded = false;
1327
1328   private boolean checkForJmol = true;
1329
1330   private boolean checkedForJmol = false; // ensure we don't check for jmol
1331
1332   // every time the app is re-inited
1333
1334   public boolean jmolAvailable = false;
1335
1336   private boolean alignPdbStructures = false;
1337
1338   /**
1339    * use an external structure viewer exclusively (no jmols or mc_views will be
1340    * opened by JalviewLite itself)
1341    */
1342   public boolean useXtrnalSviewer = false;
1343
1344   public static boolean debug = false;
1345
1346   static String builddate = null, version = null, installation = null;
1347
1348   private static void initBuildDetails()
1349   {
1350     if (builddate == null)
1351     {
1352       builddate = "unknown";
1353       version = "test";
1354       installation = "applet";
1355       java.net.URL url = JalviewLite.class
1356               .getResource("/.build_properties");
1357       if (url != null)
1358       {
1359         try
1360         {
1361           BufferedReader reader = new BufferedReader(
1362                   new InputStreamReader(url.openStream()));
1363           String line;
1364           while ((line = reader.readLine()) != null)
1365           {
1366             if (line.indexOf("VERSION") > -1)
1367             {
1368               version = line.substring(line.indexOf("=") + 1);
1369             }
1370             if (line.indexOf("BUILD_DATE") > -1)
1371             {
1372               builddate = line.substring(line.indexOf("=") + 1);
1373             }
1374             if (line.indexOf("INSTALLATION") > -1)
1375             {
1376               installation = line.substring(line.indexOf("=") + 1);
1377             }
1378           }
1379         } catch (Exception ex)
1380         {
1381           ex.printStackTrace();
1382         }
1383       }
1384     }
1385   }
1386
1387   public static String getBuildDate()
1388   {
1389     initBuildDetails();
1390     return builddate;
1391   }
1392
1393   public static String getInstallation()
1394   {
1395     initBuildDetails();
1396     return installation;
1397   }
1398
1399   public static String getVersion()
1400   {
1401     initBuildDetails();
1402     return version;
1403   }
1404
1405   // public JSObject scriptObject = null;
1406
1407   /**
1408    * init method for Jalview Applet
1409    */
1410   @Override
1411   public void init()
1412   {
1413     debug = TRUE.equalsIgnoreCase(getParameter("debug"));
1414     try
1415     {
1416       if (debug)
1417       {
1418         System.err.println("Applet context is '"
1419                 + getAppletContext().getClass().toString() + "'");
1420       }
1421       JSObject scriptObject = JSObject.getWindow(this);
1422       if (debug && scriptObject != null)
1423       {
1424         System.err.println("Applet has Javascript callback support.");
1425       }
1426
1427     } catch (Exception ex)
1428     {
1429       System.err.println(
1430               "Warning: No JalviewLite javascript callbacks available.");
1431       if (debug)
1432       {
1433         ex.printStackTrace();
1434       }
1435     }
1436
1437     if (debug)
1438     {
1439       System.err.println("JalviewLite Version " + getVersion());
1440       System.err.println("Build Date : " + getBuildDate());
1441       System.err.println("Installation : " + getInstallation());
1442     }
1443     String externalsviewer = getParameter("externalstructureviewer");
1444     if (externalsviewer != null)
1445     {
1446       useXtrnalSviewer = externalsviewer.trim().toLowerCase(Locale.ROOT)
1447               .equals(TRUE);
1448     }
1449     /**
1450      * if true disable the check for jmol
1451      */
1452     String chkforJmol = getParameter("nojmol");
1453     if (chkforJmol != null)
1454     {
1455       checkForJmol = !chkforJmol.equals(TRUE);
1456     }
1457     /**
1458      * get the separator parameter if present
1459      */
1460     String sep = getParameter("separator");
1461     if (sep != null)
1462     {
1463       if (sep.length() > 0)
1464       {
1465         separator = sep;
1466         if (debug)
1467         {
1468           System.err.println("Separator set to '" + separator + "'");
1469         }
1470       }
1471       else
1472       {
1473         throw new Error(MessageManager
1474                 .getString("error.invalid_separator_parameter"));
1475       }
1476     }
1477     int r = 255;
1478     int g = 255;
1479     int b = 255;
1480     String param = getParameter("RGB");
1481
1482     if (param != null)
1483     {
1484       try
1485       {
1486         r = Integer.parseInt(param.substring(0, 2), 16);
1487         g = Integer.parseInt(param.substring(2, 4), 16);
1488         b = Integer.parseInt(param.substring(4, 6), 16);
1489       } catch (Exception ex)
1490       {
1491         r = 255;
1492         g = 255;
1493         b = 255;
1494       }
1495     }
1496     param = getParameter("label");
1497     if (param != null)
1498     {
1499       launcher.setLabel(param);
1500     }
1501
1502     setBackground(new Color(r, g, b));
1503
1504     file = getParameter("file");
1505
1506     if (file == null)
1507     {
1508       // Maybe the sequences are added as parameters
1509       StringBuffer data = new StringBuffer("PASTE");
1510       int i = 1;
1511       while ((file = getParameter("sequence" + i)) != null)
1512       {
1513         data.append(file.toString() + "\n");
1514         i++;
1515       }
1516       if (data.length() > 5)
1517       {
1518         file = data.toString();
1519       }
1520     }
1521     if (getDefaultParameter("enableSplitFrame", true))
1522     {
1523       file2 = getParameter("file2");
1524     }
1525
1526     embedded = TRUE.equalsIgnoreCase(getParameter("embedded"));
1527     if (embedded)
1528     {
1529       LoadingThread loader = new LoadingThread(file, file2, this);
1530       loader.start();
1531     }
1532     else if (file != null)
1533     {
1534       /*
1535        * Start the applet immediately or show a button to start it
1536        */
1537       if (FALSE.equalsIgnoreCase(getParameter("showbutton")))
1538       {
1539         LoadingThread loader = new LoadingThread(file, file2, this);
1540         loader.start();
1541       }
1542       else
1543       {
1544         add(launcher);
1545         launcher.addActionListener(new java.awt.event.ActionListener()
1546         {
1547           @Override
1548           public void actionPerformed(ActionEvent e)
1549           {
1550             LoadingThread loader = new LoadingThread(file, file2,
1551                     JalviewLite.this);
1552             loader.start();
1553           }
1554         });
1555       }
1556     }
1557     else
1558     {
1559       // jalview initialisation with no alignment. loadAlignment() method can
1560       // still be called to open new alignments.
1561       file = "NO FILE";
1562       fileFound = false;
1563       callInitCallback();
1564     }
1565   }
1566
1567   private void initLiveConnect()
1568   {
1569     // try really hard to get the liveConnect thing working
1570     boolean notFailed = false;
1571     int tries = 0;
1572     while (!notFailed && tries < 10)
1573     {
1574       if (tries > 0)
1575       {
1576         System.err.println("LiveConnect request thread going to sleep.");
1577       }
1578       try
1579       {
1580         Thread.sleep(700 * (1 + tries));
1581       } catch (InterruptedException q)
1582       {
1583       }
1584       ;
1585       if (tries++ > 0)
1586       {
1587         System.err.println("LiveConnect request thread woken up.");
1588       }
1589       try
1590       {
1591         JSObject scriptObject = JSObject.getWindow(this);
1592         if (scriptObject.eval("navigator") != null)
1593         {
1594           notFailed = true;
1595         }
1596       } catch (Exception jsex)
1597       {
1598         System.err.println("Attempt " + tries
1599                 + " to access LiveConnect javascript failed.");
1600       }
1601     }
1602   }
1603
1604   private void callInitCallback()
1605   {
1606     String initjscallback = getParameter("oninit");
1607     if (initjscallback == null)
1608     {
1609       return;
1610     }
1611     initjscallback = initjscallback.trim();
1612     if (initjscallback.length() > 0)
1613     {
1614       JSObject scriptObject = null;
1615       try
1616       {
1617         scriptObject = JSObject.getWindow(this);
1618       } catch (Exception ex)
1619       {
1620       }
1621       ;
1622       // try really hard to let the browser plugin know we want liveconnect
1623       initLiveConnect();
1624
1625       if (scriptObject != null)
1626       {
1627         try
1628         {
1629           // do onInit with the JS executor thread
1630           new JSFunctionExec(this).executeJavascriptFunction(true,
1631                   initjscallback, null,
1632                   "Calling oninit callback '" + initjscallback + "'.");
1633         } catch (Exception e)
1634         {
1635           System.err.println("Exception when executing _oninit callback '"
1636                   + initjscallback + "'.");
1637           e.printStackTrace();
1638         }
1639       }
1640       else
1641       {
1642         System.err.println("Not executing _oninit callback '"
1643                 + initjscallback + "' - no scripting allowed.");
1644       }
1645     }
1646   }
1647
1648   /**
1649    * Initialises and displays a new java.awt.Frame
1650    * 
1651    * @param frame
1652    *          java.awt.Frame to be displayed
1653    * @param title
1654    *          title of new frame
1655    * @param width
1656    *          width if new frame
1657    * @param height
1658    *          height of new frame
1659    */
1660   public static void addFrame(final Frame frame, String title, int width,
1661           int height)
1662   {
1663     frame.setLocation(lastFrameX, lastFrameY);
1664     lastFrameX += 40;
1665     lastFrameY += 40;
1666     frame.setSize(width, height);
1667     frame.setTitle(title);
1668     frame.addWindowListener(new WindowAdapter()
1669     {
1670       @Override
1671       public void windowClosing(WindowEvent e)
1672       {
1673         if (frame instanceof AlignFrame)
1674         {
1675           AlignViewport vp = ((AlignFrame) frame).viewport;
1676           ((AlignFrame) frame).closeMenuItem_actionPerformed();
1677           if (vp.applet.currentAlignFrame == frame)
1678           {
1679             vp.applet.currentAlignFrame = null;
1680           }
1681           vp.applet = null;
1682           vp = null;
1683
1684         }
1685         lastFrameX -= 40;
1686         lastFrameY -= 40;
1687         if (frame instanceof EmbmenuFrame)
1688         {
1689           ((EmbmenuFrame) frame).destroyMenus();
1690         }
1691         frame.setMenuBar(null);
1692         frame.dispose();
1693       }
1694
1695       @Override
1696       public void windowActivated(WindowEvent e)
1697       {
1698         if (frame instanceof AlignFrame)
1699         {
1700           ((AlignFrame) frame).viewport.applet.currentAlignFrame = (AlignFrame) frame;
1701           if (debug)
1702           {
1703             System.err.println("Activated window " + frame);
1704           }
1705         }
1706         // be good.
1707         super.windowActivated(e);
1708       }
1709       /*
1710        * Probably not necessary to do this - see TODO above. (non-Javadoc)
1711        * 
1712        * @see
1713        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
1714        * )
1715        * 
1716        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
1717        * frame) { currentAlignFrame = null; if (debug) {
1718        * System.err.println("Deactivated window "+frame); } }
1719        * super.windowDeactivated(e); }
1720        */
1721     });
1722     frame.setVisible(true);
1723   }
1724
1725   /**
1726    * This paints the background surrounding the "Launch Jalview button" <br>
1727    * <br>
1728    * If file given in parameter not found, displays error message
1729    * 
1730    * @param g
1731    *          graphics context
1732    */
1733   @Override
1734   public void paint(Graphics g)
1735   {
1736     if (!fileFound)
1737     {
1738       g.setColor(new Color(200, 200, 200));
1739       g.setColor(Color.cyan);
1740       g.fillRect(0, 0, getSize().width, getSize().height);
1741       g.setColor(Color.red);
1742       g.drawString(
1743               MessageManager.getString("label.jalview_cannot_open_file"), 5,
1744               15);
1745       g.drawString("\"" + file + "\"", 5, 30);
1746     }
1747     else if (embedded)
1748     {
1749       g.setColor(Color.black);
1750       g.setFont(new Font("Arial", Font.BOLD, 24));
1751       g.drawString(MessageManager.getString("label.jalview_applet"), 50,
1752               getSize().height / 2 - 30);
1753       g.drawString(MessageManager.getString("label.loading_data") + "...",
1754               50, getSize().height / 2);
1755     }
1756   }
1757
1758   /**
1759    * get all components associated with the applet of the given type
1760    * 
1761    * @param class1
1762    * @return
1763    */
1764   public Vector getAppletWindow(Class class1)
1765   {
1766     Vector wnds = new Vector();
1767     Component[] cmp = getComponents();
1768     if (cmp != null)
1769     {
1770       for (int i = 0; i < cmp.length; i++)
1771       {
1772         if (class1.isAssignableFrom(cmp[i].getClass()))
1773         {
1774           wnds.addElement(cmp);
1775         }
1776       }
1777     }
1778     return wnds;
1779   }
1780
1781   class LoadJmolThread extends Thread
1782   {
1783     private boolean running = false;
1784
1785     @Override
1786     public void run()
1787     {
1788       if (running || checkedForJmol)
1789       {
1790         return;
1791       }
1792       running = true;
1793       if (checkForJmol)
1794       {
1795         try
1796         {
1797           if (!System.getProperty("java.version").startsWith("1.1"))
1798           {
1799             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
1800             jmolAvailable = true;
1801           }
1802           if (!jmolAvailable)
1803           {
1804             System.out.println(
1805                     "Jmol not available - Using mc_view for structures");
1806           }
1807         } catch (java.lang.ClassNotFoundException ex)
1808         {
1809         }
1810       }
1811       else
1812       {
1813         jmolAvailable = false;
1814         if (debug)
1815         {
1816           System.err.println(
1817                   "Skipping Jmol check. Will use mc_view (probably)");
1818         }
1819       }
1820       checkedForJmol = true;
1821       running = false;
1822     }
1823
1824     public boolean notFinished()
1825     {
1826       return running || !checkedForJmol;
1827     }
1828   }
1829
1830   class LoadingThread extends Thread
1831   {
1832     /**
1833      * State variable: protocol for access to file source
1834      */
1835     DataSourceType protocol;
1836
1837     String _file; // alignment file or URL spec
1838
1839     String _file2; // second alignment file or URL spec
1840
1841     JalviewLite applet;
1842
1843     private void dbgMsg(String msg)
1844     {
1845       if (JalviewLite.debug)
1846       {
1847         System.err.println(msg);
1848       }
1849     }
1850
1851     /**
1852      * update the protocol state variable for accessing the datasource located
1853      * by file.
1854      * 
1855      * @param path
1856      * @return possibly updated datasource string
1857      */
1858     public String resolveFileProtocol(String path)
1859     {
1860       /*
1861        * is it paste data?
1862        */
1863       if (path.startsWith("PASTE"))
1864       {
1865         protocol = DataSourceType.PASTE;
1866         return path.substring(5);
1867       }
1868
1869       /*
1870        * is it a URL?
1871        */
1872       if (path.indexOf("://") != -1)
1873       {
1874         protocol = DataSourceType.URL;
1875         return path;
1876       }
1877
1878       /*
1879        * try relative to document root
1880        */
1881       URL documentBase = getDocumentBase();
1882       String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
1883       if (HttpUtils.isValidUrl(withDocBase))
1884       {
1885         if (debug)
1886         {
1887           System.err.println("Prepended document base '" + documentBase
1888                   + "' to make: '" + withDocBase + "'");
1889         }
1890         protocol = DataSourceType.URL;
1891         return withDocBase;
1892       }
1893
1894       /*
1895        * try relative to codebase (if different to document base)
1896        */
1897       URL codeBase = getCodeBase();
1898       String withCodeBase = applet.resolveUrlForLocalOrAbsolute(path,
1899               codeBase);
1900       if (!withCodeBase.equals(withDocBase)
1901               && HttpUtils.isValidUrl(withCodeBase))
1902       {
1903         protocol = DataSourceType.URL;
1904         if (debug)
1905         {
1906           System.err.println("Prepended codebase '" + codeBase
1907                   + "' to make: '" + withCodeBase + "'");
1908         }
1909         return withCodeBase;
1910       }
1911
1912       /*
1913        * try locating by classloader; try this last so files in the directory
1914        * are resolved using document base
1915        */
1916       if (inArchive(path))
1917       {
1918         protocol = DataSourceType.CLASSLOADER;
1919       }
1920       return path;
1921     }
1922
1923     public LoadingThread(String file, String file2, JalviewLite _applet)
1924     {
1925       this._file = file;
1926       this._file2 = file2;
1927       applet = _applet;
1928     }
1929
1930     @Override
1931     public void run()
1932     {
1933       LoadJmolThread jmolchecker = new LoadJmolThread();
1934       jmolchecker.start();
1935       while (jmolchecker.notFinished())
1936       {
1937         // wait around until the Jmol check is complete.
1938         try
1939         {
1940           Thread.sleep(2);
1941         } catch (Exception e)
1942         {
1943         }
1944       }
1945       startLoading();
1946       // applet.callInitCallback();
1947     }
1948
1949     /**
1950      * Load the alignment and any related files as specified by applet
1951      * parameters
1952      */
1953     private void startLoading()
1954     {
1955       dbgMsg("Loading thread started with:\n>>file\n" + _file
1956               + ">>endfile");
1957
1958       dbgMsg("Loading started.");
1959
1960       AlignFrame newAlignFrame = readAlignment(_file);
1961       AlignFrame newAlignFrame2 = readAlignment(_file2);
1962       if (newAlignFrame != null)
1963       {
1964         addToDisplay(newAlignFrame, newAlignFrame2);
1965         loadTree(newAlignFrame);
1966
1967         loadScoreFile(newAlignFrame);
1968
1969         loadFeatures(newAlignFrame);
1970
1971         loadAnnotations(newAlignFrame);
1972
1973         loadJnetFile(newAlignFrame);
1974
1975         loadPdbFiles(newAlignFrame);
1976       }
1977       else
1978       {
1979         fileFound = false;
1980         applet.remove(launcher);
1981         applet.repaint();
1982       }
1983       callInitCallback();
1984     }
1985
1986     /**
1987      * Add an AlignFrame to the display; or if two are provided, a SplitFrame.
1988      * 
1989      * @param af
1990      * @param af2
1991      */
1992     public void addToDisplay(AlignFrame af, AlignFrame af2)
1993     {
1994       if (af2 != null)
1995       {
1996         AlignmentI al1 = af.viewport.getAlignment();
1997         AlignmentI al2 = af2.viewport.getAlignment();
1998         AlignmentI cdna = al1.isNucleotide() ? al1 : al2;
1999         AlignmentI prot = al1.isNucleotide() ? al2 : al1;
2000         if (AlignmentUtils.mapProteinAlignmentToCdna(prot, cdna))
2001         {
2002           al2.alignAs(al1);
2003           SplitFrame sf = new SplitFrame(af, af2);
2004           sf.addToDisplay(embedded, JalviewLite.this);
2005           return;
2006         }
2007         else
2008         {
2009           String msg = "Could not map any sequence in " + af2.getTitle()
2010                   + " as "
2011                   + (al1.isNucleotide() ? "protein product" : "cDNA")
2012                   + " for " + af.getTitle();
2013           System.err.println(msg);
2014         }
2015       }
2016
2017       af.addToDisplay(embedded);
2018     }
2019
2020     /**
2021      * Read the alignment file (from URL, text 'paste', or archive by
2022      * classloader).
2023      * 
2024      * @return
2025      */
2026     protected AlignFrame readAlignment(String fileParam)
2027     {
2028       if (fileParam == null)
2029       {
2030         return null;
2031       }
2032       String resolvedFile = resolveFileProtocol(fileParam);
2033       AlignmentI al = null;
2034       try
2035       {
2036         FileFormatI format = new IdentifyFile().identify(resolvedFile,
2037                 protocol);
2038         dbgMsg("File identified as '" + format + "'");
2039         al = new AppletFormatAdapter().readFile(resolvedFile, protocol,
2040                 format);
2041         if ((al != null) && (al.getHeight() > 0))
2042         {
2043           dbgMsg("Successfully loaded file.");
2044           al.setDataset(null);
2045           AlignFrame newAlignFrame = new AlignFrame(al, applet,
2046                   resolvedFile, embedded, false);
2047           newAlignFrame.setTitle(resolvedFile);
2048           if (initialAlignFrame == null)
2049           {
2050             initialAlignFrame = newAlignFrame;
2051           }
2052           // update the focus.
2053           currentAlignFrame = newAlignFrame;
2054
2055           if (protocol == DataSourceType.PASTE)
2056           {
2057             newAlignFrame.setTitle(MessageManager
2058                     .formatMessage("label.sequences_from", new Object[]
2059                     { applet.getDocumentBase().toString() }));
2060           }
2061
2062           newAlignFrame.statusBar.setText(MessageManager.formatMessage(
2063                   "label.successfully_loaded_file", new Object[]
2064                   { resolvedFile }));
2065
2066           return newAlignFrame;
2067         }
2068       } catch (java.io.IOException ex)
2069       {
2070         dbgMsg("File load exception.");
2071         ex.printStackTrace();
2072         if (debug)
2073         {
2074           try
2075           {
2076             FileParse fp = new FileParse(resolvedFile, protocol);
2077             String ln = null;
2078             dbgMsg(">>>Dumping contents of '" + resolvedFile + "' " + "("
2079                     + protocol + ")");
2080             while ((ln = fp.nextLine()) != null)
2081             {
2082               dbgMsg(ln);
2083             }
2084             dbgMsg(">>>Dump finished.");
2085           } catch (Exception e)
2086           {
2087             System.err.println(
2088                     "Exception when trying to dump the content of the file parameter.");
2089             e.printStackTrace();
2090           }
2091         }
2092       }
2093       return null;
2094     }
2095
2096     /**
2097      * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
2098      * else false.
2099      * 
2100      * @param alignFrame
2101      * @return
2102      */
2103     protected boolean loadPdbFiles(AlignFrame alignFrame)
2104     {
2105       boolean result = false;
2106       /*
2107        * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
2108        * related to JAL-434
2109        */
2110
2111       applet.setAlignPdbStructures(
2112               getDefaultParameter("alignpdbfiles", false));
2113       /*
2114        * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
2115        * PDB|1GAQ|1GAQ|C">
2116        * 
2117        * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
2118        * 
2119        * <param name="PDBfile3" value="1q0o Q45135_9MICO">
2120        */
2121
2122       int pdbFileCount = 0;
2123       // Accumulate pdbs here if they are heading for the same view (if
2124       // alignPdbStructures is true)
2125       Vector pdbs = new Vector();
2126       // create a lazy matcher if we're asked to
2127       jalview.analysis.SequenceIdMatcher matcher = (applet
2128               .getDefaultParameter("relaxedidmatch", false))
2129                       ? new jalview.analysis.SequenceIdMatcher(
2130                               alignFrame.getAlignViewport().getAlignment()
2131                                       .getSequencesArray())
2132                       : null;
2133
2134       String param;
2135       do
2136       {
2137         if (pdbFileCount > 0)
2138         {
2139           param = applet.getParameter("PDBFILE" + pdbFileCount);
2140         }
2141         else
2142         {
2143           param = applet.getParameter("PDBFILE");
2144         }
2145
2146         if (param != null)
2147         {
2148           PDBEntry pdb = new PDBEntry();
2149
2150           String seqstring;
2151           SequenceI[] seqs = null;
2152           String[] chains = null;
2153
2154           StringTokenizer st = new StringTokenizer(param, " ");
2155
2156           if (st.countTokens() < 2)
2157           {
2158             String sequence = applet.getParameter("PDBSEQ");
2159             if (sequence != null)
2160             {
2161               seqs = new SequenceI[] { matcher == null
2162                       ? (Sequence) alignFrame.getAlignViewport()
2163                               .getAlignment().findName(sequence)
2164                       : matcher.findIdMatch(sequence) };
2165             }
2166
2167           }
2168           else
2169           {
2170             param = st.nextToken();
2171             List<SequenceI> tmp = new ArrayList<>();
2172             List<String> tmp2 = new ArrayList<>();
2173
2174             while (st.hasMoreTokens())
2175             {
2176               seqstring = st.nextToken();
2177               StringTokenizer st2 = new StringTokenizer(seqstring, "=");
2178               if (st2.countTokens() > 1)
2179               {
2180                 // This is the chain
2181                 tmp2.add(st2.nextToken());
2182                 seqstring = st2.nextToken();
2183               }
2184               tmp.add(matcher == null
2185                       ? (Sequence) alignFrame.getAlignViewport()
2186                               .getAlignment().findName(seqstring)
2187                       : matcher.findIdMatch(seqstring));
2188             }
2189
2190             seqs = tmp.toArray(new SequenceI[tmp.size()]);
2191             if (tmp2.size() == tmp.size())
2192             {
2193               chains = tmp2.toArray(new String[tmp2.size()]);
2194             }
2195           }
2196           param = resolveFileProtocol(param);
2197           // TODO check JAL-357 for files in a jar (CLASSLOADER)
2198           pdb.setFile(param);
2199
2200           if (seqs != null)
2201           {
2202             for (int i = 0; i < seqs.length; i++)
2203             {
2204               if (seqs[i] != null)
2205               {
2206                 ((Sequence) seqs[i]).addPDBId(pdb);
2207                 StructureSelectionManager
2208                         .getStructureSelectionManager(applet)
2209                         .registerPDBEntry(pdb);
2210               }
2211               else
2212               {
2213                 if (JalviewLite.debug)
2214                 {
2215                   // this may not really be a problem but we give a warning
2216                   // anyway
2217                   System.err.println(
2218                           "Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
2219                                   + i + ")");
2220                 }
2221               }
2222             }
2223
2224             if (!alignPdbStructures)
2225             {
2226               alignFrame.newStructureView(applet, pdb, seqs, chains,
2227                       protocol);
2228             }
2229             else
2230             {
2231               pdbs.addElement(new Object[] { pdb, seqs, chains, protocol });
2232             }
2233           }
2234         }
2235
2236         pdbFileCount++;
2237       } while (param != null || pdbFileCount < 10);
2238       if (pdbs.size() > 0)
2239       {
2240         SequenceI[][] seqs = new SequenceI[pdbs.size()][];
2241         PDBEntry[] pdb = new PDBEntry[pdbs.size()];
2242         String[][] chains = new String[pdbs.size()][];
2243         String[] protocols = new String[pdbs.size()];
2244         for (int pdbsi = 0, pdbsiSize = pdbs
2245                 .size(); pdbsi < pdbsiSize; pdbsi++)
2246         {
2247           Object[] o = (Object[]) pdbs.elementAt(pdbsi);
2248           pdb[pdbsi] = (PDBEntry) o[0];
2249           seqs[pdbsi] = (SequenceI[]) o[1];
2250           chains[pdbsi] = (String[]) o[2];
2251           protocols[pdbsi] = (String) o[3];
2252         }
2253         alignFrame.alignedStructureView(applet, pdb, seqs, chains,
2254                 protocols);
2255         result = true;
2256       }
2257       return result;
2258     }
2259
2260     /**
2261      * Load in a Jnetfile if specified by parameter. Returns true if loaded,
2262      * else false.
2263      * 
2264      * @param alignFrame
2265      * @return
2266      */
2267     protected boolean loadJnetFile(AlignFrame alignFrame)
2268     {
2269       boolean result = false;
2270       String param = applet.getParameter("jnetfile");
2271       if (param == null)
2272       {
2273         // jnet became jpred around 2016
2274         param = applet.getParameter("jpredfile");
2275       }
2276       if (param != null)
2277       {
2278         try
2279         {
2280           param = resolveFileProtocol(param);
2281           JPredFile predictions = new JPredFile(param, protocol);
2282           JnetAnnotationMaker.add_annotation(predictions,
2283                   alignFrame.viewport.getAlignment(), 0, false);
2284           // false == do not add sequence profile from concise output
2285
2286           alignFrame.viewport.getAlignment().setupJPredAlignment();
2287
2288           alignFrame.alignPanel.fontChanged();
2289           alignFrame.alignPanel.setScrollValues(0, 0);
2290           result = true;
2291         } catch (Exception ex)
2292         {
2293           ex.printStackTrace();
2294         }
2295       }
2296       return result;
2297     }
2298
2299     /**
2300      * Load annotations if specified by parameter. Returns true if loaded, else
2301      * false.
2302      * 
2303      * @param alignFrame
2304      * @return
2305      */
2306     protected boolean loadAnnotations(AlignFrame alignFrame)
2307     {
2308       boolean result = false;
2309       String param = applet.getParameter("annotations");
2310       if (param != null)
2311       {
2312         param = resolveFileProtocol(param);
2313
2314         if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
2315                 param, protocol))
2316         {
2317           alignFrame.alignPanel.fontChanged();
2318           alignFrame.alignPanel.setScrollValues(0, 0);
2319           result = true;
2320         }
2321         else
2322         {
2323           System.err.println(
2324                   "Annotations were not added from annotation file '"
2325                           + param + "'");
2326         }
2327       }
2328       return result;
2329     }
2330
2331     /**
2332      * Load features file and view settings as specified by parameters. Returns
2333      * true if features were loaded, else false.
2334      * 
2335      * @param alignFrame
2336      * @return
2337      */
2338     protected boolean loadFeatures(AlignFrame alignFrame)
2339     {
2340       boolean result = false;
2341       // ///////////////////////////
2342       // modify display of features
2343       // we do this before any features have been loaded, ensuring any hidden
2344       // groups are hidden when features first displayed
2345       //
2346       // hide specific groups
2347       //
2348       String param = applet.getParameter("hidefeaturegroups");
2349       if (param != null)
2350       {
2351         alignFrame.setFeatureGroupState(separatorListToArray(param), false);
2352         // applet.setFeatureGroupStateOn(newAlignFrame, param, false);
2353       }
2354       // show specific groups
2355       param = applet.getParameter("showfeaturegroups");
2356       if (param != null)
2357       {
2358         alignFrame.setFeatureGroupState(separatorListToArray(param), true);
2359         // applet.setFeatureGroupStateOn(newAlignFrame, param, true);
2360       }
2361       // and now load features
2362       param = applet.getParameter("features");
2363       if (param != null)
2364       {
2365         param = resolveFileProtocol(param);
2366
2367         result = alignFrame.parseFeaturesFile(param, protocol);
2368       }
2369
2370       param = applet.getParameter("showFeatureSettings");
2371       if (param != null && param.equalsIgnoreCase(TRUE))
2372       {
2373         alignFrame.viewport.setShowSequenceFeatures(true);
2374         new FeatureSettings(alignFrame.alignPanel);
2375       }
2376       return result;
2377     }
2378
2379     /**
2380      * Load a score file if specified by parameter. Returns true if file was
2381      * loaded, else false.
2382      * 
2383      * @param alignFrame
2384      */
2385     protected boolean loadScoreFile(AlignFrame alignFrame)
2386     {
2387       boolean result = false;
2388       String sScoreFile = applet.getParameter("scoreFile");
2389       if (sScoreFile != null && !"".equals(sScoreFile))
2390       {
2391         try
2392         {
2393           if (debug)
2394           {
2395             System.err.println(
2396                     "Attempting to load T-COFFEE score file from the scoreFile parameter");
2397           }
2398           result = alignFrame.loadScoreFile(sScoreFile);
2399           if (!result)
2400           {
2401             System.err.println(
2402                     "Failed to parse T-COFFEE parameter as a valid score file ('"
2403                             + sScoreFile + "')");
2404           }
2405         } catch (Exception e)
2406         {
2407           System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
2408                   sScoreFile, e.getMessage());
2409         }
2410       }
2411       return result;
2412     }
2413
2414     /**
2415      * Load a tree for the alignment if specified by parameter. Returns true if
2416      * a tree was loaded, else false.
2417      * 
2418      * @param alignFrame
2419      * @return
2420      */
2421     protected boolean loadTree(AlignFrame alignFrame)
2422     {
2423       boolean result = false;
2424       String treeFile = applet.getParameter("tree");
2425       if (treeFile == null)
2426       {
2427         treeFile = applet.getParameter("treeFile");
2428       }
2429
2430       if (treeFile != null)
2431       {
2432         try
2433         {
2434           treeFile = resolveFileProtocol(treeFile);
2435           NewickFile fin = new NewickFile(treeFile, protocol);
2436           fin.parse();
2437
2438           if (fin.getTree() != null)
2439           {
2440             alignFrame.loadTree(fin, treeFile);
2441             result = true;
2442             dbgMsg("Successfully imported tree.");
2443           }
2444           else
2445           {
2446             dbgMsg("Tree parameter did not resolve to a valid tree.");
2447           }
2448         } catch (Exception ex)
2449         {
2450           ex.printStackTrace();
2451         }
2452       }
2453       return result;
2454     }
2455
2456     /**
2457      * Discovers whether the given file is in the Applet Archive
2458      * 
2459      * @param f
2460      *          String
2461      * @return boolean
2462      */
2463     boolean inArchive(String f)
2464     {
2465       // This might throw a security exception in certain browsers
2466       // Netscape Communicator for instance.
2467       try
2468       {
2469         boolean rtn = (getClass().getResourceAsStream("/" + f) != null);
2470         if (debug)
2471         {
2472           System.err.println("Resource '" + f + "' was "
2473                   + (rtn ? "" : "not ") + "located by classloader.");
2474         }
2475         return rtn;
2476       } catch (Exception ex)
2477       {
2478         System.out.println("Exception checking resources: " + f + " " + ex);
2479         return false;
2480       }
2481     }
2482   }
2483
2484   /**
2485    * @return the default alignFrame acted on by the public applet methods. May
2486    *         return null with an error message on System.err indicating the
2487    *         fact.
2488    */
2489   public AlignFrame getDefaultTargetFrame()
2490   {
2491     if (currentAlignFrame != null)
2492     {
2493       return currentAlignFrame;
2494     }
2495     if (initialAlignFrame != null)
2496     {
2497       return initialAlignFrame;
2498     }
2499     System.err.println(
2500             "Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
2501     return null;
2502   }
2503
2504   /**
2505    * separator used for separatorList
2506    */
2507   protected String separator = "" + ((char) 0x00AC); // the default used to be
2508                                                      // '|' but many sequence
2509                                                      // IDS include pipes.
2510
2511   /**
2512    * set to enable the URL based javascript execution mechanism
2513    */
2514   public boolean jsfallbackEnabled = false;
2515
2516   /**
2517    * parse the string into a list
2518    * 
2519    * @param list
2520    * @return elements separated by separator
2521    */
2522   public String[] separatorListToArray(String list)
2523   {
2524     return separatorListToArray(list, separator);
2525   }
2526
2527   /**
2528    * parse the string into a list
2529    * 
2530    * @param list
2531    * @param separator
2532    * @return elements separated by separator
2533    */
2534   public static String[] separatorListToArray(String list, String separator)
2535   {
2536     // TODO use StringUtils version (slightly different...)
2537     int seplen = separator.length();
2538     if (list == null || list.equals("") || list.equals(separator))
2539     {
2540       return null;
2541     }
2542     java.util.Vector jv = new Vector();
2543     int cp = 0, pos;
2544     while ((pos = list.indexOf(separator, cp)) > cp)
2545     {
2546       jv.addElement(list.substring(cp, pos));
2547       cp = pos + seplen;
2548     }
2549     if (cp < list.length())
2550     {
2551       String c = list.substring(cp);
2552       if (!c.equals(separator))
2553       {
2554         jv.addElement(c);
2555       }
2556     }
2557     if (jv.size() > 0)
2558     {
2559       String[] v = new String[jv.size()];
2560       for (int i = 0; i < v.length; i++)
2561       {
2562         v[i] = (String) jv.elementAt(i);
2563       }
2564       jv.removeAllElements();
2565       if (debug)
2566       {
2567         System.err.println("Array from '" + separator
2568                 + "' separated List:\n" + v.length);
2569         for (int i = 0; i < v.length; i++)
2570         {
2571           System.err.println("item " + i + " '" + v[i] + "'");
2572         }
2573       }
2574       return v;
2575     }
2576     if (debug)
2577     {
2578       System.err.println(
2579               "Empty Array from '" + separator + "' separated List");
2580     }
2581     return null;
2582   }
2583
2584   /**
2585    * concatenate the list with separator
2586    * 
2587    * @param list
2588    * @return concatenated string
2589    */
2590   public String arrayToSeparatorList(String[] list)
2591   {
2592     return arrayToSeparatorList(list, separator);
2593   }
2594
2595   /**
2596    * concatenate the list with separator
2597    * 
2598    * @param list
2599    * @param separator
2600    * @return concatenated string
2601    */
2602   public static String arrayToSeparatorList(String[] list, String separator)
2603   {
2604     // TODO use StringUtils version
2605     StringBuffer v = new StringBuffer();
2606     if (list != null && list.length > 0)
2607     {
2608       for (int i = 0, iSize = list.length; i < iSize; i++)
2609       {
2610         if (list[i] != null)
2611         {
2612           if (i > 0)
2613           {
2614             v.append(separator);
2615           }
2616           v.append(list[i]);
2617         }
2618       }
2619       if (debug)
2620       {
2621         System.err
2622                 .println("Returning '" + separator + "' separated List:\n");
2623         System.err.println(v);
2624       }
2625       return v.toString();
2626     }
2627     if (debug)
2628     {
2629       System.err.println(
2630               "Returning empty '" + separator + "' separated List\n");
2631     }
2632     return "" + separator;
2633   }
2634
2635   /*
2636    * (non-Javadoc)
2637    * 
2638    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroups()
2639    */
2640   @Override
2641   public String getFeatureGroups()
2642   {
2643     String lst = arrayToSeparatorList(
2644             getDefaultTargetFrame().getFeatureGroups());
2645     return lst;
2646   }
2647
2648   /*
2649    * (non-Javadoc)
2650    * 
2651    * @see
2652    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOn(jalview.appletgui.AlignFrame
2653    * )
2654    */
2655   @Override
2656   public String getFeatureGroupsOn(AlignFrame alf)
2657   {
2658     String lst = arrayToSeparatorList(alf.getFeatureGroups());
2659     return lst;
2660   }
2661
2662   /*
2663    * (non-Javadoc)
2664    * 
2665    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfState(boolean)
2666    */
2667   @Override
2668   public String getFeatureGroupsOfState(boolean visible)
2669   {
2670     return arrayToSeparatorList(
2671             getDefaultTargetFrame().getFeatureGroupsOfState(visible));
2672   }
2673
2674   /*
2675    * (non-Javadoc)
2676    * 
2677    * @see
2678    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfStateOn(jalview.appletgui
2679    * .AlignFrame, boolean)
2680    */
2681   @Override
2682   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
2683   {
2684     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
2685   }
2686
2687   /*
2688    * (non-Javadoc)
2689    * 
2690    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupStateOn(jalview.appletgui.
2691    * AlignFrame, java.lang.String, boolean)
2692    */
2693   @Override
2694   public void setFeatureGroupStateOn(final AlignFrame alf,
2695           final String groups, boolean state)
2696   {
2697     final boolean st = state;// !(state==null || state.equals("") ||
2698     // state.toLowerCase(Locale.ROOT).equals("false"));
2699     java.awt.EventQueue.invokeLater(new Runnable()
2700     {
2701       @Override
2702       public void run()
2703       {
2704         alf.setFeatureGroupState(separatorListToArray(groups), st);
2705       }
2706     });
2707   }
2708
2709   /*
2710    * (non-Javadoc)
2711    * 
2712    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupState(java.lang.String,
2713    * boolean)
2714    */
2715   @Override
2716   public void setFeatureGroupState(String groups, boolean state)
2717   {
2718     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
2719   }
2720
2721   /*
2722    * (non-Javadoc)
2723    * 
2724    * @see jalview.bin.JalviewLiteJsApi#getSeparator()
2725    */
2726   @Override
2727   public String getSeparator()
2728   {
2729     return separator;
2730   }
2731
2732   /*
2733    * (non-Javadoc)
2734    * 
2735    * @see jalview.bin.JalviewLiteJsApi#setSeparator(java.lang.String)
2736    */
2737   @Override
2738   public void setSeparator(String separator)
2739   {
2740     if (separator == null || separator.length() < 1)
2741     {
2742       // reset to default
2743       separator = "" + ((char) 0x00AC);
2744     }
2745     this.separator = separator;
2746     if (debug)
2747     {
2748       System.err.println("Default Separator now: '" + separator + "'");
2749     }
2750   }
2751
2752   /**
2753    * get boolean value of applet parameter 'name' and return default if
2754    * parameter is not set
2755    * 
2756    * @param name
2757    *          name of paremeter
2758    * @param def
2759    *          the value to return otherwise
2760    * @return true or false
2761    */
2762   public boolean getDefaultParameter(String name, boolean def)
2763   {
2764     String stn;
2765     if ((stn = getParameter(name)) == null)
2766     {
2767       return def;
2768     }
2769     if (TRUE.equalsIgnoreCase(stn))
2770     {
2771       return true;
2772     }
2773     return false;
2774   }
2775
2776   /*
2777    * (non-Javadoc)
2778    * 
2779    * @see jalview.bin.JalviewLiteJsApi#addPdbFile(jalview.appletgui.AlignFrame,
2780    * java.lang.String, java.lang.String, java.lang.String)
2781    */
2782   @Override
2783   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
2784           String pdbEntryString, String pdbFile)
2785   {
2786     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
2787   }
2788
2789   protected void setAlignPdbStructures(boolean alignPdbStructures)
2790   {
2791     this.alignPdbStructures = alignPdbStructures;
2792   }
2793
2794   public boolean isAlignPdbStructures()
2795   {
2796     return alignPdbStructures;
2797   }
2798
2799   @Override
2800   public void start()
2801   {
2802     // callInitCallback();
2803   }
2804
2805   private Hashtable<String, long[]> jshashes = new Hashtable<>();
2806
2807   private Hashtable<String, Hashtable<String, String[]>> jsmessages = new Hashtable<>();
2808
2809   public void setJsMessageSet(String messageclass, String viewId,
2810           String[] colcommands)
2811   {
2812     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2813     if (msgset == null)
2814     {
2815       msgset = new Hashtable<>();
2816       jsmessages.put(messageclass, msgset);
2817     }
2818     msgset.put(viewId, colcommands);
2819     long[] l = new long[colcommands.length];
2820     for (int i = 0; i < colcommands.length; i++)
2821     {
2822       l[i] = colcommands[i].hashCode();
2823     }
2824     jshashes.put(messageclass + "|" + viewId, l);
2825   }
2826
2827   /*
2828    * (non-Javadoc)
2829    * 
2830    * @see jalview.bin.JalviewLiteJsApi#getJsMessage(java.lang.String,
2831    * java.lang.String)
2832    */
2833   @Override
2834   public String getJsMessage(String messageclass, String viewId)
2835   {
2836     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2837     if (msgset != null)
2838     {
2839       String[] msgs = msgset.get(viewId);
2840       if (msgs != null)
2841       {
2842         for (int i = 0; i < msgs.length; i++)
2843         {
2844           if (msgs[i] != null)
2845           {
2846             String m = msgs[i];
2847             msgs[i] = null;
2848             return m;
2849           }
2850         }
2851       }
2852     }
2853     return "";
2854   }
2855
2856   public boolean isJsMessageSetChanged(String string, String string2,
2857           String[] colcommands)
2858   {
2859     long[] l = jshashes.get(string + "|" + string2);
2860     if (l == null && colcommands != null)
2861     {
2862       return true;
2863     }
2864     for (int i = 0; i < colcommands.length; i++)
2865     {
2866       if (l[i] != colcommands[i].hashCode())
2867       {
2868         return true;
2869       }
2870     }
2871     return false;
2872   }
2873
2874   private Vector jsExecQueue = new Vector();
2875
2876   public Vector getJsExecQueue()
2877   {
2878     return jsExecQueue;
2879   }
2880
2881   public void setExecutor(JSFunctionExec jsFunctionExec2)
2882   {
2883     jsFunctionExec = jsFunctionExec2;
2884   }
2885
2886   /**
2887    * return the given colour value parameter or the given default if parameter
2888    * not given
2889    * 
2890    * @param colparam
2891    * @param defcolour
2892    * @return
2893    */
2894   public Color getDefaultColourParameter(String colparam, Color defcolour)
2895   {
2896     String colprop = getParameter(colparam);
2897     if (colprop == null || colprop.trim().length() == 0)
2898     {
2899       return defcolour;
2900     }
2901     Color col = ColorUtils.parseColourString(colprop);
2902     if (col == null)
2903     {
2904       System.err.println("Couldn't parse '" + colprop + "' as a colour for "
2905               + colparam);
2906     }
2907     return (col == null) ? defcolour : col;
2908   }
2909
2910   public void openJalviewHelpUrl()
2911   {
2912     String helpUrl = getParameter("jalviewhelpurl");
2913     if (helpUrl == null || helpUrl.trim().length() < 5)
2914     {
2915       helpUrl = "http://www.jalview.org/help.html";
2916     }
2917     showURL(helpUrl, "HELP");
2918   }
2919
2920   /**
2921    * form a complete URL given a path to a resource and a reference location on
2922    * the same server
2923    * 
2924    * @param targetPath
2925    *          - an absolute path on the same server as localref or a document
2926    *          located relative to localref
2927    * @param localref
2928    *          - a URL on the same server as url
2929    * @return a complete URL for the resource located by url
2930    */
2931   private String resolveUrlForLocalOrAbsolute(String targetPath,
2932           URL localref)
2933   {
2934     String resolvedPath = "";
2935     if (targetPath.startsWith("/"))
2936     {
2937       String codebase = localref.toString();
2938       String localfile = localref.getFile();
2939       resolvedPath = codebase.substring(0,
2940               codebase.length() - localfile.length()) + targetPath;
2941       return resolvedPath;
2942     }
2943
2944     /*
2945      * get URL path and strip off any trailing file e.g.
2946      * www.jalview.org/examples/index.html#applets?a=b is trimmed to
2947      * www.jalview.org/examples/
2948      */
2949     String urlPath = localref.toString();
2950     String directoryPath = urlPath;
2951     int lastSeparator = directoryPath.lastIndexOf("/");
2952     if (lastSeparator > 0)
2953     {
2954       directoryPath = directoryPath.substring(0, lastSeparator + 1);
2955     }
2956
2957     if (targetPath.startsWith("/"))
2958     {
2959       /*
2960        * construct absolute URL to a file on the server - this is not allowed?
2961        */
2962       // String localfile = localref.getFile();
2963       // resolvedPath = urlPath.substring(0,
2964       // urlPath.length() - localfile.length())
2965       // + targetPath;
2966       resolvedPath = directoryPath + targetPath.substring(1);
2967     }
2968     else
2969     {
2970       resolvedPath = directoryPath + targetPath;
2971     }
2972     if (debug)
2973     {
2974       System.err.println(
2975               "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
2976     }
2977     return resolvedPath;
2978   }
2979
2980   /**
2981    * open a URL in the browser - resolving it according to relative refs and
2982    * coping with javascript: protocol if necessary.
2983    * 
2984    * @param url
2985    * @param target
2986    */
2987   public void showURL(String url, String target)
2988   {
2989     try
2990     {
2991       if (url.indexOf(":") == -1)
2992       {
2993         // TODO: verify (Bas Vroling bug) prepend codebase or server URL to
2994         // form valid URL
2995         // Should really use docbase, not codebase.
2996         URL prepend;
2997         url = resolveUrlForLocalOrAbsolute(url,
2998                 prepend = getDefaultParameter("resolvetocodebase", false)
2999                         ? getCodeBase()
3000                         : getDocumentBase());
3001         if (debug)
3002         {
3003           System.err.println("Show url (prepended " + prepend
3004                   + " - toggle resolvetocodebase if code/docbase resolution is wrong): "
3005                   + url);
3006         }
3007       }
3008       else
3009       {
3010         if (debug)
3011         {
3012           System.err.println("Show url: " + url);
3013         }
3014       }
3015       if (url.indexOf("javascript:") == 0)
3016       {
3017         // no target for the javascript context
3018         getAppletContext().showDocument(new java.net.URL(url));
3019       }
3020       else
3021       {
3022         getAppletContext().showDocument(new java.net.URL(url), target);
3023       }
3024     } catch (Exception ex)
3025     {
3026       ex.printStackTrace();
3027     }
3028   }
3029
3030   /**
3031    * bind structures in a viewer to any matching sequences in an alignFrame (use
3032    * sequenceIds to limit scope of search to specific sequences)
3033    * 
3034    * @param alFrame
3035    * @param viewer
3036    * @param sequenceIds
3037    * @return TODO: consider making an exception structure for indicating when
3038    *         binding fails public SequenceStructureBinding
3039    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
3040    *         sequenceIds) {
3041    * 
3042    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
3043    *         alFrame.addStructureViewInstance(viewer,
3044    *         separatorListToArray(sequenceIds)); } else { return
3045    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
3046    */
3047 }