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