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