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