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