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