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