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