JAL-845 code/refactoring/tests related to linking DNA and protein
[jalview.git] / src / jalview / bin / 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.List;
64 import java.util.StringTokenizer;
65 import java.util.Vector;
66
67 import netscape.javascript.JSException;
68 import netscape.javascript.JSObject;
69
70 /**
71  * Jalview Applet. Runs in Java 1.18 runtime
72  * 
73  * @author $author$
74  * @version $Revision: 1.92 $
75  */
76 public class JalviewLite extends Applet implements
77         StructureSelectionManagerProvider, JalviewLiteJsApi
78 {
79
80   public StructureSelectionManager getStructureSelectionManager()
81   {
82     return StructureSelectionManager.getStructureSelectionManager(this);
83   }
84
85   // /////////////////////////////////////////
86   // The following public methods maybe called
87   // externally, eg via javascript in HTML page
88   /*
89    * (non-Javadoc)
90    * 
91    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
92    */
93   public String getSelectedSequences()
94   {
95     return getSelectedSequencesFrom(getDefaultTargetFrame());
96   }
97
98   /*
99    * (non-Javadoc)
100    * 
101    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
102    */
103   public String getSelectedSequences(String sep)
104   {
105     return getSelectedSequencesFrom(getDefaultTargetFrame(), sep);
106   }
107
108   /*
109    * (non-Javadoc)
110    * 
111    * @see
112    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
113    * .AlignFrame)
114    */
115   public String getSelectedSequencesFrom(AlignFrame alf)
116   {
117     return getSelectedSequencesFrom(alf, separator); // ""+0x00AC);
118   }
119
120   /*
121    * (non-Javadoc)
122    * 
123    * @see
124    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
125    * .AlignFrame, java.lang.String)
126    */
127   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
128   {
129     StringBuffer result = new StringBuffer("");
130     if (sep == null || sep.length() == 0)
131     {
132       sep = separator; // "+0x00AC;
133     }
134     if (alf.viewport.getSelectionGroup() != null)
135     {
136       SequenceI[] seqs = alf.viewport.getSelectionGroup()
137               .getSequencesInOrder(alf.viewport.getAlignment());
138
139       for (int i = 0; i < seqs.length; i++)
140       {
141         result.append(seqs[i].getName());
142         result.append(sep);
143       }
144     }
145
146     return result.toString();
147   }
148
149   /*
150    * (non-Javadoc)
151    * 
152    * @see jalview.bin.JalviewLiteJsApi#highlight(java.lang.String,
153    * java.lang.String, java.lang.String)
154    */
155   public void highlight(String sequenceId, String position,
156           String alignedPosition)
157   {
158     highlightIn(getDefaultTargetFrame(), sequenceId, position,
159             alignedPosition);
160   }
161
162   /*
163    * (non-Javadoc)
164    * 
165    * @see jalview.bin.JalviewLiteJsApi#highlightIn(jalview.appletgui.AlignFrame,
166    * java.lang.String, java.lang.String, java.lang.String)
167    */
168   public void highlightIn(final AlignFrame alf, final String sequenceId,
169           final String position, final String alignedPosition)
170   {
171     // TODO: could try to highlight in all alignments if alf==null
172     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
173             alf.viewport.getAlignment().getSequencesArray());
174     final SequenceI sq = matcher.findIdMatch(sequenceId);
175     if (sq != null)
176     {
177       int apos = -1;
178       try
179       {
180         apos = new Integer(position).intValue();
181         apos--;
182       } catch (NumberFormatException ex)
183       {
184         return;
185       }
186       final StructureSelectionManagerProvider me = this;
187       final int pos = apos;
188       // use vamsas listener to broadcast to all listeners in scope
189       if (alignedPosition != null
190               && (alignedPosition.trim().length() == 0 || alignedPosition
191                       .toLowerCase().indexOf("false") > -1))
192       {
193         java.awt.EventQueue.invokeLater(new Runnable()
194         {
195           @Override
196           public void run()
197           {
198             StructureSelectionManager.getStructureSelectionManager(me)
199                     .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
200           }
201         });
202       }
203       else
204       {
205         java.awt.EventQueue.invokeLater(new Runnable()
206         {
207           @Override
208           public void run()
209           {
210             StructureSelectionManager.getStructureSelectionManager(me)
211                     .mouseOverVamsasSequence(sq, pos, null);
212           }
213         });
214       }
215     }
216   }
217
218   /*
219    * (non-Javadoc)
220    * 
221    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
222    * java.lang.String)
223    */
224   public void select(String sequenceIds, String columns)
225   {
226     selectIn(getDefaultTargetFrame(), sequenceIds, columns, separator);
227   }
228
229   /*
230    * (non-Javadoc)
231    * 
232    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
233    * java.lang.String, java.lang.String)
234    */
235   public void select(String sequenceIds, String columns, String sep)
236   {
237     selectIn(getDefaultTargetFrame(), sequenceIds, columns, sep);
238   }
239
240   /*
241    * (non-Javadoc)
242    * 
243    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
244    * java.lang.String, java.lang.String)
245    */
246   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
247   {
248     selectIn(alf, sequenceIds, columns, separator);
249   }
250
251   /*
252    * (non-Javadoc)
253    * 
254    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
255    * java.lang.String, java.lang.String, java.lang.String)
256    */
257   public void selectIn(final AlignFrame alf, String sequenceIds,
258           String columns, String sep)
259   {
260     if (sep == null || sep.length() == 0)
261     {
262       sep = separator;
263     }
264     else
265     {
266       if (debug)
267       {
268         System.err.println("Selecting region using separator string '"
269                 + separator + "'");
270       }
271     }
272     // deparse fields
273     String[] ids = separatorListToArray(sequenceIds, sep);
274     String[] cols = separatorListToArray(columns, sep);
275     final SequenceGroup sel = new SequenceGroup();
276     final ColumnSelection csel = new ColumnSelection();
277     AlignmentI al = alf.viewport.getAlignment();
278     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
279             alf.viewport.getAlignment().getSequencesArray());
280     int start = 0, end = al.getWidth(), alw = al.getWidth();
281     boolean seqsfound = true;
282     if (ids != null && ids.length > 0)
283     {
284       seqsfound = false;
285       for (int i = 0; i < ids.length; i++)
286       {
287         if (ids[i].trim().length() == 0)
288         {
289           continue;
290         }
291         SequenceI sq = matcher.findIdMatch(ids[i]);
292         if (sq != null)
293         {
294           seqsfound = true;
295           sel.addSequence(sq, false);
296         }
297       }
298     }
299     boolean inseqpos = false;
300     if (cols != null && cols.length > 0)
301     {
302       boolean seset = false;
303       for (int i = 0; i < cols.length; i++)
304       {
305         String cl = cols[i].trim();
306         if (cl.length() == 0)
307         {
308           continue;
309         }
310         int p;
311         if ((p = cl.indexOf("-")) > -1)
312         {
313           int from = -1, to = -1;
314           try
315           {
316             from = new Integer(cl.substring(0, p)).intValue();
317             from--;
318           } catch (NumberFormatException ex)
319           {
320             System.err
321                     .println("ERROR: Couldn't parse first integer in range element column selection string '"
322                             + cl + "' - format is 'from-to'");
323             return;
324           }
325           try
326           {
327             to = new Integer(cl.substring(p + 1)).intValue();
328             to--;
329           } catch (NumberFormatException ex)
330           {
331             System.err
332                     .println("ERROR: Couldn't parse second integer in range element column selection string '"
333                             + cl + "' - format is 'from-to'");
334             return;
335           }
336           if (from >= 0 && to >= 0)
337           {
338             // valid range
339             if (from < to)
340             {
341               int t = to;
342               to = from;
343               to = t;
344             }
345             if (!seset)
346             {
347               start = from;
348               end = to;
349               seset = true;
350             }
351             else
352             {
353               // comment to prevent range extension
354               if (start > from)
355               {
356                 start = from;
357               }
358               if (end < to)
359               {
360                 end = to;
361               }
362             }
363             for (int r = from; r <= to; r++)
364             {
365               if (r >= 0 && r < alw)
366               {
367                 csel.addElement(r);
368               }
369             }
370             if (debug)
371             {
372               System.err.println("Range '" + cl + "' deparsed as [" + from
373                       + "," + to + "]");
374             }
375           }
376           else
377           {
378             System.err.println("ERROR: Invalid Range '" + cl
379                     + "' deparsed as [" + from + "," + to + "]");
380           }
381         }
382         else
383         {
384           int r = -1;
385           try
386           {
387             r = new Integer(cl).intValue();
388             r--;
389           } catch (NumberFormatException ex)
390           {
391             if (cl.toLowerCase().equals("sequence"))
392             {
393               // we are in the dataset sequence's coordinate frame.
394               inseqpos = true;
395             }
396             else
397             {
398               System.err
399                       .println("ERROR: Couldn't parse integer from point selection element of column selection string '"
400                               + cl + "'");
401               return;
402             }
403           }
404           if (r >= 0 && r <= alw)
405           {
406             if (!seset)
407             {
408               start = r;
409               end = r;
410               seset = true;
411             }
412             else
413             {
414               // comment to prevent range extension
415               if (start > r)
416               {
417                 start = r;
418               }
419               if (end < r)
420               {
421                 end = r;
422               }
423             }
424             csel.addElement(r);
425             if (debug)
426             {
427               System.err.println("Point selection '" + cl
428                       + "' deparsed as [" + r + "]");
429             }
430           }
431           else
432           {
433             System.err.println("ERROR: Invalid Point selection '" + cl
434                     + "' deparsed as [" + r + "]");
435           }
436         }
437       }
438     }
439     if (seqsfound)
440     {
441       // we only propagate the selection when it was the null selection, or the
442       // given sequences were found in the alignment.
443       if (inseqpos && sel.getSize() > 0)
444       {
445         // assume first sequence provides reference frame ?
446         SequenceI rs = sel.getSequenceAt(0);
447         start = rs.findIndex(start);
448         end = rs.findIndex(end);
449         if (csel != null)
450         {
451           List<Integer> cs = csel.getSelected();
452           csel.clear();
453           for (Integer selectedCol : cs)
454           {
455             csel.addElement(rs.findIndex(selectedCol));
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, installation = null;
1273
1274   private static void initBuildDetails()
1275   {
1276     if (builddate == null)
1277     {
1278       builddate = "unknown";
1279       version = "test";
1280       installation = "Webstart";
1281       java.net.URL url = JalviewLite.class
1282               .getResource("/.build_properties");
1283       if (url != null)
1284       {
1285         try
1286         {
1287           BufferedReader reader = new BufferedReader(new InputStreamReader(
1288                   url.openStream()));
1289           String line;
1290           while ((line = reader.readLine()) != null)
1291           {
1292             if (line.indexOf("VERSION") > -1)
1293             {
1294               version = line.substring(line.indexOf("=") + 1);
1295             }
1296             if (line.indexOf("BUILD_DATE") > -1)
1297             {
1298               builddate = line.substring(line.indexOf("=") + 1);
1299             }
1300             if (line.indexOf("INSTALLATION") > -1)
1301             {
1302               installation = line.substring(line.indexOf("=") + 1);
1303             }
1304           }
1305         } catch (Exception ex)
1306         {
1307           ex.printStackTrace();
1308         }
1309       }
1310     }
1311   }
1312
1313   public static String getBuildDate()
1314   {
1315     initBuildDetails();
1316     return builddate;
1317   }
1318
1319   public static String getInstallation()
1320   {
1321     initBuildDetails();
1322     return installation;
1323   }
1324
1325   public static String getVersion()
1326   {
1327     initBuildDetails();
1328     return version;
1329   }
1330
1331   // public JSObject scriptObject = null;
1332
1333   /**
1334    * init method for Jalview Applet
1335    */
1336   public void init()
1337   {
1338     // remove any handlers that might be hanging around from an earlier instance
1339     try
1340     {
1341       if (debug)
1342       {
1343         System.err.println("Applet context is '"
1344                 + getAppletContext().getClass().toString() + "'");
1345       }
1346       JSObject scriptObject = JSObject.getWindow(this);
1347       if (debug && scriptObject != null)
1348       {
1349         System.err.println("Applet has Javascript callback support.");
1350       }
1351
1352     } catch (Exception ex)
1353     {
1354       System.err
1355               .println("Warning: No JalviewLite javascript callbacks available.");
1356       if (debug)
1357       {
1358         ex.printStackTrace();
1359       }
1360     }
1361     /**
1362      * turn on extra applet debugging
1363      */
1364     String dbg = getParameter("debug");
1365     if (dbg != null)
1366     {
1367       debug = dbg.toLowerCase().equals("true");
1368     }
1369     if (debug)
1370     {
1371
1372       System.err.println("JalviewLite Version " + getVersion());
1373       System.err.println("Build Date : " + getBuildDate());
1374       System.err.println("Installation : " + getInstallation());
1375
1376     }
1377     String externalsviewer = getParameter("externalstructureviewer");
1378     if (externalsviewer != null)
1379     {
1380       useXtrnalSviewer = externalsviewer.trim().toLowerCase()
1381               .equals("true");
1382     }
1383     /**
1384      * if true disable the check for jmol
1385      */
1386     String chkforJmol = getParameter("nojmol");
1387     if (chkforJmol != null)
1388     {
1389       checkForJmol = !chkforJmol.equals("true");
1390     }
1391     /**
1392      * get the separator parameter if present
1393      */
1394     String sep = getParameter("separator");
1395     if (sep != null)
1396     {
1397       if (sep.length() > 0)
1398       {
1399         separator = sep;
1400         if (debug)
1401         {
1402           System.err.println("Separator set to '" + separator + "'");
1403         }
1404       }
1405       else
1406       {
1407         throw new Error(MessageManager.getString("error.invalid_separator_parameter"));
1408       }
1409     }
1410     int r = 255;
1411     int g = 255;
1412     int b = 255;
1413     String param = getParameter("RGB");
1414
1415     if (param != null)
1416     {
1417       try
1418       {
1419         r = Integer.parseInt(param.substring(0, 2), 16);
1420         g = Integer.parseInt(param.substring(2, 4), 16);
1421         b = Integer.parseInt(param.substring(4, 6), 16);
1422       } catch (Exception ex)
1423       {
1424         r = 255;
1425         g = 255;
1426         b = 255;
1427       }
1428     }
1429     param = getParameter("label");
1430     if (param != null)
1431     {
1432       launcher.setLabel(param);
1433     }
1434
1435     setBackground(new Color(r, g, b));
1436
1437     file = getParameter("file");
1438
1439     if (file == null)
1440     {
1441       // Maybe the sequences are added as parameters
1442       StringBuffer data = new StringBuffer("PASTE");
1443       int i = 1;
1444       while ((file = getParameter("sequence" + i)) != null)
1445       {
1446         data.append(file.toString() + "\n");
1447         i++;
1448       }
1449       if (data.length() > 5)
1450       {
1451         file = data.toString();
1452       }
1453     }
1454
1455     final JalviewLite jvapplet = this;
1456     if (getParameter("embedded") != null
1457             && getParameter("embedded").equalsIgnoreCase("true"))
1458     {
1459       // Launch as embedded applet in page
1460       embedded = true;
1461       LoadingThread loader = new LoadingThread(file, jvapplet);
1462       loader.start();
1463     }
1464     else if (file != null)
1465     {
1466       if (getParameter("showbutton") == null
1467               || !getParameter("showbutton").equalsIgnoreCase("false"))
1468       {
1469         // Add the JalviewLite 'Button' to the page
1470         add(launcher);
1471         launcher.addActionListener(new java.awt.event.ActionListener()
1472         {
1473           public void actionPerformed(ActionEvent e)
1474           {
1475             LoadingThread loader = new LoadingThread(file, jvapplet);
1476             loader.start();
1477           }
1478         });
1479       }
1480       else
1481       {
1482         // Open jalviewLite immediately.
1483         LoadingThread loader = new LoadingThread(file, jvapplet);
1484         loader.start();
1485       }
1486     }
1487     else
1488     {
1489       // jalview initialisation with no alignment. loadAlignment() method can
1490       // still be called to open new alignments.
1491       file = "NO FILE";
1492       fileFound = false;
1493       callInitCallback();
1494     }
1495   }
1496
1497   private void initLiveConnect()
1498   {
1499     // try really hard to get the liveConnect thing working
1500     boolean notFailed = false;
1501     int tries = 0;
1502     while (!notFailed && tries < 10)
1503     {
1504       if (tries > 0)
1505       {
1506         System.err.println("LiveConnect request thread going to sleep.");
1507       }
1508       try
1509       {
1510         Thread.sleep(700 * (1 + tries));
1511       } catch (InterruptedException q)
1512       {
1513       }
1514       ;
1515       if (tries++ > 0)
1516       {
1517         System.err.println("LiveConnect request thread woken up.");
1518       }
1519       try
1520       {
1521         JSObject scriptObject = JSObject.getWindow(this);
1522         if (scriptObject.eval("navigator") != null)
1523         {
1524           notFailed = true;
1525         }
1526       } catch (JSException jsex)
1527       {
1528         System.err.println("Attempt " + tries
1529                 + " to access LiveConnect javascript failed.");
1530       }
1531     }
1532   }
1533
1534   private void callInitCallback()
1535   {
1536     String initjscallback = getParameter("oninit");
1537     if (initjscallback == null)
1538     {
1539       return;
1540     }
1541     initjscallback = initjscallback.trim();
1542     if (initjscallback.length() > 0)
1543     {
1544       JSObject scriptObject = null;
1545       try
1546       {
1547         scriptObject = JSObject.getWindow(this);
1548       } catch (Exception ex)
1549       {
1550       }
1551       ;
1552       // try really hard to let the browser plugin know we want liveconnect
1553       initLiveConnect();
1554
1555       if (scriptObject != null)
1556       {
1557         try
1558         {
1559           // do onInit with the JS executor thread
1560           new JSFunctionExec(this).executeJavascriptFunction(true,
1561                   initjscallback, null, "Calling oninit callback '"
1562                           + initjscallback + "'.");
1563         } catch (Exception e)
1564         {
1565           System.err.println("Exception when executing _oninit callback '"
1566                   + initjscallback + "'.");
1567           e.printStackTrace();
1568         }
1569       }
1570       else
1571       {
1572         System.err.println("Not executing _oninit callback '"
1573                 + initjscallback + "' - no scripting allowed.");
1574       }
1575     }
1576   }
1577
1578   /**
1579    * Initialises and displays a new java.awt.Frame
1580    * 
1581    * @param frame
1582    *          java.awt.Frame to be displayed
1583    * @param title
1584    *          title of new frame
1585    * @param width
1586    *          width if new frame
1587    * @param height
1588    *          height of new frame
1589    */
1590   public static void addFrame(final Frame frame, String title, int width,
1591           int height)
1592   {
1593     frame.setLocation(lastFrameX, lastFrameY);
1594     lastFrameX += 40;
1595     lastFrameY += 40;
1596     frame.setSize(width, height);
1597     frame.setTitle(title);
1598     frame.addWindowListener(new WindowAdapter()
1599     {
1600       public void windowClosing(WindowEvent e)
1601       {
1602         if (frame instanceof AlignFrame)
1603         {
1604           AlignViewport vp = ((AlignFrame) frame).viewport;
1605           ((AlignFrame) frame).closeMenuItem_actionPerformed();
1606           if (vp.applet.currentAlignFrame == frame)
1607           {
1608             vp.applet.currentAlignFrame = null;
1609           }
1610           vp.applet = null;
1611           vp = null;
1612
1613         }
1614         lastFrameX -= 40;
1615         lastFrameY -= 40;
1616         if (frame instanceof EmbmenuFrame)
1617         {
1618           ((EmbmenuFrame) frame).destroyMenus();
1619         }
1620         frame.setMenuBar(null);
1621         frame.dispose();
1622       }
1623
1624       public void windowActivated(WindowEvent e)
1625       {
1626         if (frame instanceof AlignFrame)
1627         {
1628           ((AlignFrame) frame).viewport.applet.currentAlignFrame = (AlignFrame) frame;
1629           if (debug)
1630           {
1631             System.err.println("Activated window " + frame);
1632           }
1633         }
1634         // be good.
1635         super.windowActivated(e);
1636       }
1637       /*
1638        * Probably not necessary to do this - see TODO above. (non-Javadoc)
1639        * 
1640        * @see
1641        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
1642        * )
1643        * 
1644        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
1645        * frame) { currentAlignFrame = null; if (debug) {
1646        * System.err.println("Deactivated window "+frame); } }
1647        * super.windowDeactivated(e); }
1648        */
1649     });
1650     frame.setVisible(true);
1651   }
1652
1653   /**
1654    * This paints the background surrounding the "Launch Jalview button" <br>
1655    * <br>
1656    * If file given in parameter not found, displays error message
1657    * 
1658    * @param g
1659    *          graphics context
1660    */
1661   public void paint(Graphics g)
1662   {
1663     if (!fileFound)
1664     {
1665       g.setColor(new Color(200, 200, 200));
1666       g.setColor(Color.cyan);
1667       g.fillRect(0, 0, getSize().width, getSize().height);
1668       g.setColor(Color.red);
1669       g.drawString(
1670               MessageManager.getString("label.jalview_cannot_open_file"),
1671               5, 15);
1672       g.drawString("\"" + file + "\"", 5, 30);
1673     }
1674     else if (embedded)
1675     {
1676       g.setColor(Color.black);
1677       g.setFont(new Font("Arial", Font.BOLD, 24));
1678       g.drawString(MessageManager.getString("label.jalview_applet"), 50,
1679               getSize().height / 2 - 30);
1680       g.drawString(MessageManager.getString("label.loading_data") + "...",
1681               50, getSize().height / 2);
1682     }
1683   }
1684
1685   /**
1686    * get all components associated with the applet of the given type
1687    * 
1688    * @param class1
1689    * @return
1690    */
1691   public Vector getAppletWindow(Class class1)
1692   {
1693     Vector wnds = new Vector();
1694     Component[] cmp = getComponents();
1695     if (cmp != null)
1696     {
1697       for (int i = 0; i < cmp.length; i++)
1698       {
1699         if (class1.isAssignableFrom(cmp[i].getClass()))
1700         {
1701           wnds.addElement(cmp);
1702         }
1703       }
1704     }
1705     return wnds;
1706   }
1707
1708   class LoadJmolThread extends Thread
1709   {
1710     private boolean running = false;
1711
1712     public void run()
1713     {
1714       if (running || checkedForJmol)
1715       {
1716         return;
1717       }
1718       running = true;
1719       if (checkForJmol)
1720       {
1721         try
1722         {
1723           if (!System.getProperty("java.version").startsWith("1.1"))
1724           {
1725             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
1726             jmolAvailable = true;
1727           }
1728           if (!jmolAvailable)
1729           {
1730             System.out
1731                     .println("Jmol not available - Using MCview for structures");
1732           }
1733         } catch (java.lang.ClassNotFoundException ex)
1734         {
1735         }
1736       }
1737       else
1738       {
1739         jmolAvailable = false;
1740         if (debug)
1741         {
1742           System.err
1743                   .println("Skipping Jmol check. Will use MCView (probably)");
1744         }
1745       }
1746       checkedForJmol = true;
1747       running = false;
1748     }
1749
1750     public boolean notFinished()
1751     {
1752       return running || !checkedForJmol;
1753     }
1754   }
1755
1756   class LoadingThread extends Thread
1757   {
1758     /**
1759      * State variable: File source
1760      */
1761     String file;
1762
1763     /**
1764      * State variable: protocol for access to file source
1765      */
1766     String protocol;
1767
1768     /**
1769      * State variable: format of file source
1770      */
1771     String format;
1772
1773     String _file;
1774
1775     JalviewLite applet;
1776
1777     private void dbgMsg(String msg)
1778     {
1779       if (applet.debug)
1780       {
1781         System.err.println(msg);
1782       }
1783     }
1784
1785     /**
1786      * update the protocol state variable for accessing the datasource located
1787      * by file.
1788      * 
1789      * @param file
1790      * @return possibly updated datasource string
1791      */
1792     public String setProtocolState(String file)
1793     {
1794       if (file.startsWith("PASTE"))
1795       {
1796         file = file.substring(5);
1797         protocol = AppletFormatAdapter.PASTE;
1798       }
1799       else if (inArchive(file))
1800       {
1801         protocol = AppletFormatAdapter.CLASSLOADER;
1802       }
1803       else
1804       {
1805         file = addProtocol(file);
1806         protocol = AppletFormatAdapter.URL;
1807       }
1808       dbgMsg("Protocol identified as '" + protocol + "'");
1809       return file;
1810     }
1811
1812     public LoadingThread(String _file, JalviewLite _applet)
1813     {
1814       this._file = _file;
1815       applet = _applet;
1816     }
1817
1818     public void run()
1819     {
1820       LoadJmolThread jmolchecker = new LoadJmolThread();
1821       jmolchecker.start();
1822       while (jmolchecker.notFinished())
1823       {
1824         // wait around until the Jmol check is complete.
1825         try
1826         {
1827           Thread.sleep(2);
1828         } catch (Exception e)
1829         {
1830         }
1831         ;
1832       }
1833       startLoading();
1834       // applet.callInitCallback();
1835     }
1836
1837     private void startLoading()
1838     {
1839       AlignFrame newAlignFrame;
1840       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
1841       file = setProtocolState(_file);
1842
1843       format = new jalview.io.IdentifyFile().Identify(file, protocol);
1844       dbgMsg("File identified as '" + format + "'");
1845       dbgMsg("Loading started.");
1846       Alignment al = null;
1847       try
1848       {
1849         al = new AppletFormatAdapter().readFile(file, protocol, format);
1850       } catch (java.io.IOException ex)
1851       {
1852         dbgMsg("File load exception.");
1853         ex.printStackTrace();
1854         if (debug)
1855         {
1856           try
1857           {
1858             FileParse fp = new FileParse(file, protocol);
1859             String ln = null;
1860             dbgMsg(">>>Dumping contents of '" + file + "' " + "("
1861                     + protocol + ")");
1862             while ((ln = fp.nextLine()) != null)
1863             {
1864               dbgMsg(ln);
1865             }
1866             dbgMsg(">>>Dump finished.");
1867           } catch (Exception e)
1868           {
1869             System.err
1870                     .println("Exception when trying to dump the content of the file parameter.");
1871             e.printStackTrace();
1872           }
1873         }
1874       }
1875       if ((al != null) && (al.getHeight() > 0))
1876       {
1877         dbgMsg("Successfully loaded file.");
1878         newAlignFrame = new AlignFrame(al, applet, file, embedded);
1879         if (initialAlignFrame == null)
1880         {
1881           initialAlignFrame = newAlignFrame;
1882         }
1883         // update the focus.
1884         currentAlignFrame = newAlignFrame;
1885
1886         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
1887         {
1888           newAlignFrame.setTitle(MessageManager.formatMessage(
1889                   "label.sequences_from", new String[]
1890                   { applet.getDocumentBase().toString() }));
1891         }
1892
1893         newAlignFrame.statusBar.setText(MessageManager.formatMessage(
1894                 "label.successfully_loaded_file", new String[]
1895                 { file }));
1896
1897         String treeFile = applet.getParameter("tree");
1898         if (treeFile == null)
1899         {
1900           treeFile = applet.getParameter("treeFile");
1901         }
1902
1903         if (treeFile != null)
1904         {
1905           try
1906           {
1907             treeFile = setProtocolState(treeFile);
1908             /*
1909              * if (inArchive(treeFile)) { protocol =
1910              * AppletFormatAdapter.CLASSLOADER; } else { protocol =
1911              * AppletFormatAdapter.URL; treeFile = addProtocol(treeFile); }
1912              */
1913             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
1914                     protocol);
1915
1916             fin.parse();
1917
1918             if (fin.getTree() != null)
1919             {
1920               newAlignFrame.loadTree(fin, treeFile);
1921               dbgMsg("Successfuly imported tree.");
1922             }
1923             else
1924             {
1925               dbgMsg("Tree parameter did not resolve to a valid tree.");
1926             }
1927           } catch (Exception ex)
1928           {
1929             ex.printStackTrace();
1930           }
1931         }
1932
1933         /*
1934          * Try to load T-Coffee score file
1935          */
1936         String sScoreFile = applet.getParameter("scoreFile");
1937         if (sScoreFile != null && !"".equals(sScoreFile))
1938         {
1939           try
1940           {
1941             if (debug)
1942             {
1943               System.err
1944                       .println("Attempting to load T-COFFEE score file from the scoreFile parameter");
1945             }
1946             if (!newAlignFrame.loadScoreFile(sScoreFile))
1947             {
1948               System.err
1949                       .println("Failed to parse T-COFFEE parameter as a valid score file ('"
1950                               + sScoreFile + "')");
1951             }
1952           } catch (Exception e)
1953           {
1954             System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
1955                     sScoreFile, e.getMessage());
1956           }
1957         }
1958
1959         // ///////////////////////////
1960         // modify display of features
1961         // we do this before any features have been loaded, ensuring any hidden
1962         // groups are hidden when features first displayed
1963         //
1964         // hide specific groups
1965         //
1966         String param = applet.getParameter("hidefeaturegroups");
1967         if (param != null)
1968         {
1969           newAlignFrame.setFeatureGroupState(separatorListToArray(param),
1970                   false);
1971           // applet.setFeatureGroupStateOn(newAlignFrame, param, false);
1972         }
1973         // show specific groups
1974         param = applet.getParameter("showfeaturegroups");
1975         if (param != null)
1976         {
1977           newAlignFrame.setFeatureGroupState(separatorListToArray(param),
1978                   true);
1979           // applet.setFeatureGroupStateOn(newAlignFrame, param, true);
1980         }
1981         // and now load features
1982         param = applet.getParameter("features");
1983         if (param != null)
1984         {
1985           param = setProtocolState(param);
1986
1987           newAlignFrame.parseFeaturesFile(param, protocol);
1988         }
1989
1990         param = applet.getParameter("showFeatureSettings");
1991         if (param != null && param.equalsIgnoreCase("true"))
1992         {
1993           newAlignFrame.viewport.showSequenceFeatures(true);
1994           new FeatureSettings(newAlignFrame.alignPanel);
1995         }
1996
1997         param = applet.getParameter("annotations");
1998         if (param != null)
1999         {
2000           param = setProtocolState(param);
2001
2002           if (new AnnotationFile().readAnnotationFile(
2003                   newAlignFrame.viewport.getAlignment(), param, protocol))
2004           {
2005             newAlignFrame.alignPanel.fontChanged();
2006             newAlignFrame.alignPanel.setScrollValues(0, 0);
2007           }
2008           else
2009           {
2010             System.err
2011                     .println("Annotations were not added from annotation file '"
2012                             + param + "'");
2013           }
2014
2015         }
2016
2017         param = applet.getParameter("jnetfile");
2018         if (param != null)
2019         {
2020           try
2021           {
2022             param = setProtocolState(param);
2023             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
2024                     param, protocol);
2025             JnetAnnotationMaker.add_annotation(predictions,
2026                     newAlignFrame.viewport.getAlignment(), 0, false); // false==do
2027             // not
2028             // add
2029             // sequence
2030             // profile
2031             // from
2032             // concise
2033             // output
2034             newAlignFrame.alignPanel.fontChanged();
2035             newAlignFrame.alignPanel.setScrollValues(0, 0);
2036           } catch (Exception ex)
2037           {
2038             ex.printStackTrace();
2039           }
2040         }
2041         /*
2042          * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6
2043          * - related to JAL-434
2044          */
2045         applet.setAlignPdbStructures(getDefaultParameter("alignpdbfiles",
2046                 false));
2047         /*
2048          * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
2049          * PDB|1GAQ|1GAQ|C">
2050          * 
2051          * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
2052          * 
2053          * <param name="PDBfile3" value="1q0o Q45135_9MICO">
2054          */
2055
2056         int pdbFileCount = 0;
2057         // Accumulate pdbs here if they are heading for the same view (if
2058         // alignPdbStructures is true)
2059         Vector pdbs = new Vector();
2060         // create a lazy matcher if we're asked to
2061         jalview.analysis.SequenceIdMatcher matcher = (applet
2062                 .getDefaultParameter("relaxedidmatch", false)) ? new jalview.analysis.SequenceIdMatcher(
2063                 newAlignFrame.getAlignViewport().getAlignment()
2064                         .getSequencesArray()) : null;
2065
2066         do
2067         {
2068           if (pdbFileCount > 0)
2069           {
2070             param = applet.getParameter("PDBFILE" + pdbFileCount);
2071           }
2072           else
2073           {
2074             param = applet.getParameter("PDBFILE");
2075           }
2076
2077           if (param != null)
2078           {
2079             PDBEntry pdb = new PDBEntry();
2080
2081             String seqstring;
2082             SequenceI[] seqs = null;
2083             String[] chains = null;
2084
2085             StringTokenizer st = new StringTokenizer(param, " ");
2086
2087             if (st.countTokens() < 2)
2088             {
2089               String sequence = applet.getParameter("PDBSEQ");
2090               if (sequence != null)
2091               {
2092                 seqs = new SequenceI[]
2093                 { matcher == null ? (Sequence) newAlignFrame
2094                         .getAlignViewport().getAlignment()
2095                         .findName(sequence) : matcher.findIdMatch(sequence) };
2096               }
2097
2098             }
2099             else
2100             {
2101               param = st.nextToken();
2102               Vector tmp = new Vector();
2103               Vector tmp2 = new Vector();
2104
2105               while (st.hasMoreTokens())
2106               {
2107                 seqstring = st.nextToken();
2108                 StringTokenizer st2 = new StringTokenizer(seqstring, "=");
2109                 if (st2.countTokens() > 1)
2110                 {
2111                   // This is the chain
2112                   tmp2.addElement(st2.nextToken());
2113                   seqstring = st2.nextToken();
2114                 }
2115                 tmp.addElement(matcher == null ? (Sequence) newAlignFrame
2116                         .getAlignViewport().getAlignment()
2117                         .findName(seqstring) : matcher
2118                         .findIdMatch(seqstring));
2119               }
2120
2121               seqs = new SequenceI[tmp.size()];
2122               tmp.copyInto(seqs);
2123               if (tmp2.size() == tmp.size())
2124               {
2125                 chains = new String[tmp2.size()];
2126                 tmp2.copyInto(chains);
2127               }
2128             }
2129             param = setProtocolState(param);
2130
2131             if (// !jmolAvailable
2132             // &&
2133             protocol == AppletFormatAdapter.CLASSLOADER
2134                     && !useXtrnalSviewer)
2135             {
2136               // Re: JAL-357 : the bug isn't a problem if we are using an
2137               // external viewer!
2138               // TODO: verify this Re:
2139               // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
2140               // This exception preserves the current behaviour where, even if
2141               // the local pdb file was identified in the class loader
2142               protocol = AppletFormatAdapter.URL; // this is probably NOT
2143               // CORRECT!
2144               param = addProtocol(param); //
2145             }
2146
2147             pdb.setFile(param);
2148
2149             if (seqs != null)
2150             {
2151               for (int i = 0; i < seqs.length; i++)
2152               {
2153                 if (seqs[i] != null)
2154                 {
2155                   ((Sequence) seqs[i]).addPDBId(pdb);
2156                   StructureSelectionManager.getStructureSelectionManager(
2157                           applet).registerPDBEntry(pdb);
2158                 }
2159                 else
2160                 {
2161                   if (JalviewLite.debug)
2162                   {
2163                     // this may not really be a problem but we give a warning
2164                     // anyway
2165                     System.err
2166                             .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
2167                                     + i + ")");
2168                   }
2169                 }
2170               }
2171
2172               if (!alignPdbStructures)
2173               {
2174                 newAlignFrame.newStructureView(applet, pdb, seqs, chains,
2175                         protocol);
2176               }
2177               else
2178               {
2179                 pdbs.addElement(new Object[]
2180                 { pdb, seqs, chains, new String(protocol) });
2181               }
2182             }
2183           }
2184
2185           pdbFileCount++;
2186         } while (param != null || pdbFileCount < 10);
2187         if (pdbs.size() > 0)
2188         {
2189           SequenceI[][] seqs = new SequenceI[pdbs.size()][];
2190           PDBEntry[] pdb = new PDBEntry[pdbs.size()];
2191           String[][] chains = new String[pdbs.size()][];
2192           String[] protocols = new String[pdbs.size()];
2193           for (int pdbsi = 0, pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
2194           {
2195             Object[] o = (Object[]) pdbs.elementAt(pdbsi);
2196             pdb[pdbsi] = (PDBEntry) o[0];
2197             seqs[pdbsi] = (SequenceI[]) o[1];
2198             chains[pdbsi] = (String[]) o[2];
2199             protocols[pdbsi] = (String) o[3];
2200           }
2201           newAlignFrame.alignedStructureView(applet, pdb, seqs, chains,
2202                   protocols);
2203
2204         }
2205       }
2206       else
2207       {
2208         fileFound = false;
2209         applet.remove(launcher);
2210         applet.repaint();
2211       }
2212       callInitCallback();
2213     }
2214
2215     /**
2216      * Discovers whether the given file is in the Applet Archive
2217      * 
2218      * @param file
2219      *          String
2220      * @return boolean
2221      */
2222     boolean inArchive(String file)
2223     {
2224       // This might throw a security exception in certain browsers
2225       // Netscape Communicator for instance.
2226       try
2227       {
2228         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
2229         if (debug)
2230         {
2231           System.err.println("Resource '" + file + "' was "
2232                   + (rtn ? "" : "not") + " located by classloader.");
2233         }
2234         return rtn;
2235       } catch (Exception ex)
2236       {
2237         System.out.println("Exception checking resources: " + file + " "
2238                 + ex);
2239         return false;
2240       }
2241     }
2242
2243     String addProtocol(String file)
2244     {
2245       if (file.indexOf("://") == -1)
2246       {
2247         String fl = applet.resolveUrlForLocalOrAbsolute(file,
2248                 getDocumentBase());
2249         try
2250         {
2251           if (new java.net.URL(fl).openStream() != null)
2252           {
2253             if (debug)
2254             {
2255               System.err.println("Prepended document base for resource: '"
2256                       + file + "'");
2257             }
2258             return fl;
2259           }
2260         } catch (Exception x)
2261         {
2262         }
2263         ;
2264         fl = applet.resolveUrlForLocalOrAbsolute(file, getCodeBase());
2265         try
2266         {
2267           if (new java.net.URL(fl).openStream() != null)
2268           {
2269             if (debug)
2270             {
2271               System.err.println("Prepended codebase for resource: '"
2272                       + file + "'");
2273             }
2274             return fl;
2275           }
2276         } catch (Exception x)
2277         {
2278         }
2279         ;
2280
2281       }
2282
2283       return file;
2284     }
2285   }
2286
2287   /**
2288    * @return the default alignFrame acted on by the public applet methods. May
2289    *         return null with an error message on System.err indicating the
2290    *         fact.
2291    */
2292   public AlignFrame getDefaultTargetFrame()
2293   {
2294     if (currentAlignFrame != null)
2295     {
2296       return currentAlignFrame;
2297     }
2298     if (initialAlignFrame != null)
2299     {
2300       return initialAlignFrame;
2301     }
2302     System.err
2303             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
2304     return null;
2305   }
2306
2307   /**
2308    * separator used for separatorList
2309    */
2310   protected String separator = "" + ((char) 0x00AC); // the default used to be
2311                                                      // '|' but many sequence
2312                                                      // IDS include pipes.
2313
2314   /**
2315    * set to enable the URL based javascript execution mechanism
2316    */
2317   public boolean jsfallbackEnabled = false;
2318
2319   /**
2320    * parse the string into a list
2321    * 
2322    * @param list
2323    * @return elements separated by separator
2324    */
2325   public String[] separatorListToArray(String list)
2326   {
2327     return separatorListToArray(list, separator);
2328   }
2329
2330   /**
2331    * parse the string into a list
2332    * 
2333    * @param list
2334    * @param separator
2335    * @return elements separated by separator
2336    */
2337   public String[] separatorListToArray(String list, String separator)
2338   {
2339     // note separator local variable intentionally masks object field
2340     int seplen = separator.length();
2341     if (list == null || list.equals("") || list.equals(separator))
2342     {
2343       return null;
2344     }
2345     java.util.Vector jv = new Vector();
2346     int cp = 0, pos;
2347     while ((pos = list.indexOf(separator, cp)) > cp)
2348     {
2349       jv.addElement(list.substring(cp, pos));
2350       cp = pos + seplen;
2351     }
2352     if (cp < list.length())
2353     {
2354       String c = list.substring(cp);
2355       if (!c.equals(separator))
2356       {
2357         jv.addElement(c);
2358       }
2359     }
2360     if (jv.size() > 0)
2361     {
2362       String[] v = new String[jv.size()];
2363       for (int i = 0; i < v.length; i++)
2364       {
2365         v[i] = (String) jv.elementAt(i);
2366       }
2367       jv.removeAllElements();
2368       if (debug)
2369       {
2370         System.err.println("Array from '" + separator
2371                 + "' separated List:\n" + v.length);
2372         for (int i = 0; i < v.length; i++)
2373         {
2374           System.err.println("item " + i + " '" + v[i] + "'");
2375         }
2376       }
2377       return v;
2378     }
2379     if (debug)
2380     {
2381       System.err.println("Empty Array from '" + separator
2382               + "' separated List");
2383     }
2384     return null;
2385   }
2386
2387   /**
2388    * concatenate the list with separator
2389    * 
2390    * @param list
2391    * @return concatenated string
2392    */
2393   public String arrayToSeparatorList(String[] list)
2394   {
2395     return arrayToSeparatorList(list, separator);
2396   }
2397
2398   /**
2399    * concatenate the list with separator
2400    * 
2401    * @param list
2402    * @param separator
2403    * @return concatenated string
2404    */
2405   public String arrayToSeparatorList(String[] list, String separator)
2406   {
2407     StringBuffer v = new StringBuffer();
2408     if (list != null && list.length > 0)
2409     {
2410       for (int i = 0, iSize = list.length; i < iSize; i++)
2411       {
2412         if (list[i] != null)
2413         {
2414           if (i > 0)
2415           {
2416             v.append(separator);
2417           }
2418           v.append(list[i]);
2419         }
2420       }
2421       if (debug)
2422       {
2423         System.err.println("Returning '" + separator
2424                 + "' separated List:\n");
2425         System.err.println(v);
2426       }
2427       return v.toString();
2428     }
2429     if (debug)
2430     {
2431       System.err.println("Returning empty '" + separator
2432               + "' separated List\n");
2433     }
2434     return "" + separator;
2435   }
2436
2437   /*
2438    * (non-Javadoc)
2439    * 
2440    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroups()
2441    */
2442   public String getFeatureGroups()
2443   {
2444     String lst = arrayToSeparatorList(getDefaultTargetFrame()
2445             .getFeatureGroups());
2446     return lst;
2447   }
2448
2449   /*
2450    * (non-Javadoc)
2451    * 
2452    * @see
2453    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOn(jalview.appletgui.AlignFrame
2454    * )
2455    */
2456   public String getFeatureGroupsOn(AlignFrame alf)
2457   {
2458     String lst = arrayToSeparatorList(alf.getFeatureGroups());
2459     return lst;
2460   }
2461
2462   /*
2463    * (non-Javadoc)
2464    * 
2465    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfState(boolean)
2466    */
2467   public String getFeatureGroupsOfState(boolean visible)
2468   {
2469     return arrayToSeparatorList(getDefaultTargetFrame()
2470             .getFeatureGroupsOfState(visible));
2471   }
2472
2473   /*
2474    * (non-Javadoc)
2475    * 
2476    * @see
2477    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfStateOn(jalview.appletgui
2478    * .AlignFrame, boolean)
2479    */
2480   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
2481   {
2482     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
2483   }
2484
2485   /*
2486    * (non-Javadoc)
2487    * 
2488    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupStateOn(jalview.appletgui.
2489    * AlignFrame, java.lang.String, boolean)
2490    */
2491   public void setFeatureGroupStateOn(final AlignFrame alf,
2492           final String groups, boolean state)
2493   {
2494     final boolean st = state;// !(state==null || state.equals("") ||
2495     // state.toLowerCase().equals("false"));
2496     java.awt.EventQueue.invokeLater(new Runnable()
2497     {
2498       @Override
2499       public void run()
2500       {
2501         alf.setFeatureGroupState(separatorListToArray(groups), st);
2502       }
2503     });
2504   }
2505
2506   /*
2507    * (non-Javadoc)
2508    * 
2509    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupState(java.lang.String,
2510    * boolean)
2511    */
2512   public void setFeatureGroupState(String groups, boolean state)
2513   {
2514     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
2515   }
2516
2517   /*
2518    * (non-Javadoc)
2519    * 
2520    * @see jalview.bin.JalviewLiteJsApi#getSeparator()
2521    */
2522   public String getSeparator()
2523   {
2524     return separator;
2525   }
2526
2527   /*
2528    * (non-Javadoc)
2529    * 
2530    * @see jalview.bin.JalviewLiteJsApi#setSeparator(java.lang.String)
2531    */
2532   public void setSeparator(String separator)
2533   {
2534     if (separator == null || separator.length() < 1)
2535     {
2536       // reset to default
2537       separator = "" + ((char) 0x00AC);
2538     }
2539     this.separator = separator;
2540     if (debug)
2541     {
2542       System.err.println("Default Separator now: '" + separator + "'");
2543     }
2544   }
2545
2546   /**
2547    * get boolean value of applet parameter 'name' and return default if
2548    * parameter is not set
2549    * 
2550    * @param name
2551    *          name of paremeter
2552    * @param def
2553    *          the value to return otherwise
2554    * @return true or false
2555    */
2556   public boolean getDefaultParameter(String name, boolean def)
2557   {
2558     String stn;
2559     if ((stn = getParameter(name)) == null)
2560     {
2561       return def;
2562     }
2563     if (stn.toLowerCase().equals("true"))
2564     {
2565       return true;
2566     }
2567     return false;
2568   }
2569
2570   /*
2571    * (non-Javadoc)
2572    * 
2573    * @see jalview.bin.JalviewLiteJsApi#addPdbFile(jalview.appletgui.AlignFrame,
2574    * java.lang.String, java.lang.String, java.lang.String)
2575    */
2576   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
2577           String pdbEntryString, String pdbFile)
2578   {
2579     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
2580   }
2581
2582   protected void setAlignPdbStructures(boolean alignPdbStructures)
2583   {
2584     this.alignPdbStructures = alignPdbStructures;
2585   }
2586
2587   public boolean isAlignPdbStructures()
2588   {
2589     return alignPdbStructures;
2590   }
2591
2592   public void start()
2593   {
2594     // callInitCallback();
2595   }
2596
2597   private Hashtable<String, long[]> jshashes = new Hashtable<String, long[]>();
2598
2599   private Hashtable<String, Hashtable<String, String[]>> jsmessages = new Hashtable<String, Hashtable<String, String[]>>();
2600
2601   public void setJsMessageSet(String messageclass, String viewId,
2602           String[] colcommands)
2603   {
2604     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2605     if (msgset == null)
2606     {
2607       msgset = new Hashtable<String, String[]>();
2608       jsmessages.put(messageclass, msgset);
2609     }
2610     msgset.put(viewId, colcommands);
2611     long[] l = new long[colcommands.length];
2612     for (int i = 0; i < colcommands.length; i++)
2613     {
2614       l[i] = colcommands[i].hashCode();
2615     }
2616     jshashes.put(messageclass + "|" + viewId, l);
2617   }
2618
2619   /*
2620    * (non-Javadoc)
2621    * 
2622    * @see jalview.bin.JalviewLiteJsApi#getJsMessage(java.lang.String,
2623    * java.lang.String)
2624    */
2625   public String getJsMessage(String messageclass, String viewId)
2626   {
2627     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2628     if (msgset != null)
2629     {
2630       String[] msgs = msgset.get(viewId);
2631       if (msgs != null)
2632       {
2633         for (int i = 0; i < msgs.length; i++)
2634         {
2635           if (msgs[i] != null)
2636           {
2637             String m = msgs[i];
2638             msgs[i] = null;
2639             return m;
2640           }
2641         }
2642       }
2643     }
2644     return "";
2645   }
2646
2647   public boolean isJsMessageSetChanged(String string, String string2,
2648           String[] colcommands)
2649   {
2650     long[] l = jshashes.get(string + "|" + string2);
2651     if (l == null && colcommands != null)
2652     {
2653       return true;
2654     }
2655     for (int i = 0; i < colcommands.length; i++)
2656     {
2657       if (l[i] != colcommands[i].hashCode())
2658       {
2659         return true;
2660       }
2661     }
2662     return false;
2663   }
2664
2665   private Vector jsExecQueue = new Vector();
2666
2667   public Vector getJsExecQueue()
2668   {
2669     return jsExecQueue;
2670   }
2671
2672   public void setExecutor(JSFunctionExec jsFunctionExec2)
2673   {
2674     jsFunctionExec = jsFunctionExec2;
2675   }
2676
2677   /**
2678    * return the given colour value parameter or the given default if parameter
2679    * not given
2680    * 
2681    * @param colparam
2682    * @param defcolour
2683    * @return
2684    */
2685   public Color getDefaultColourParameter(String colparam, Color defcolour)
2686   {
2687     String colprop = getParameter(colparam);
2688     if (colprop == null || colprop.trim().length() == 0)
2689     {
2690       return defcolour;
2691     }
2692     Color col = jalview.schemes.ColourSchemeProperty
2693             .getAWTColorFromName(colprop);
2694     if (col == null)
2695     {
2696       try
2697       {
2698         col = new jalview.schemes.UserColourScheme(colprop).findColour('A');
2699       } catch (Exception ex)
2700       {
2701         System.err.println("Couldn't parse '" + colprop
2702                 + "' as a colour for " + colparam);
2703         col = null;
2704       }
2705     }
2706     return (col == null) ? defcolour : col;
2707
2708   }
2709
2710   public void openJalviewHelpUrl()
2711   {
2712     String helpUrl = getParameter("jalviewhelpurl");
2713     if (helpUrl == null || helpUrl.trim().length() < 5)
2714     {
2715       helpUrl = "http://www.jalview.org/help.html";
2716     }
2717     showURL(helpUrl, "HELP");
2718   }
2719
2720   /**
2721    * form a complete URL given a path to a resource and a reference location on
2722    * the same server
2723    * 
2724    * @param url
2725    *          - an absolute path on the same server as localref or a document
2726    *          located relative to localref
2727    * @param localref
2728    *          - a URL on the same server as url
2729    * @return a complete URL for the resource located by url
2730    */
2731   private String resolveUrlForLocalOrAbsolute(String url, URL localref)
2732   {
2733     String codebase = localref.toString();
2734     if (url.indexOf("/") == 0)
2735     {
2736       url = codebase.substring(0, codebase.length()
2737               - localref.getFile().length())
2738               + url;
2739     }
2740     else
2741     {
2742       url = localref + url;
2743     }
2744     return url;
2745   }
2746
2747   /**
2748    * open a URL in the browser - resolving it according to relative refs and
2749    * coping with javascript: protocol if necessary.
2750    * 
2751    * @param url
2752    * @param target
2753    */
2754   public void showURL(String url, String target)
2755   {
2756     try
2757     {
2758       if (url.indexOf(":") == -1)
2759       {
2760         // TODO: verify (Bas Vroling bug) prepend codebase or server URL to
2761         // form valid URL
2762         // Should really use docbase, not codebase.
2763         URL prepend;
2764         url = resolveUrlForLocalOrAbsolute(
2765                 url,
2766                 prepend = getDefaultParameter("resolvetocodebase", false) ? getDocumentBase()
2767                         : getCodeBase());
2768         if (debug)
2769         {
2770           System.err
2771                   .println("Show url (prepended "
2772                           + prepend
2773                           + " - toggle resolvetocodebase if code/docbase resolution is wrong): "
2774                           + url);
2775         }
2776       }
2777       else
2778       {
2779         if (debug)
2780         {
2781           System.err.println("Show url: " + url);
2782         }
2783       }
2784       if (url.indexOf("javascript:") == 0)
2785       {
2786         // no target for the javascript context
2787         getAppletContext().showDocument(new java.net.URL(url));
2788       }
2789       else
2790       {
2791         getAppletContext().showDocument(new java.net.URL(url), target);
2792       }
2793     } catch (Exception ex)
2794     {
2795       ex.printStackTrace();
2796     }
2797   }
2798
2799   /**
2800    * bind structures in a viewer to any matching sequences in an alignFrame (use
2801    * sequenceIds to limit scope of search to specific sequences)
2802    * 
2803    * @param alFrame
2804    * @param viewer
2805    * @param sequenceIds
2806    * @return TODO: consider making an exception structure for indicating when
2807    *         binding fails public SequenceStructureBinding
2808    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
2809    *         sequenceIds) {
2810    * 
2811    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
2812    *         alFrame.addStructureViewInstance(viewer,
2813    *         separatorListToArray(sequenceIds)); } else { return
2814    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
2815    */
2816 }