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