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