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