JAL-1645 Version-Rel Version 2.9 Year-Rel 2015 Licensing glob
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
3  * Copyright (C) 2015 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 maybe 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    * (non-Javadoc)
961    * 
962    * @see jalview.bin.JalviewLiteJsApi#setStructureListener(java.lang.String,
963    * java.lang.String)
964    */
965   public void setStructureListener(String listener, String modelSet)
966   {
967     if (listener != null)
968     {
969       listener = listener.trim();
970       if (listener.length() == 0)
971       {
972         System.err
973                 .println("jalview Javascript error: Ignoring empty function for selection listener.");
974         return;
975       }
976     }
977     jalview.javascript.MouseOverStructureListener mol = new jalview.javascript.MouseOverStructureListener(
978             this, listener, separatorListToArray(modelSet));
979     javascriptListeners.addElement(mol);
980     StructureSelectionManager.getStructureSelectionManager(this)
981             .addStructureViewerListener(mol);
982     if (debug)
983     {
984       System.err.println("Added a javascript structure viewer listener '"
985               + listener + "'");
986       System.err.println("There are now " + javascriptListeners.size()
987               + " listeners in total.");
988     }
989   }
990
991   /*
992    * (non-Javadoc)
993    * 
994    * @see
995    * jalview.bin.JalviewLiteJsApi#removeJavascriptListener(jalview.appletgui
996    * .AlignFrame, java.lang.String)
997    */
998   public void removeJavascriptListener(AlignFrame af, String listener)
999   {
1000     if (listener != null)
1001     {
1002       listener = listener.trim();
1003       if (listener.length() == 0)
1004       {
1005         listener = null;
1006       }
1007     }
1008     boolean rprt = false;
1009     for (int ms = 0, msSize = javascriptListeners.size(); ms < msSize;)
1010     {
1011       Object lstn = javascriptListeners.elementAt(ms);
1012       JsCallBack lstner = (JsCallBack) lstn;
1013       if ((af == null || lstner.getAlignFrame() == af)
1014               && (listener == null || lstner.getListenerFunction().equals(
1015                       listener)))
1016       {
1017         javascriptListeners.removeElement(lstner);
1018         msSize--;
1019         if (lstner instanceof SelectionListener)
1020         {
1021           StructureSelectionManager.getStructureSelectionManager(this)
1022                   .removeSelectionListener((SelectionListener) lstner);
1023         }
1024         else
1025         {
1026           StructureSelectionManager.getStructureSelectionManager(this)
1027                   .removeStructureViewerListener(lstner, null);
1028         }
1029         rprt = debug;
1030         if (debug)
1031         {
1032           System.err.println("Removed listener '" + listener + "'");
1033         }
1034       }
1035       else
1036       {
1037         ms++;
1038       }
1039     }
1040     if (rprt)
1041     {
1042       System.err.println("There are now " + javascriptListeners.size()
1043               + " listeners in total.");
1044     }
1045   }
1046
1047   public void stop()
1048   {
1049     System.err.println("Applet " + getName() + " stop().");
1050     tidyUp();
1051   }
1052
1053   public void destroy()
1054   {
1055     System.err.println("Applet " + getName() + " destroy().");
1056     tidyUp();
1057   }
1058
1059   private void tidyUp()
1060   {
1061     removeAll();
1062     if (currentAlignFrame != null && currentAlignFrame.viewport != null
1063             && currentAlignFrame.viewport.applet != null)
1064     {
1065       AlignViewport av = currentAlignFrame.viewport;
1066       currentAlignFrame.closeMenuItem_actionPerformed();
1067       av.applet = null;
1068       currentAlignFrame = null;
1069     }
1070     if (javascriptListeners != null)
1071     {
1072       while (javascriptListeners.size() > 0)
1073       {
1074         jalview.javascript.JSFunctionExec mol = javascriptListeners
1075                 .elementAt(0);
1076         javascriptListeners.removeElement(mol);
1077         if (mol instanceof SelectionListener)
1078         {
1079           StructureSelectionManager.getStructureSelectionManager(this)
1080                   .removeSelectionListener((SelectionListener) mol);
1081         }
1082         else
1083         {
1084           StructureSelectionManager.getStructureSelectionManager(this)
1085                   .removeStructureViewerListener(mol, null);
1086         }
1087         mol.jvlite = null;
1088       }
1089     }
1090     if (jsFunctionExec != null)
1091     {
1092       jsFunctionExec.stopQueue();
1093       jsFunctionExec.jvlite = null;
1094     }
1095     initialAlignFrame = null;
1096     jsFunctionExec = null;
1097     javascriptListeners = null;
1098     StructureSelectionManager.release(this);
1099   }
1100
1101   private jalview.javascript.JSFunctionExec jsFunctionExec;
1102
1103   /*
1104    * (non-Javadoc)
1105    * 
1106    * @see jalview.bin.JalviewLiteJsApi#mouseOverStructure(java.lang.String,
1107    * java.lang.String, java.lang.String)
1108    */
1109   public void mouseOverStructure(final String pdbResNum,
1110           final String chain, final String pdbfile)
1111   {
1112     final StructureSelectionManagerProvider me = this;
1113     java.awt.EventQueue.invokeLater(new Runnable()
1114     {
1115       @Override
1116       public void run()
1117       {
1118         try
1119         {
1120           StructureSelectionManager.getStructureSelectionManager(me)
1121                   .mouseOverStructure(new Integer(pdbResNum).intValue(),
1122                           chain, pdbfile);
1123           if (debug)
1124           {
1125             System.err.println("mouseOver for '" + pdbResNum
1126                     + "' in chain '" + chain + "' in structure '" + pdbfile
1127                     + "'");
1128           }
1129         } catch (NumberFormatException e)
1130         {
1131           System.err.println("Ignoring invalid residue number string '"
1132                   + pdbResNum + "'");
1133         }
1134
1135       }
1136     });
1137   }
1138
1139   /*
1140    * (non-Javadoc)
1141    * 
1142    * @see
1143    * jalview.bin.JalviewLiteJsApi#scrollViewToIn(jalview.appletgui.AlignFrame,
1144    * java.lang.String, java.lang.String)
1145    */
1146   public void scrollViewToIn(final AlignFrame alf, final String topRow,
1147           final String leftHandColumn)
1148   {
1149     java.awt.EventQueue.invokeLater(new Runnable()
1150     {
1151       @Override
1152       public void run()
1153       {
1154         try
1155         {
1156           alf.scrollTo(new Integer(topRow).intValue(), new Integer(
1157                   leftHandColumn).intValue());
1158
1159         } catch (Exception ex)
1160         {
1161           System.err.println("Couldn't parse integer arguments (topRow='"
1162                   + topRow + "' and leftHandColumn='" + leftHandColumn
1163                   + "')");
1164           ex.printStackTrace();
1165         }
1166       }
1167     });
1168   }
1169
1170   /*
1171    * (non-Javadoc)
1172    * 
1173    * @see
1174    * jalview.javascript.JalviewLiteJsApi#scrollViewToRowIn(jalview.appletgui
1175    * .AlignFrame, java.lang.String)
1176    */
1177   @Override
1178   public void scrollViewToRowIn(final AlignFrame alf, final String topRow)
1179   {
1180
1181     java.awt.EventQueue.invokeLater(new Runnable()
1182     {
1183       @Override
1184       public void run()
1185       {
1186         try
1187         {
1188           alf.scrollToRow(new Integer(topRow).intValue());
1189
1190         } catch (Exception ex)
1191         {
1192           System.err.println("Couldn't parse integer arguments (topRow='"
1193                   + topRow + "')");
1194           ex.printStackTrace();
1195         }
1196
1197       }
1198     });
1199   }
1200
1201   /*
1202    * (non-Javadoc)
1203    * 
1204    * @see
1205    * jalview.javascript.JalviewLiteJsApi#scrollViewToColumnIn(jalview.appletgui
1206    * .AlignFrame, java.lang.String)
1207    */
1208   @Override
1209   public void scrollViewToColumnIn(final AlignFrame alf,
1210           final String leftHandColumn)
1211   {
1212     java.awt.EventQueue.invokeLater(new Runnable()
1213     {
1214
1215       @Override
1216       public void run()
1217       {
1218         try
1219         {
1220           alf.scrollToColumn(new Integer(leftHandColumn).intValue());
1221
1222         } catch (Exception ex)
1223         {
1224           System.err
1225                   .println("Couldn't parse integer arguments (leftHandColumn='"
1226                           + leftHandColumn + "')");
1227           ex.printStackTrace();
1228         }
1229       }
1230     });
1231
1232   }
1233
1234   // //////////////////////////////////////////////
1235   // //////////////////////////////////////////////
1236
1237   public static int lastFrameX = 200;
1238
1239   public static int lastFrameY = 200;
1240
1241   boolean fileFound = true;
1242
1243   String file = "No file";
1244
1245   String file2 = null;
1246
1247   Button launcher = new Button(
1248           MessageManager.getString("label.start_jalview"));
1249
1250   /**
1251    * The currentAlignFrame is static, it will change if and when the user
1252    * selects a new window. Note that it will *never* point back to the embedded
1253    * AlignFrame if the applet is started as embedded on the page and then
1254    * afterwards a new view is created.
1255    */
1256   public AlignFrame currentAlignFrame = null;
1257
1258   /**
1259    * This is the first frame to be displayed, and does not change. API calls
1260    * will default to this instance if currentAlignFrame is null.
1261    */
1262   AlignFrame initialAlignFrame = null;
1263
1264   boolean embedded = false;
1265
1266   private boolean checkForJmol = true;
1267
1268   private boolean checkedForJmol = false; // ensure we don't check for jmol
1269
1270   // every time the app is re-inited
1271
1272   public boolean jmolAvailable = false;
1273
1274   private boolean alignPdbStructures = false;
1275
1276   /**
1277    * use an external structure viewer exclusively (no jmols or MCViews will be
1278    * opened by JalviewLite itself)
1279    */
1280   public boolean useXtrnalSviewer = false;
1281
1282   public static boolean debug = false;
1283
1284   static String builddate = null, version = null, installation = null;
1285
1286   private static void initBuildDetails()
1287   {
1288     if (builddate == null)
1289     {
1290       builddate = "unknown";
1291       version = "test";
1292       installation = "applet";
1293       java.net.URL url = JalviewLite.class
1294               .getResource("/.build_properties");
1295       if (url != null)
1296       {
1297         try
1298         {
1299           BufferedReader reader = new BufferedReader(new InputStreamReader(
1300                   url.openStream()));
1301           String line;
1302           while ((line = reader.readLine()) != null)
1303           {
1304             if (line.indexOf("VERSION") > -1)
1305             {
1306               version = line.substring(line.indexOf("=") + 1);
1307             }
1308             if (line.indexOf("BUILD_DATE") > -1)
1309             {
1310               builddate = line.substring(line.indexOf("=") + 1);
1311             }
1312             if (line.indexOf("INSTALLATION") > -1)
1313             {
1314               installation = line.substring(line.indexOf("=") + 1);
1315             }
1316           }
1317         } catch (Exception ex)
1318         {
1319           ex.printStackTrace();
1320         }
1321       }
1322     }
1323   }
1324
1325   public static String getBuildDate()
1326   {
1327     initBuildDetails();
1328     return builddate;
1329   }
1330
1331   public static String getInstallation()
1332   {
1333     initBuildDetails();
1334     return installation;
1335   }
1336
1337   public static String getVersion()
1338   {
1339     initBuildDetails();
1340     return version;
1341   }
1342
1343   // public JSObject scriptObject = null;
1344
1345   /**
1346    * init method for Jalview Applet
1347    */
1348   public void init()
1349   {
1350     // remove any handlers that might be hanging around from an earlier instance
1351     try
1352     {
1353       if (debug)
1354       {
1355         System.err.println("Applet context is '"
1356                 + getAppletContext().getClass().toString() + "'");
1357       }
1358       JSObject scriptObject = JSObject.getWindow(this);
1359       if (debug && scriptObject != null)
1360       {
1361         System.err.println("Applet has Javascript callback support.");
1362       }
1363
1364     } catch (Exception ex)
1365     {
1366       System.err
1367               .println("Warning: No JalviewLite javascript callbacks available.");
1368       if (debug)
1369       {
1370         ex.printStackTrace();
1371       }
1372     }
1373     /**
1374      * turn on extra applet debugging
1375      */
1376     debug = TRUE.equalsIgnoreCase(getParameter("debug"));
1377     if (debug)
1378     {
1379
1380       System.err.println("JalviewLite Version " + getVersion());
1381       System.err.println("Build Date : " + getBuildDate());
1382       System.err.println("Installation : " + getInstallation());
1383
1384     }
1385     String externalsviewer = getParameter("externalstructureviewer");
1386     if (externalsviewer != null)
1387     {
1388       useXtrnalSviewer = externalsviewer.trim().toLowerCase().equals(TRUE);
1389     }
1390     /**
1391      * if true disable the check for jmol
1392      */
1393     String chkforJmol = getParameter("nojmol");
1394     if (chkforJmol != null)
1395     {
1396       checkForJmol = !chkforJmol.equals(TRUE);
1397     }
1398     /**
1399      * get the separator parameter if present
1400      */
1401     String sep = getParameter("separator");
1402     if (sep != null)
1403     {
1404       if (sep.length() > 0)
1405       {
1406         separator = sep;
1407         if (debug)
1408         {
1409           System.err.println("Separator set to '" + separator + "'");
1410         }
1411       }
1412       else
1413       {
1414         throw new Error(
1415                 MessageManager
1416                         .getString("error.invalid_separator_parameter"));
1417       }
1418     }
1419     int r = 255;
1420     int g = 255;
1421     int b = 255;
1422     String param = getParameter("RGB");
1423
1424     if (param != null)
1425     {
1426       try
1427       {
1428         r = Integer.parseInt(param.substring(0, 2), 16);
1429         g = Integer.parseInt(param.substring(2, 4), 16);
1430         b = Integer.parseInt(param.substring(4, 6), 16);
1431       } catch (Exception ex)
1432       {
1433         r = 255;
1434         g = 255;
1435         b = 255;
1436       }
1437     }
1438     param = getParameter("label");
1439     if (param != null)
1440     {
1441       launcher.setLabel(param);
1442     }
1443
1444     setBackground(new Color(r, g, b));
1445
1446     file = getParameter("file");
1447
1448     if (file == null)
1449     {
1450       // Maybe the sequences are added as parameters
1451       StringBuffer data = new StringBuffer("PASTE");
1452       int i = 1;
1453       while ((file = getParameter("sequence" + i)) != null)
1454       {
1455         data.append(file.toString() + "\n");
1456         i++;
1457       }
1458       if (data.length() > 5)
1459       {
1460         file = data.toString();
1461       }
1462     }
1463     if (getDefaultParameter("enableSplitFrame", true))
1464     {
1465       file2 = getParameter("file2");
1466     }
1467
1468     embedded = TRUE.equalsIgnoreCase(getParameter("embedded"));
1469     if (embedded)
1470     {
1471       LoadingThread loader = new LoadingThread(file, file2, this);
1472       loader.start();
1473     }
1474     else if (file != null)
1475     {
1476       /*
1477        * Start the applet immediately or show a button to start it
1478        */
1479       if (FALSE.equalsIgnoreCase(getParameter("showbutton")))
1480       {
1481         LoadingThread loader = new LoadingThread(file, file2, this);
1482         loader.start();
1483       }
1484       else
1485       {
1486         add(launcher);
1487         launcher.addActionListener(new java.awt.event.ActionListener()
1488         {
1489           public void actionPerformed(ActionEvent e)
1490           {
1491             LoadingThread loader = new LoadingThread(file, file2,
1492                     JalviewLite.this);
1493             loader.start();
1494           }
1495         });
1496       }
1497     }
1498     else
1499     {
1500       // jalview initialisation with no alignment. loadAlignment() method can
1501       // still be called to open new alignments.
1502       file = "NO FILE";
1503       fileFound = false;
1504       callInitCallback();
1505     }
1506   }
1507
1508   private void initLiveConnect()
1509   {
1510     // try really hard to get the liveConnect thing working
1511     boolean notFailed = false;
1512     int tries = 0;
1513     while (!notFailed && tries < 10)
1514     {
1515       if (tries > 0)
1516       {
1517         System.err.println("LiveConnect request thread going to sleep.");
1518       }
1519       try
1520       {
1521         Thread.sleep(700 * (1 + tries));
1522       } catch (InterruptedException q)
1523       {
1524       }
1525       ;
1526       if (tries++ > 0)
1527       {
1528         System.err.println("LiveConnect request thread woken up.");
1529       }
1530       try
1531       {
1532         JSObject scriptObject = JSObject.getWindow(this);
1533         if (scriptObject.eval("navigator") != null)
1534         {
1535           notFailed = true;
1536         }
1537       } catch (Exception jsex)
1538       {
1539         System.err.println("Attempt " + tries
1540                 + " to access LiveConnect javascript failed.");
1541       }
1542     }
1543   }
1544
1545   private void callInitCallback()
1546   {
1547     String initjscallback = getParameter("oninit");
1548     if (initjscallback == null)
1549     {
1550       return;
1551     }
1552     initjscallback = initjscallback.trim();
1553     if (initjscallback.length() > 0)
1554     {
1555       JSObject scriptObject = null;
1556       try
1557       {
1558         scriptObject = JSObject.getWindow(this);
1559       } catch (Exception ex)
1560       {
1561       }
1562       ;
1563       // try really hard to let the browser plugin know we want liveconnect
1564       initLiveConnect();
1565
1566       if (scriptObject != null)
1567       {
1568         try
1569         {
1570           // do onInit with the JS executor thread
1571           new JSFunctionExec(this).executeJavascriptFunction(true,
1572                   initjscallback, null, "Calling oninit callback '"
1573                           + initjscallback + "'.");
1574         } catch (Exception e)
1575         {
1576           System.err.println("Exception when executing _oninit callback '"
1577                   + initjscallback + "'.");
1578           e.printStackTrace();
1579         }
1580       }
1581       else
1582       {
1583         System.err.println("Not executing _oninit callback '"
1584                 + initjscallback + "' - no scripting allowed.");
1585       }
1586     }
1587   }
1588
1589   /**
1590    * Initialises and displays a new java.awt.Frame
1591    * 
1592    * @param frame
1593    *          java.awt.Frame to be displayed
1594    * @param title
1595    *          title of new frame
1596    * @param width
1597    *          width if new frame
1598    * @param height
1599    *          height of new frame
1600    */
1601   public static void addFrame(final Frame frame, String title, int width,
1602           int height)
1603   {
1604     frame.setLocation(lastFrameX, lastFrameY);
1605     lastFrameX += 40;
1606     lastFrameY += 40;
1607     frame.setSize(width, height);
1608     frame.setTitle(title);
1609     frame.addWindowListener(new WindowAdapter()
1610     {
1611       public void windowClosing(WindowEvent e)
1612       {
1613         if (frame instanceof AlignFrame)
1614         {
1615           AlignViewport vp = ((AlignFrame) frame).viewport;
1616           ((AlignFrame) frame).closeMenuItem_actionPerformed();
1617           if (vp.applet.currentAlignFrame == frame)
1618           {
1619             vp.applet.currentAlignFrame = null;
1620           }
1621           vp.applet = null;
1622           vp = null;
1623
1624         }
1625         lastFrameX -= 40;
1626         lastFrameY -= 40;
1627         if (frame instanceof EmbmenuFrame)
1628         {
1629           ((EmbmenuFrame) frame).destroyMenus();
1630         }
1631         frame.setMenuBar(null);
1632         frame.dispose();
1633       }
1634
1635       public void windowActivated(WindowEvent e)
1636       {
1637         if (frame instanceof AlignFrame)
1638         {
1639           ((AlignFrame) frame).viewport.applet.currentAlignFrame = (AlignFrame) frame;
1640           if (debug)
1641           {
1642             System.err.println("Activated window " + frame);
1643           }
1644         }
1645         // be good.
1646         super.windowActivated(e);
1647       }
1648       /*
1649        * Probably not necessary to do this - see TODO above. (non-Javadoc)
1650        * 
1651        * @see
1652        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
1653        * )
1654        * 
1655        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
1656        * frame) { currentAlignFrame = null; if (debug) {
1657        * System.err.println("Deactivated window "+frame); } }
1658        * super.windowDeactivated(e); }
1659        */
1660     });
1661     frame.setVisible(true);
1662   }
1663
1664   /**
1665    * This paints the background surrounding the "Launch Jalview button" <br>
1666    * <br>
1667    * If file given in parameter not found, displays error message
1668    * 
1669    * @param g
1670    *          graphics context
1671    */
1672   public void paint(Graphics g)
1673   {
1674     if (!fileFound)
1675     {
1676       g.setColor(new Color(200, 200, 200));
1677       g.setColor(Color.cyan);
1678       g.fillRect(0, 0, getSize().width, getSize().height);
1679       g.setColor(Color.red);
1680       g.drawString(
1681               MessageManager.getString("label.jalview_cannot_open_file"),
1682               5, 15);
1683       g.drawString("\"" + file + "\"", 5, 30);
1684     }
1685     else if (embedded)
1686     {
1687       g.setColor(Color.black);
1688       g.setFont(new Font("Arial", Font.BOLD, 24));
1689       g.drawString(MessageManager.getString("label.jalview_applet"), 50,
1690               getSize().height / 2 - 30);
1691       g.drawString(MessageManager.getString("label.loading_data") + "...",
1692               50, getSize().height / 2);
1693     }
1694   }
1695
1696   /**
1697    * get all components associated with the applet of the given type
1698    * 
1699    * @param class1
1700    * @return
1701    */
1702   public Vector getAppletWindow(Class class1)
1703   {
1704     Vector wnds = new Vector();
1705     Component[] cmp = getComponents();
1706     if (cmp != null)
1707     {
1708       for (int i = 0; i < cmp.length; i++)
1709       {
1710         if (class1.isAssignableFrom(cmp[i].getClass()))
1711         {
1712           wnds.addElement(cmp);
1713         }
1714       }
1715     }
1716     return wnds;
1717   }
1718
1719   class LoadJmolThread extends Thread
1720   {
1721     private boolean running = false;
1722
1723     public void run()
1724     {
1725       if (running || checkedForJmol)
1726       {
1727         return;
1728       }
1729       running = true;
1730       if (checkForJmol)
1731       {
1732         try
1733         {
1734           if (!System.getProperty("java.version").startsWith("1.1"))
1735           {
1736             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
1737             jmolAvailable = true;
1738           }
1739           if (!jmolAvailable)
1740           {
1741             System.out
1742                     .println("Jmol not available - Using MCview for structures");
1743           }
1744         } catch (java.lang.ClassNotFoundException ex)
1745         {
1746         }
1747       }
1748       else
1749       {
1750         jmolAvailable = false;
1751         if (debug)
1752         {
1753           System.err
1754                   .println("Skipping Jmol check. Will use MCView (probably)");
1755         }
1756       }
1757       checkedForJmol = true;
1758       running = false;
1759     }
1760
1761     public boolean notFinished()
1762     {
1763       return running || !checkedForJmol;
1764     }
1765   }
1766
1767   class LoadingThread extends Thread
1768   {
1769     /**
1770      * State variable: protocol for access to file source
1771      */
1772     String protocol;
1773
1774     String _file; // alignment file or URL spec
1775
1776     String _file2; // second alignment file or URL spec
1777
1778     JalviewLite applet;
1779
1780     private void dbgMsg(String msg)
1781     {
1782       if (JalviewLite.debug)
1783       {
1784         System.err.println(msg);
1785       }
1786     }
1787
1788     /**
1789      * update the protocol state variable for accessing the datasource located
1790      * by file.
1791      * 
1792      * @param file
1793      * @return possibly updated datasource string
1794      */
1795     public String setProtocolState(String file)
1796     {
1797       if (file.startsWith("PASTE"))
1798       {
1799         file = file.substring(5);
1800         protocol = AppletFormatAdapter.PASTE;
1801       }
1802       else if (inArchive(file))
1803       {
1804         protocol = AppletFormatAdapter.CLASSLOADER;
1805       }
1806       else
1807       {
1808         file = addProtocol(file);
1809         protocol = AppletFormatAdapter.URL;
1810       }
1811       dbgMsg("Protocol identified as '" + protocol + "'");
1812       return file;
1813     }
1814
1815     public LoadingThread(String file, String file2, JalviewLite _applet)
1816     {
1817       this._file = file;
1818       this._file2 = file2;
1819       applet = _applet;
1820     }
1821
1822     public void run()
1823     {
1824       LoadJmolThread jmolchecker = new LoadJmolThread();
1825       jmolchecker.start();
1826       while (jmolchecker.notFinished())
1827       {
1828         // wait around until the Jmol check is complete.
1829         try
1830         {
1831           Thread.sleep(2);
1832         } catch (Exception e)
1833         {
1834         }
1835       }
1836       startLoading();
1837       // applet.callInitCallback();
1838     }
1839
1840     /**
1841      * Load the alignment and any related files as specified by applet
1842      * parameters
1843      */
1844     private void startLoading()
1845     {
1846       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
1847
1848       dbgMsg("Loading started.");
1849
1850       AlignFrame newAlignFrame = readAlignment(_file);
1851       AlignFrame newAlignFrame2 = readAlignment(_file2);
1852       if (newAlignFrame != null)
1853       {
1854         addToDisplay(newAlignFrame, newAlignFrame2);
1855         loadTree(newAlignFrame);
1856
1857         loadScoreFile(newAlignFrame);
1858
1859         loadFeatures(newAlignFrame);
1860
1861         loadAnnotations(newAlignFrame);
1862
1863         loadJnetFile(newAlignFrame);
1864
1865         loadPdbFiles(newAlignFrame);
1866       }
1867       else
1868       {
1869         fileFound = false;
1870         applet.remove(launcher);
1871         applet.repaint();
1872       }
1873       callInitCallback();
1874     }
1875
1876     /**
1877      * Add an AlignFrame to the display; or if two are provided, a SplitFrame.
1878      * 
1879      * @param af
1880      * @param af2
1881      */
1882     public void addToDisplay(AlignFrame af, AlignFrame af2)
1883     {
1884       if (af2 != null)
1885       {
1886         AlignmentI al1 = af.viewport.getAlignment();
1887         AlignmentI al2 = af2.viewport.getAlignment();
1888         if (AlignmentUtils.isMappable(al1, al2))
1889         {
1890           SplitFrame sf = new SplitFrame(af, af2);
1891           sf.addToDisplay(embedded, JalviewLite.this);
1892           return;
1893         }
1894         else
1895         {
1896           String msg = "Could not map any sequence in " + af2.getTitle()
1897                   + " as "
1898                   + (al1.isNucleotide() ? "protein product" : "cDNA")
1899                   + " for " + af.getTitle();
1900           System.err.println(msg);
1901         }
1902       }
1903
1904       af.addToDisplay(embedded);
1905     }
1906
1907     /**
1908      * Read the alignment file (from URL, text 'paste', or archive by
1909      * classloader).
1910      * 
1911      * @return
1912      */
1913     protected AlignFrame readAlignment(String fileParam)
1914     {
1915       if (fileParam == null)
1916       {
1917         return null;
1918       }
1919       String resolvedFile = setProtocolState(fileParam);
1920       String format = new IdentifyFile().Identify(resolvedFile, protocol);
1921       dbgMsg("File identified as '" + format + "'");
1922       AlignmentI al = null;
1923       try
1924       {
1925         al = new AppletFormatAdapter().readFile(resolvedFile, protocol,
1926                 format);
1927         if ((al != null) && (al.getHeight() > 0))
1928         {
1929           dbgMsg("Successfully loaded file.");
1930           al.setDataset(null);
1931           AlignFrame newAlignFrame = new AlignFrame(al, applet,
1932                   resolvedFile, embedded, false);
1933           newAlignFrame.setTitle(resolvedFile);
1934           if (initialAlignFrame == null)
1935           {
1936             initialAlignFrame = newAlignFrame;
1937           }
1938           // update the focus.
1939           currentAlignFrame = newAlignFrame;
1940
1941           if (protocol == AppletFormatAdapter.PASTE)
1942           {
1943             newAlignFrame.setTitle(MessageManager.formatMessage(
1944                     "label.sequences_from", new Object[] { applet
1945                             .getDocumentBase().toString() }));
1946           }
1947
1948           newAlignFrame.statusBar.setText(MessageManager.formatMessage(
1949                   "label.successfully_loaded_file",
1950                   new Object[] { resolvedFile }));
1951
1952           return newAlignFrame;
1953         }
1954       } catch (java.io.IOException ex)
1955       {
1956         dbgMsg("File load exception.");
1957         ex.printStackTrace();
1958         if (debug)
1959         {
1960           try
1961           {
1962             FileParse fp = new FileParse(resolvedFile, protocol);
1963             String ln = null;
1964             dbgMsg(">>>Dumping contents of '" + resolvedFile + "' " + "("
1965                     + protocol + ")");
1966             while ((ln = fp.nextLine()) != null)
1967             {
1968               dbgMsg(ln);
1969             }
1970             dbgMsg(">>>Dump finished.");
1971           } catch (Exception e)
1972           {
1973             System.err
1974                     .println("Exception when trying to dump the content of the file parameter.");
1975             e.printStackTrace();
1976           }
1977         }
1978       }
1979       return null;
1980     }
1981
1982     /**
1983      * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
1984      * else false.
1985      * 
1986      * @param alignFrame
1987      * @return
1988      */
1989     protected boolean loadPdbFiles(AlignFrame alignFrame)
1990     {
1991       boolean result = false;
1992       /*
1993        * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
1994        * related to JAL-434
1995        */
1996
1997       applet.setAlignPdbStructures(getDefaultParameter("alignpdbfiles",
1998               false));
1999       /*
2000        * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
2001        * PDB|1GAQ|1GAQ|C">
2002        * 
2003        * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
2004        * 
2005        * <param name="PDBfile3" value="1q0o Q45135_9MICO">
2006        */
2007
2008       int pdbFileCount = 0;
2009       // Accumulate pdbs here if they are heading for the same view (if
2010       // alignPdbStructures is true)
2011       Vector pdbs = new Vector();
2012       // create a lazy matcher if we're asked to
2013       jalview.analysis.SequenceIdMatcher matcher = (applet
2014               .getDefaultParameter("relaxedidmatch", false)) ? new jalview.analysis.SequenceIdMatcher(
2015               alignFrame.getAlignViewport().getAlignment()
2016                       .getSequencesArray()) : null;
2017
2018       String param;
2019       do
2020       {
2021         if (pdbFileCount > 0)
2022         {
2023           param = applet.getParameter("PDBFILE" + pdbFileCount);
2024         }
2025         else
2026         {
2027           param = applet.getParameter("PDBFILE");
2028         }
2029
2030         if (param != null)
2031         {
2032           PDBEntry pdb = new PDBEntry();
2033
2034           String seqstring;
2035           SequenceI[] seqs = null;
2036           String[] chains = null;
2037
2038           StringTokenizer st = new StringTokenizer(param, " ");
2039
2040           if (st.countTokens() < 2)
2041           {
2042             String sequence = applet.getParameter("PDBSEQ");
2043             if (sequence != null)
2044             {
2045               seqs = new SequenceI[] { matcher == null ? (Sequence) alignFrame
2046                       .getAlignViewport().getAlignment().findName(sequence)
2047                       : matcher.findIdMatch(sequence) };
2048             }
2049
2050           }
2051           else
2052           {
2053             param = st.nextToken();
2054             Vector tmp = new Vector();
2055             Vector tmp2 = new Vector();
2056
2057             while (st.hasMoreTokens())
2058             {
2059               seqstring = st.nextToken();
2060               StringTokenizer st2 = new StringTokenizer(seqstring, "=");
2061               if (st2.countTokens() > 1)
2062               {
2063                 // This is the chain
2064                 tmp2.addElement(st2.nextToken());
2065                 seqstring = st2.nextToken();
2066               }
2067               tmp.addElement(matcher == null ? (Sequence) alignFrame
2068                       .getAlignViewport().getAlignment()
2069                       .findName(seqstring) : matcher.findIdMatch(seqstring));
2070             }
2071
2072             seqs = new SequenceI[tmp.size()];
2073             tmp.copyInto(seqs);
2074             if (tmp2.size() == tmp.size())
2075             {
2076               chains = new String[tmp2.size()];
2077               tmp2.copyInto(chains);
2078             }
2079           }
2080           param = setProtocolState(param);
2081
2082           if (// !jmolAvailable
2083           // &&
2084           protocol == AppletFormatAdapter.CLASSLOADER && !useXtrnalSviewer)
2085           {
2086             // Re: JAL-357 : the bug isn't a problem if we are using an
2087             // external viewer!
2088             // TODO: verify this Re:
2089             // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
2090             // This exception preserves the current behaviour where, even if
2091             // the local pdb file was identified in the class loader
2092             protocol = AppletFormatAdapter.URL; // this is probably NOT
2093             // CORRECT!
2094             param = addProtocol(param); //
2095           }
2096
2097           pdb.setFile(param);
2098
2099           if (seqs != null)
2100           {
2101             for (int i = 0; i < seqs.length; i++)
2102             {
2103               if (seqs[i] != null)
2104               {
2105                 ((Sequence) seqs[i]).addPDBId(pdb);
2106                 StructureSelectionManager.getStructureSelectionManager(
2107                         applet).registerPDBEntry(pdb);
2108               }
2109               else
2110               {
2111                 if (JalviewLite.debug)
2112                 {
2113                   // this may not really be a problem but we give a warning
2114                   // anyway
2115                   System.err
2116                           .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
2117                                   + i + ")");
2118                 }
2119               }
2120             }
2121
2122             if (!alignPdbStructures)
2123             {
2124               alignFrame.newStructureView(applet, pdb, seqs, chains,
2125                       protocol);
2126             }
2127             else
2128             {
2129               pdbs.addElement(new Object[] { pdb, seqs, chains,
2130                   new String(protocol) });
2131             }
2132           }
2133         }
2134
2135         pdbFileCount++;
2136       } while (param != null || pdbFileCount < 10);
2137       if (pdbs.size() > 0)
2138       {
2139         SequenceI[][] seqs = new SequenceI[pdbs.size()][];
2140         PDBEntry[] pdb = new PDBEntry[pdbs.size()];
2141         String[][] chains = new String[pdbs.size()][];
2142         String[] protocols = new String[pdbs.size()];
2143         for (int pdbsi = 0, pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
2144         {
2145           Object[] o = (Object[]) pdbs.elementAt(pdbsi);
2146           pdb[pdbsi] = (PDBEntry) o[0];
2147           seqs[pdbsi] = (SequenceI[]) o[1];
2148           chains[pdbsi] = (String[]) o[2];
2149           protocols[pdbsi] = (String) o[3];
2150         }
2151         alignFrame.alignedStructureView(applet, pdb, seqs, chains,
2152                 protocols);
2153         result = true;
2154       }
2155       return result;
2156     }
2157
2158     /**
2159      * Load in a Jnetfile if specified by parameter. Returns true if loaded,
2160      * else false.
2161      * 
2162      * @param alignFrame
2163      * @return
2164      */
2165     protected boolean loadJnetFile(AlignFrame alignFrame)
2166     {
2167       boolean result = false;
2168       String param = applet.getParameter("jnetfile");
2169       if (param != null)
2170       {
2171         try
2172         {
2173           param = setProtocolState(param);
2174           JPredFile predictions = new JPredFile(param, protocol);
2175           JnetAnnotationMaker.add_annotation(predictions,
2176                   alignFrame.viewport.getAlignment(), 0, false);
2177           // false == do not add sequence profile from concise output
2178           SequenceI repseq = alignFrame.viewport.getAlignment()
2179                   .getSequenceAt(0);
2180           alignFrame.viewport.getAlignment().setSeqrep(repseq);
2181           ColumnSelection cs = new ColumnSelection();
2182           cs.hideInsertionsFor(repseq);
2183           alignFrame.viewport.setColumnSelection(cs);
2184           alignFrame.alignPanel.fontChanged();
2185           alignFrame.alignPanel.setScrollValues(0, 0);
2186           result = true;
2187         } catch (Exception ex)
2188         {
2189           ex.printStackTrace();
2190         }
2191       }
2192       return result;
2193     }
2194
2195     /**
2196      * Load annotations if specified by parameter. Returns true if loaded, else
2197      * false.
2198      * 
2199      * @param alignFrame
2200      * @return
2201      */
2202     protected boolean loadAnnotations(AlignFrame alignFrame)
2203     {
2204       boolean result = false;
2205       String param = applet.getParameter("annotations");
2206       if (param != null)
2207       {
2208         param = setProtocolState(param);
2209
2210         if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
2211                 param, protocol))
2212         {
2213           alignFrame.alignPanel.fontChanged();
2214           alignFrame.alignPanel.setScrollValues(0, 0);
2215           result = true;
2216         }
2217         else
2218         {
2219           System.err
2220                   .println("Annotations were not added from annotation file '"
2221                           + param + "'");
2222         }
2223       }
2224       return result;
2225     }
2226
2227     /**
2228      * Load features file and view settings as specified by parameters. Returns
2229      * true if features were loaded, else false.
2230      * 
2231      * @param alignFrame
2232      * @return
2233      */
2234     protected boolean loadFeatures(AlignFrame alignFrame)
2235     {
2236       boolean result = false;
2237       // ///////////////////////////
2238       // modify display of features
2239       // we do this before any features have been loaded, ensuring any hidden
2240       // groups are hidden when features first displayed
2241       //
2242       // hide specific groups
2243       //
2244       String param = applet.getParameter("hidefeaturegroups");
2245       if (param != null)
2246       {
2247         alignFrame.setFeatureGroupState(separatorListToArray(param), false);
2248         // applet.setFeatureGroupStateOn(newAlignFrame, param, false);
2249       }
2250       // show specific groups
2251       param = applet.getParameter("showfeaturegroups");
2252       if (param != null)
2253       {
2254         alignFrame.setFeatureGroupState(separatorListToArray(param), true);
2255         // applet.setFeatureGroupStateOn(newAlignFrame, param, true);
2256       }
2257       // and now load features
2258       param = applet.getParameter("features");
2259       if (param != null)
2260       {
2261         param = setProtocolState(param);
2262
2263         result = alignFrame.parseFeaturesFile(param, protocol);
2264       }
2265
2266       param = applet.getParameter("showFeatureSettings");
2267       if (param != null && param.equalsIgnoreCase(TRUE))
2268       {
2269         alignFrame.viewport.setShowSequenceFeatures(true);
2270         new FeatureSettings(alignFrame.alignPanel);
2271       }
2272       return result;
2273     }
2274
2275     /**
2276      * Load a score file if specified by parameter. Returns true if file was
2277      * loaded, else false.
2278      * 
2279      * @param alignFrame
2280      */
2281     protected boolean loadScoreFile(AlignFrame alignFrame)
2282     {
2283       boolean result = false;
2284       String sScoreFile = applet.getParameter("scoreFile");
2285       if (sScoreFile != null && !"".equals(sScoreFile))
2286       {
2287         try
2288         {
2289           if (debug)
2290           {
2291             System.err
2292                     .println("Attempting to load T-COFFEE score file from the scoreFile parameter");
2293           }
2294           result = alignFrame.loadScoreFile(sScoreFile);
2295           if (!result)
2296           {
2297             System.err
2298                     .println("Failed to parse T-COFFEE parameter as a valid score file ('"
2299                             + sScoreFile + "')");
2300           }
2301         } catch (Exception e)
2302         {
2303           System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
2304                   sScoreFile, e.getMessage());
2305         }
2306       }
2307       return result;
2308     }
2309
2310     /**
2311      * Load a tree for the alignment if specified by parameter. Returns true if
2312      * a tree was loaded, else false.
2313      * 
2314      * @param alignFrame
2315      * @return
2316      */
2317     protected boolean loadTree(AlignFrame alignFrame)
2318     {
2319       boolean result = false;
2320       String treeFile = applet.getParameter("tree");
2321       if (treeFile == null)
2322       {
2323         treeFile = applet.getParameter("treeFile");
2324       }
2325
2326       if (treeFile != null)
2327       {
2328         try
2329         {
2330           treeFile = setProtocolState(treeFile);
2331           NewickFile fin = new NewickFile(treeFile, protocol);
2332           fin.parse();
2333
2334           if (fin.getTree() != null)
2335           {
2336             alignFrame.loadTree(fin, treeFile);
2337             result = true;
2338             dbgMsg("Successfully imported tree.");
2339           }
2340           else
2341           {
2342             dbgMsg("Tree parameter did not resolve to a valid tree.");
2343           }
2344         } catch (Exception ex)
2345         {
2346           ex.printStackTrace();
2347         }
2348       }
2349       return result;
2350     }
2351
2352     /**
2353      * Discovers whether the given file is in the Applet Archive
2354      * 
2355      * @param file
2356      *          String
2357      * @return boolean
2358      */
2359     boolean inArchive(String file)
2360     {
2361       // This might throw a security exception in certain browsers
2362       // Netscape Communicator for instance.
2363       try
2364       {
2365         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
2366         if (debug)
2367         {
2368           System.err.println("Resource '" + file + "' was "
2369                   + (rtn ? "" : "not") + " located by classloader.");
2370         }
2371         return rtn;
2372       } catch (Exception ex)
2373       {
2374         System.out.println("Exception checking resources: " + file + " "
2375                 + ex);
2376         return false;
2377       }
2378     }
2379
2380     /**
2381      * If the file is not already in URL format, tries to locate it by resolving
2382      * as a URL.
2383      * 
2384      * @param f
2385      * @return
2386      */
2387     String addProtocol(final String f)
2388     {
2389       if (f.indexOf("://") != -1)
2390       {
2391         // already has URL format
2392         return f;
2393       }
2394
2395       /*
2396        * Try relative to document base
2397        */
2398       URL documentBase = getDocumentBase();
2399       String url = applet.resolveUrlForLocalOrAbsolute(f, documentBase);
2400       if (urlExists(url))
2401       {
2402         if (debug)
2403         {
2404           System.err.println("Prepended document base '" + documentBase
2405                   + "' to make: '" + url + "'");
2406         }
2407         return url;
2408       }
2409
2410       /*
2411        * Try relative to codebase
2412        */
2413       URL codeBase = getCodeBase();
2414       url = applet.resolveUrlForLocalOrAbsolute(f, codeBase);
2415       if (urlExists(url))
2416       {
2417         if (debug)
2418         {
2419           System.err.println("Prepended codebase '" + codeBase
2420                   + "' to make: '" + url + "'");
2421         }
2422         return url;
2423       }
2424
2425       return f;
2426     }
2427
2428     /**
2429      * Returns true if an input stream can be opened on the specified URL, else
2430      * false.
2431      * 
2432      * @param url
2433      * @return
2434      */
2435     private boolean urlExists(String url)
2436     {
2437       InputStream is = null;
2438       try
2439       {
2440         is = new URL(url).openStream();
2441         if (is != null)
2442         {
2443           return true;
2444         }
2445       } catch (Exception x)
2446       {
2447         // ignore
2448       } finally
2449       {
2450         if (is != null)
2451         {
2452           try
2453           {
2454             is.close();
2455           } catch (IOException e)
2456           {
2457             // ignore
2458           }
2459         }
2460       }
2461       return false;
2462     }
2463   }
2464
2465   /**
2466    * @return the default alignFrame acted on by the public applet methods. May
2467    *         return null with an error message on System.err indicating the
2468    *         fact.
2469    */
2470   public AlignFrame getDefaultTargetFrame()
2471   {
2472     if (currentAlignFrame != null)
2473     {
2474       return currentAlignFrame;
2475     }
2476     if (initialAlignFrame != null)
2477     {
2478       return initialAlignFrame;
2479     }
2480     System.err
2481             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
2482     return null;
2483   }
2484
2485   /**
2486    * separator used for separatorList
2487    */
2488   protected String separator = "" + ((char) 0x00AC); // the default used to be
2489                                                      // '|' but many sequence
2490                                                      // IDS include pipes.
2491
2492   /**
2493    * set to enable the URL based javascript execution mechanism
2494    */
2495   public boolean jsfallbackEnabled = false;
2496
2497   /**
2498    * parse the string into a list
2499    * 
2500    * @param list
2501    * @return elements separated by separator
2502    */
2503   public String[] separatorListToArray(String list)
2504   {
2505     return separatorListToArray(list, separator);
2506   }
2507
2508   /**
2509    * parse the string into a list
2510    * 
2511    * @param list
2512    * @param separator
2513    * @return elements separated by separator
2514    */
2515   public String[] separatorListToArray(String list, String separator)
2516   {
2517     int seplen = separator.length();
2518     if (list == null || list.equals("") || list.equals(separator))
2519     {
2520       return null;
2521     }
2522     java.util.Vector jv = new Vector();
2523     int cp = 0, pos;
2524     while ((pos = list.indexOf(separator, cp)) > cp)
2525     {
2526       jv.addElement(list.substring(cp, pos));
2527       cp = pos + seplen;
2528     }
2529     if (cp < list.length())
2530     {
2531       String c = list.substring(cp);
2532       if (!c.equals(separator))
2533       {
2534         jv.addElement(c);
2535       }
2536     }
2537     if (jv.size() > 0)
2538     {
2539       String[] v = new String[jv.size()];
2540       for (int i = 0; i < v.length; i++)
2541       {
2542         v[i] = (String) jv.elementAt(i);
2543       }
2544       jv.removeAllElements();
2545       if (debug)
2546       {
2547         System.err.println("Array from '" + separator
2548                 + "' separated List:\n" + v.length);
2549         for (int i = 0; i < v.length; i++)
2550         {
2551           System.err.println("item " + i + " '" + v[i] + "'");
2552         }
2553       }
2554       return v;
2555     }
2556     if (debug)
2557     {
2558       System.err.println("Empty Array from '" + separator
2559               + "' separated List");
2560     }
2561     return null;
2562   }
2563
2564   /**
2565    * concatenate the list with separator
2566    * 
2567    * @param list
2568    * @return concatenated string
2569    */
2570   public String arrayToSeparatorList(String[] list)
2571   {
2572     return arrayToSeparatorList(list, separator);
2573   }
2574
2575   /**
2576    * concatenate the list with separator
2577    * 
2578    * @param list
2579    * @param separator
2580    * @return concatenated string
2581    */
2582   public String arrayToSeparatorList(String[] list, String separator)
2583   {
2584     StringBuffer v = new StringBuffer();
2585     if (list != null && list.length > 0)
2586     {
2587       for (int i = 0, iSize = list.length; i < iSize; i++)
2588       {
2589         if (list[i] != null)
2590         {
2591           if (i > 0)
2592           {
2593             v.append(separator);
2594           }
2595           v.append(list[i]);
2596         }
2597       }
2598       if (debug)
2599       {
2600         System.err.println("Returning '" + separator
2601                 + "' separated List:\n");
2602         System.err.println(v);
2603       }
2604       return v.toString();
2605     }
2606     if (debug)
2607     {
2608       System.err.println("Returning empty '" + separator
2609               + "' separated List\n");
2610     }
2611     return "" + separator;
2612   }
2613
2614   /*
2615    * (non-Javadoc)
2616    * 
2617    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroups()
2618    */
2619   public String getFeatureGroups()
2620   {
2621     String lst = arrayToSeparatorList(getDefaultTargetFrame()
2622             .getFeatureGroups());
2623     return lst;
2624   }
2625
2626   /*
2627    * (non-Javadoc)
2628    * 
2629    * @see
2630    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOn(jalview.appletgui.AlignFrame
2631    * )
2632    */
2633   public String getFeatureGroupsOn(AlignFrame alf)
2634   {
2635     String lst = arrayToSeparatorList(alf.getFeatureGroups());
2636     return lst;
2637   }
2638
2639   /*
2640    * (non-Javadoc)
2641    * 
2642    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfState(boolean)
2643    */
2644   public String getFeatureGroupsOfState(boolean visible)
2645   {
2646     return arrayToSeparatorList(getDefaultTargetFrame()
2647             .getFeatureGroupsOfState(visible));
2648   }
2649
2650   /*
2651    * (non-Javadoc)
2652    * 
2653    * @see
2654    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfStateOn(jalview.appletgui
2655    * .AlignFrame, boolean)
2656    */
2657   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
2658   {
2659     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
2660   }
2661
2662   /*
2663    * (non-Javadoc)
2664    * 
2665    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupStateOn(jalview.appletgui.
2666    * AlignFrame, java.lang.String, boolean)
2667    */
2668   public void setFeatureGroupStateOn(final AlignFrame alf,
2669           final String groups, boolean state)
2670   {
2671     final boolean st = state;// !(state==null || state.equals("") ||
2672     // state.toLowerCase().equals("false"));
2673     java.awt.EventQueue.invokeLater(new Runnable()
2674     {
2675       @Override
2676       public void run()
2677       {
2678         alf.setFeatureGroupState(separatorListToArray(groups), st);
2679       }
2680     });
2681   }
2682
2683   /*
2684    * (non-Javadoc)
2685    * 
2686    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupState(java.lang.String,
2687    * boolean)
2688    */
2689   public void setFeatureGroupState(String groups, boolean state)
2690   {
2691     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
2692   }
2693
2694   /*
2695    * (non-Javadoc)
2696    * 
2697    * @see jalview.bin.JalviewLiteJsApi#getSeparator()
2698    */
2699   public String getSeparator()
2700   {
2701     return separator;
2702   }
2703
2704   /*
2705    * (non-Javadoc)
2706    * 
2707    * @see jalview.bin.JalviewLiteJsApi#setSeparator(java.lang.String)
2708    */
2709   public void setSeparator(String separator)
2710   {
2711     if (separator == null || separator.length() < 1)
2712     {
2713       // reset to default
2714       separator = "" + ((char) 0x00AC);
2715     }
2716     this.separator = separator;
2717     if (debug)
2718     {
2719       System.err.println("Default Separator now: '" + separator + "'");
2720     }
2721   }
2722
2723   /**
2724    * get boolean value of applet parameter 'name' and return default if
2725    * parameter is not set
2726    * 
2727    * @param name
2728    *          name of paremeter
2729    * @param def
2730    *          the value to return otherwise
2731    * @return true or false
2732    */
2733   public boolean getDefaultParameter(String name, boolean def)
2734   {
2735     String stn;
2736     if ((stn = getParameter(name)) == null)
2737     {
2738       return def;
2739     }
2740     if (TRUE.equalsIgnoreCase(stn))
2741     {
2742       return true;
2743     }
2744     return false;
2745   }
2746
2747   /*
2748    * (non-Javadoc)
2749    * 
2750    * @see jalview.bin.JalviewLiteJsApi#addPdbFile(jalview.appletgui.AlignFrame,
2751    * java.lang.String, java.lang.String, java.lang.String)
2752    */
2753   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
2754           String pdbEntryString, String pdbFile)
2755   {
2756     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
2757   }
2758
2759   protected void setAlignPdbStructures(boolean alignPdbStructures)
2760   {
2761     this.alignPdbStructures = alignPdbStructures;
2762   }
2763
2764   public boolean isAlignPdbStructures()
2765   {
2766     return alignPdbStructures;
2767   }
2768
2769   public void start()
2770   {
2771     // callInitCallback();
2772   }
2773
2774   private Hashtable<String, long[]> jshashes = new Hashtable<String, long[]>();
2775
2776   private Hashtable<String, Hashtable<String, String[]>> jsmessages = new Hashtable<String, Hashtable<String, String[]>>();
2777
2778   public void setJsMessageSet(String messageclass, String viewId,
2779           String[] colcommands)
2780   {
2781     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2782     if (msgset == null)
2783     {
2784       msgset = new Hashtable<String, String[]>();
2785       jsmessages.put(messageclass, msgset);
2786     }
2787     msgset.put(viewId, colcommands);
2788     long[] l = new long[colcommands.length];
2789     for (int i = 0; i < colcommands.length; i++)
2790     {
2791       l[i] = colcommands[i].hashCode();
2792     }
2793     jshashes.put(messageclass + "|" + viewId, l);
2794   }
2795
2796   /*
2797    * (non-Javadoc)
2798    * 
2799    * @see jalview.bin.JalviewLiteJsApi#getJsMessage(java.lang.String,
2800    * java.lang.String)
2801    */
2802   public String getJsMessage(String messageclass, String viewId)
2803   {
2804     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2805     if (msgset != null)
2806     {
2807       String[] msgs = msgset.get(viewId);
2808       if (msgs != null)
2809       {
2810         for (int i = 0; i < msgs.length; i++)
2811         {
2812           if (msgs[i] != null)
2813           {
2814             String m = msgs[i];
2815             msgs[i] = null;
2816             return m;
2817           }
2818         }
2819       }
2820     }
2821     return "";
2822   }
2823
2824   public boolean isJsMessageSetChanged(String string, String string2,
2825           String[] colcommands)
2826   {
2827     long[] l = jshashes.get(string + "|" + string2);
2828     if (l == null && colcommands != null)
2829     {
2830       return true;
2831     }
2832     for (int i = 0; i < colcommands.length; i++)
2833     {
2834       if (l[i] != colcommands[i].hashCode())
2835       {
2836         return true;
2837       }
2838     }
2839     return false;
2840   }
2841
2842   private Vector jsExecQueue = new Vector();
2843
2844   public Vector getJsExecQueue()
2845   {
2846     return jsExecQueue;
2847   }
2848
2849   public void setExecutor(JSFunctionExec jsFunctionExec2)
2850   {
2851     jsFunctionExec = jsFunctionExec2;
2852   }
2853
2854   /**
2855    * return the given colour value parameter or the given default if parameter
2856    * not given
2857    * 
2858    * @param colparam
2859    * @param defcolour
2860    * @return
2861    */
2862   public Color getDefaultColourParameter(String colparam, Color defcolour)
2863   {
2864     String colprop = getParameter(colparam);
2865     if (colprop == null || colprop.trim().length() == 0)
2866     {
2867       return defcolour;
2868     }
2869     Color col = jalview.schemes.ColourSchemeProperty
2870             .getAWTColorFromName(colprop);
2871     if (col == null)
2872     {
2873       try
2874       {
2875         col = new jalview.schemes.UserColourScheme(colprop).findColour('A');
2876       } catch (Exception ex)
2877       {
2878         System.err.println("Couldn't parse '" + colprop
2879                 + "' as a colour for " + colparam);
2880         col = null;
2881       }
2882     }
2883     return (col == null) ? defcolour : col;
2884
2885   }
2886
2887   public void openJalviewHelpUrl()
2888   {
2889     String helpUrl = getParameter("jalviewhelpurl");
2890     if (helpUrl == null || helpUrl.trim().length() < 5)
2891     {
2892       helpUrl = "http://www.jalview.org/help.html";
2893     }
2894     showURL(helpUrl, "HELP");
2895   }
2896
2897   /**
2898    * form a complete URL given a path to a resource and a reference location on
2899    * the same server
2900    * 
2901    * @param url
2902    *          - an absolute path on the same server as localref or a document
2903    *          located relative to localref
2904    * @param localref
2905    *          - a URL on the same server as url
2906    * @return a complete URL for the resource located by url
2907    */
2908   private String resolveUrlForLocalOrAbsolute(String url, URL localref)
2909   {
2910     String codebase = localref.toString();
2911     if (url.indexOf("/") == 0)
2912     {
2913       String localfile = localref.getFile();
2914       url = codebase.substring(0, codebase.length() - localfile.length())
2915               + url;
2916     }
2917     else
2918     {
2919       url = localref + url;
2920     }
2921     if (debug)
2922     {
2923       System.err.println("URL: " + localref.toString());
2924       System.err.println("URL.getFile: " + localref.getFile());
2925       System.err.println("URL.getPath: " + localref.getPath());
2926       System.err.println("URL.getQuery: " + localref.getQuery());
2927       System.err.println("returning " + url);
2928     }
2929     return url;
2930   }
2931
2932   /**
2933    * open a URL in the browser - resolving it according to relative refs and
2934    * coping with javascript: protocol if necessary.
2935    * 
2936    * @param url
2937    * @param target
2938    */
2939   public void showURL(String url, String target)
2940   {
2941     try
2942     {
2943       if (url.indexOf(":") == -1)
2944       {
2945         // TODO: verify (Bas Vroling bug) prepend codebase or server URL to
2946         // form valid URL
2947         // Should really use docbase, not codebase.
2948         URL prepend;
2949         url = resolveUrlForLocalOrAbsolute(
2950                 url,
2951                 prepend = getDefaultParameter("resolvetocodebase", false) ? getDocumentBase()
2952                         : getCodeBase());
2953         if (debug)
2954         {
2955           System.err
2956                   .println("Show url (prepended "
2957                           + prepend
2958                           + " - toggle resolvetocodebase if code/docbase resolution is wrong): "
2959                           + url);
2960         }
2961       }
2962       else
2963       {
2964         if (debug)
2965         {
2966           System.err.println("Show url: " + url);
2967         }
2968       }
2969       if (url.indexOf("javascript:") == 0)
2970       {
2971         // no target for the javascript context
2972         getAppletContext().showDocument(new java.net.URL(url));
2973       }
2974       else
2975       {
2976         getAppletContext().showDocument(new java.net.URL(url), target);
2977       }
2978     } catch (Exception ex)
2979     {
2980       ex.printStackTrace();
2981     }
2982   }
2983
2984   /**
2985    * bind structures in a viewer to any matching sequences in an alignFrame (use
2986    * sequenceIds to limit scope of search to specific sequences)
2987    * 
2988    * @param alFrame
2989    * @param viewer
2990    * @param sequenceIds
2991    * @return TODO: consider making an exception structure for indicating when
2992    *         binding fails public SequenceStructureBinding
2993    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
2994    *         sequenceIds) {
2995    * 
2996    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
2997    *         alFrame.addStructureViewInstance(viewer,
2998    *         separatorListToArray(sequenceIds)); } else { return
2999    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
3000    */
3001 }