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