additional test - http://www.koders.com/java/fid4C9593FE80CE8AA81158AF712B50C7A689488...
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.bin;
19
20 import jalview.appletgui.AlignFrame;
21 import jalview.appletgui.EmbmenuFrame;
22 import jalview.appletgui.FeatureSettings;
23 import jalview.datamodel.Alignment;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.ColumnSelection;
26 import jalview.datamodel.PDBEntry;
27 import jalview.datamodel.Sequence;
28 import jalview.datamodel.SequenceGroup;
29 import jalview.datamodel.SequenceI;
30 import jalview.io.AnnotationFile;
31 import jalview.io.AppletFormatAdapter;
32 import jalview.io.FileParse;
33 import jalview.io.IdentifyFile;
34 import jalview.io.JnetAnnotationMaker;
35 import jalview.javascript.JSFunctionExec;
36 import jalview.javascript.JsCallBack;
37 import jalview.structure.SelectionListener;
38 import jalview.structure.StructureSelectionManager;
39
40 import java.applet.Applet;
41 import java.awt.Button;
42 import java.awt.Color;
43 import java.awt.Component;
44 import java.awt.Font;
45 import java.awt.Frame;
46 import java.awt.Graphics;
47 import java.awt.event.ActionEvent;
48 import java.awt.event.WindowAdapter;
49 import java.awt.event.WindowEvent;
50 import java.io.BufferedReader;
51 import java.io.InputStreamReader;
52 import java.util.StringTokenizer;
53 import java.util.Vector;
54
55 import netscape.javascript.JSObject;
56
57 /**
58  * Jalview Applet. Runs in Java 1.18 runtime
59  * 
60  * @author $author$
61  * @version $Revision$
62  */
63 public class JalviewLite extends Applet
64 {
65
66   // /////////////////////////////////////////
67   // The following public methods maybe called
68   // externally, eg via javascript in HTML page
69   /**
70    * @return String list of selected sequence IDs, each terminated by the 'boolean not' character (""+0x00AC) or
71    *         (&#172;)
72    */
73   public String getSelectedSequences()
74   {
75     return getSelectedSequencesFrom(getDefaultTargetFrame());
76   }
77
78   /**
79    * @param sep
80    *          separator string or null for default
81    * @return String list of selected sequence IDs, each terminated by given separator string
82    */
83   public String getSelectedSequences(String sep)
84   {
85     return getSelectedSequencesFrom(getDefaultTargetFrame(), sep);
86   }
87
88   /**
89    * @param alf
90    *          alignframe containing selection
91    * @return String list of selected sequence IDs, each terminated by current default separator sequence
92    * 
93    */
94   public String getSelectedSequencesFrom(AlignFrame alf)
95   {
96     return getSelectedSequencesFrom(alf,separator); // ""+0x00AC);
97   }
98
99   /**
100    * get list of selected sequence IDs separated by given separator
101    * 
102    * @param alf
103    *          window containing selection
104    * @param sep
105    *          separator string to use - default is 'boolean not'
106    * @return String list of selected sequence IDs, each terminated by the given
107    *         separator
108    */
109   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
110   {
111     StringBuffer result = new StringBuffer("");
112     if (sep == null || sep.length() == 0)
113     {
114       sep = separator; // "+0x00AC;
115     }
116     if (alf.viewport.getSelectionGroup() != null)
117     {
118       SequenceI[] seqs = alf.viewport.getSelectionGroup()
119               .getSequencesInOrder(alf.viewport.getAlignment());
120
121       for (int i = 0; i < seqs.length; i++)
122       {
123         result.append(seqs[i].getName());
124         result.append(sep);
125       }
126     }
127
128     return result.toString();
129   }
130
131   /**
132    * 
133    * @param sequenceId
134    *          id of sequence to highlight
135    * @param position
136    *          integer position [ tobe implemented or range ] on sequence
137    * @param alignedPosition
138    *          true/false/empty string - indicate if position is an alignment
139    *          column or unaligned sequence position
140    */
141   public void highlight(String sequenceId, String position,
142           String alignedPosition)
143   {
144     highlightIn(getDefaultTargetFrame(), sequenceId, position, alignedPosition);
145   }
146
147   /**
148    * 
149    * @param sequenceId
150    *          id of sequence to highlight
151    * @param position
152    *          integer position [ tobe implemented or range ] on sequence
153    * @param alignedPosition
154    *          false, blank or something else - indicate if position is an
155    *          alignment column or unaligned sequence position
156    */
157   public void highlightIn(AlignFrame alf, String sequenceId, String position,
158           String alignedPosition)
159   {
160     // TODO: could try to highlight in all alignments if alf==null
161     SequenceI sq = alf.getAlignViewport().getAlignment()
162             .findName(sequenceId);
163     if (sq != null)
164     {
165       int pos, apos = -1;
166       try
167       {
168         apos = new Integer(position).intValue();
169         apos--;
170       } catch (NumberFormatException ex)
171       {
172         return;
173       }
174       // use vamsas listener to broadcast to all listeners in scope
175       if (alignedPosition != null
176               && (alignedPosition.trim().length() == 0 || alignedPosition
177                       .toLowerCase().indexOf("false") > -1))
178       {
179         StructureSelectionManager.getStructureSelectionManager()
180                 .mouseOverVamsasSequence(sq, sq.findIndex(apos));
181       }
182       else
183       {
184         StructureSelectionManager.getStructureSelectionManager()
185                 .mouseOverVamsasSequence(sq, apos);
186       }
187
188     }
189   }
190
191   /**
192    * select regions of the currrent alignment frame
193    * 
194    * @param sequenceIds
195    *          String separated list of sequence ids or empty string
196    * @param columns
197    *          String separated list { column range or column, ..} or empty
198    *          string
199    */
200   public void select(String sequenceIds, String columns)
201   {
202     selectIn(getDefaultTargetFrame(), sequenceIds, columns, separator);
203   }
204
205   /**
206    * select regions of the currrent alignment frame
207    * 
208    * @param toselect
209    *          String separated list { column range, seq1...seqn sequence ids }
210    * @param sep
211    *          separator between toselect fields
212    */
213   public void select(String sequenceIds, String columns, String sep)
214   {
215     selectIn(getDefaultTargetFrame(), sequenceIds, columns, sep);
216   }
217
218   /**
219    * select regions of the given alignment frame
220    * 
221    * @param alf
222    * @param toselect
223    *          String separated list { column range, seq1...seqn sequence ids }
224    * @param sep
225    *          separator between toselect fields
226    */
227   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
228   {
229     selectIn(alf, sequenceIds, columns, separator);
230   }
231
232   /**
233    * select regions of the given alignment frame
234    * 
235    * @param alf
236    * @param toselect
237    *          String separated list { column range, seq1...seqn sequence ids }
238    * @param sep
239    *          separator between toselect fields
240    */
241   public void selectIn(AlignFrame alf, String sequenceIds, String columns,
242           String sep)
243   {
244     if (sep == null || sep.length() == 0)
245     {
246       sep = separator;
247     } else {
248       if (debug)
249       {
250         System.err.println("Selecting region using separator string '"+separator+"'");
251       }
252     }
253     // deparse fields
254     String[] ids = separatorListToArray(sequenceIds, sep);
255     String[] cols = separatorListToArray(columns, sep);
256     SequenceGroup sel = new SequenceGroup();
257     ColumnSelection csel = new ColumnSelection();
258     AlignmentI al = alf.viewport.getAlignment();
259     int start = 0, end = al.getWidth(), alw = al.getWidth();
260     if (ids != null && ids.length > 0)
261     {
262       for (int i = 0; i < ids.length; i++)
263       {
264         if (ids[i].trim().length() == 0)
265         {
266           continue;
267         }
268         SequenceI sq = al.findName(ids[i]);
269         if (sq != null)
270         {
271           sel.addSequence(sq, false);
272         }
273       }
274     }
275     if (cols != null && cols.length > 0)
276     {
277       boolean seset = false;
278       for (int i = 0; i < cols.length; i++)
279       {
280         String cl = cols[i].trim();
281         if (cl.length() == 0)
282         {
283           continue;
284         }
285         int p;
286         if ((p = cl.indexOf("-")) > -1)
287         {
288           int from = -1, to = -1;
289           try
290           {
291             from = new Integer(cl.substring(0, p)).intValue();
292             from--;
293           } catch (NumberFormatException ex)
294           {
295             System.err
296                     .println("ERROR: Couldn't parse first integer in range element column selection string '"
297                             + cl + "' - format is 'from-to'");
298             return;
299           }
300           try
301           {
302             to = new Integer(cl.substring(p + 1)).intValue();
303             to--;
304           } catch (NumberFormatException ex)
305           {
306             System.err
307                     .println("ERROR: Couldn't parse second integer in range element column selection string '"
308                             + cl + "' - format is 'from-to'");
309             return;
310           }
311           if (from >= 0 && to >= 0)
312           {
313             // valid range
314             if (from < to)
315             {
316               int t = to;
317               to = from;
318               to = t;
319             }
320             if (!seset)
321             {
322               start = from;
323               end = to;
324               seset = true;
325             }
326             else
327             {
328               // comment to prevent range extension
329               if (start > from)
330               {
331                 start = from;
332               }
333               if (end < to)
334               {
335                 end = to;
336               }
337             }
338             for (int r = from; r <= to; r++)
339             {
340               if (r >= 0 && r < alw)
341               {
342                 csel.addElement(r);
343               }
344             }
345             if (debug)
346             {
347               System.err.println("Range '" + cl + "' deparsed as [" + from
348                       + "," + to + "]");
349             }
350           }
351           else
352           {
353             System.err.println("ERROR: Invalid Range '" + cl
354                     + "' deparsed as [" + from + "," + to + "]");
355           }
356         }
357         else
358         {
359           int r = -1;
360           try
361           {
362             r = new Integer(cl).intValue();
363             r--;
364           } catch (NumberFormatException ex)
365           {
366             System.err
367                     .println("ERROR: Couldn't parse integer from point selection element of column selection string '"
368                             + cl + "'");
369             return;
370           }
371           if (r >= 0 && r <= alw)
372           {
373             if (!seset)
374             {
375               start = r;
376               end = r;
377               seset = true;
378             }
379             else
380             {
381               // comment to prevent range extension
382               if (start > r)
383               {
384                 start = r;
385               }
386               if (end < r)
387               {
388                 end = r;
389               }
390             }
391             csel.addElement(r);
392             if (debug)
393             {
394               System.err.println("Point selection '" + cl
395                       + "' deparsed as [" + r + "]");
396             }
397           }
398           else
399           {
400             System.err.println("ERROR: Invalid Point selection '" + cl
401                     + "' deparsed as [" + r + "]");
402           }
403         }
404       }
405     }
406     sel.setStartRes(start);
407     sel.setEndRes(end);
408     alf.select(sel, csel);
409
410   }
411
412   /**
413    * get sequences selected in current alignFrame and return their alignment in
414    * format 'format' either with or without suffix
415    * 
416    * @param alf
417    *          - where selection is
418    * @param format
419    *          - format of alignment file
420    * @param suffix
421    *          - "true" to append /start-end string to each sequence ID
422    * @return selected sequences as flat file or empty string if there was no
423    *         current selection
424    */
425   public String getSelectedSequencesAsAlignment(String format, String suffix)
426   {
427     return getSelectedSequencesAsAlignmentFrom(getDefaultTargetFrame(), format,
428             suffix);
429   }
430
431   /**
432    * get sequences selected in alf and return their alignment in format 'format'
433    * either with or without suffix
434    * 
435    * @param alf
436    *          - where selection is
437    * @param format
438    *          - format of alignment file
439    * @param suffix
440    *          - "true" to append /start-end string to each sequence ID
441    * @return selected sequences as flat file or empty string if there was no
442    *         current selection
443    */
444   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
445           String format, String suffix)
446   {
447     try
448     {
449       boolean seqlimits = suffix.equalsIgnoreCase("true");
450       if (alf.viewport.getSelectionGroup() != null)
451       {
452         String reply = new AppletFormatAdapter().formatSequences(format,
453                 new Alignment(alf.viewport.getSelectionAsNewSequence()),
454                 seqlimits);
455         return reply;
456       }
457     } catch (Exception ex)
458     {
459       ex.printStackTrace();
460       return "Error retrieving alignment in " + format + " format. ";
461     }
462     return "";
463   }
464
465   public String getAlignment(String format)
466   {
467     return getAlignmentFrom(getDefaultTargetFrame(), format, "true");
468   }
469
470   public String getAlignmentFrom(AlignFrame alf, String format)
471   {
472     return getAlignmentFrom(alf, format, "true");
473   }
474
475   public String getAlignment(String format, String suffix)
476   {
477     return getAlignmentFrom(getDefaultTargetFrame(), format, suffix);
478   }
479
480   public String getAlignmentFrom(AlignFrame alf, String format,
481           String suffix)
482   {
483     try
484     {
485       boolean seqlimits = suffix.equalsIgnoreCase("true");
486
487       String reply = new AppletFormatAdapter().formatSequences(format,
488               alf.viewport.getAlignment(), seqlimits);
489       return reply;
490     } catch (Exception ex)
491     {
492       ex.printStackTrace();
493       return "Error retrieving alignment in " + format + " format. ";
494     }
495   }
496
497   public void loadAnnotation(String annotation)
498   {
499     loadAnnotationFrom(getDefaultTargetFrame(), annotation);
500   }
501
502   public void loadAnnotationFrom(AlignFrame alf, String annotation)
503   {
504     if (new AnnotationFile().readAnnotationFile(alf.getAlignViewport()
505             .getAlignment(), annotation, AppletFormatAdapter.PASTE))
506     {
507       alf.alignPanel.fontChanged();
508       alf.alignPanel.setScrollValues(0, 0);
509     }
510     else
511     {
512       alf.parseFeaturesFile(annotation, AppletFormatAdapter.PASTE);
513     }
514   }
515
516   public String getFeatures(String format)
517   {
518     return getFeaturesFrom(getDefaultTargetFrame(), format);
519   }
520
521   public String getFeaturesFrom(AlignFrame alf, String format)
522   {
523     return alf.outputFeatures(false, format);
524   }
525
526   public String getAnnotation()
527   {
528     return getAnnotationFrom(getDefaultTargetFrame());
529   }
530
531   public String getAnnotationFrom(AlignFrame alf)
532   {
533     return alf.outputAnnotations(false);
534   }
535
536   public AlignFrame newView()
537   {
538     return newViewFrom(getDefaultTargetFrame());
539   }
540
541   public AlignFrame newView(String name)
542   {
543     return newViewFrom(getDefaultTargetFrame(), name);
544   }
545
546   public AlignFrame newViewFrom(AlignFrame alf)
547   {
548     return alf.newView(null);
549   }
550
551   public AlignFrame newViewFrom(AlignFrame alf, String name)
552   {
553     return alf.newView(name);
554   }
555
556   /**
557    * 
558    * @param text
559    *          alignment file as a string
560    * @param title
561    *          window title
562    * @return null or new alignment frame
563    */
564   public AlignFrame loadAlignment(String text, String title)
565   {
566     Alignment al = null;
567
568     String format = new IdentifyFile().Identify(text,
569             AppletFormatAdapter.PASTE);
570     try
571     {
572       al = new AppletFormatAdapter().readFile(text,
573               AppletFormatAdapter.PASTE, format);
574       if (al.getHeight() > 0)
575       {
576         return new AlignFrame(al, this, title, false);
577       }
578     } catch (java.io.IOException ex)
579     {
580       ex.printStackTrace();
581     }
582     return null;
583   }
584
585   public void setMouseoverListener(String listener)
586   {
587     setMouseoverListener(currentAlignFrame, listener);
588   }
589
590   private Vector mouseoverListeners = new Vector();
591
592   public void setMouseoverListener(AlignFrame af, String listener)
593   {
594     if (listener != null)
595     {
596       listener = listener.trim();
597       if (listener.length() == 0)
598       {
599         System.err
600                 .println("jalview Javascript error: Ignoring empty function for mouseover listener.");
601         return;
602       }
603     }
604     jalview.javascript.MouseOverListener mol = new jalview.javascript.MouseOverListener(
605             this, af, listener);
606     mouseoverListeners.addElement(mol);
607     StructureSelectionManager.getStructureSelectionManager()
608             .addStructureViewerListener(mol);
609     if (debug)
610     {
611       System.err.println("Added a mouseover listener for "
612               + ((af == null) ? "All frames" : "Just views for "
613                       + af.getAlignViewport().getSequenceSetId()));
614       System.err.println("There are now " + mouseoverListeners.size()
615               + " listeners in total.");
616     }
617   }
618
619   public void setSelectionListener(String listener)
620   {
621     setSelectionListener(null, listener);
622   }
623
624   public void setSelectionListener(AlignFrame af, String listener)
625   {
626     if (listener != null)
627     {
628       listener = listener.trim();
629       if (listener.length() == 0)
630       {
631         System.err
632                 .println("jalview Javascript error: Ignoring empty function for selection listener.");
633         return;
634       }
635     }
636     jalview.javascript.JsSelectionSender mol = new jalview.javascript.JsSelectionSender(
637             this, af, listener);
638     mouseoverListeners.addElement(mol);
639     StructureSelectionManager.getStructureSelectionManager()
640             .addSelectionListener(mol);
641     if (debug)
642     {
643       System.err.println("Added a selection listener for "
644               + ((af == null) ? "All frames" : "Just views for "
645                       + af.getAlignViewport().getSequenceSetId()));
646       System.err.println("There are now " + mouseoverListeners.size()
647               + " listeners in total.");
648     }
649   }
650
651   /**
652    * remove any callback using the given listener function and associated with
653    * the given alignFrame (or null for all callbacks)
654    * 
655    * @param af
656    *          (may be null)
657    * @param listener
658    *          (may be null)
659    */
660   public void removeJavascriptListener(AlignFrame af, String listener)
661   {
662     if (listener != null)
663     {
664       listener = listener.trim();
665       if (listener.length() == 0)
666       {
667         listener = null;
668       }
669     }
670     boolean rprt = false;
671     for (int ms = 0, msSize = mouseoverListeners.size(); ms < msSize;)
672     {
673       Object lstn = mouseoverListeners.elementAt(ms);
674       JsCallBack lstner = (JsCallBack) lstn;
675       if ((af == null || lstner.getAlignFrame() == af)
676               && (listener == null || lstner.getListenerFunction().equals(
677                       listener)))
678       {
679         mouseoverListeners.removeElement(lstner);
680         msSize--;
681         if (lstner instanceof SelectionListener)
682         {
683           StructureSelectionManager.getStructureSelectionManager()
684                   .removeSelectionListener((SelectionListener) lstner);
685         }
686         else
687         {
688           StructureSelectionManager.getStructureSelectionManager()
689                   .removeStructureViewerListener(lstner, null);
690         }
691         rprt = debug;
692         if (debug)
693         {
694           System.err.println("Removed listener '" + listener + "'");
695         }
696       }
697       else
698       {
699         ms++;
700       }
701     }
702     if (rprt)
703     {
704       System.err.println("There are now " + mouseoverListeners.size()
705               + " listeners in total.");
706     }
707   }
708
709   public void stop()
710   {
711     if (mouseoverListeners != null)
712     {
713       while (mouseoverListeners.size() > 0)
714       {
715         Object mol = mouseoverListeners.elementAt(0);
716         mouseoverListeners.removeElement(mol);
717         if (mol instanceof SelectionListener)
718         {
719           StructureSelectionManager.getStructureSelectionManager()
720                   .removeSelectionListener((SelectionListener) mol);
721         }
722         else
723         {
724           StructureSelectionManager.getStructureSelectionManager()
725                   .removeStructureViewerListener(mol, null);
726         }
727       }
728     }
729     jalview.javascript.JSFunctionExec.stopQueue();
730   }
731
732   /**
733    * send a mouseover message to all the alignment windows associated with the
734    * given residue in the pdbfile
735    * 
736    * @param pdbResNum
737    * @param chain
738    * @param pdbfile
739    */
740   public void mouseOverStructure(int pdbResNum, String chain, String pdbfile)
741   {
742     StructureSelectionManager.getStructureSelectionManager()
743             .mouseOverStructure(pdbResNum, chain, pdbfile);
744   }
745
746   // //////////////////////////////////////////////
747   // //////////////////////////////////////////////
748
749   public static int lastFrameX = 200;
750
751   public static int lastFrameY = 200;
752
753   boolean fileFound = true;
754
755   String file = "No file";
756
757   Button launcher = new Button("Start Jalview");
758
759   /**
760    * The currentAlignFrame is static, it will change if and when the user
761    * selects a new window. Note that it will *never* point back to the embedded
762    * AlignFrame if the applet is started as embedded on the page and then
763    * afterwards a new view is created.
764    */
765   public AlignFrame currentAlignFrame = null;
766
767   /**
768    * This is the first frame to be displayed, and does not change. API calls
769    * will default to this instance if currentAlignFrame is null.
770    */
771   AlignFrame initialAlignFrame = null;
772
773   boolean embedded = false;
774
775   private boolean checkForJmol = true;
776
777   private boolean checkedForJmol = false; // ensure we don't check for jmol
778
779   // every time the app is re-inited
780
781   public boolean jmolAvailable = false;
782
783   private boolean alignPdbStructures = false;
784
785   public static boolean debug = false;
786
787   static String builddate = null, version = null;
788
789   private static void initBuildDetails()
790   {
791     if (builddate == null)
792     {
793       builddate = "unknown";
794       version = "test";
795       java.net.URL url = JalviewLite.class
796               .getResource("/.build_properties");
797       if (url != null)
798       {
799         try
800         {
801           BufferedReader reader = new BufferedReader(new InputStreamReader(
802                   url.openStream()));
803           String line;
804           while ((line = reader.readLine()) != null)
805           {
806             if (line.indexOf("VERSION") > -1)
807             {
808               version = line.substring(line.indexOf("=") + 1);
809             }
810             if (line.indexOf("BUILD_DATE") > -1)
811             {
812               builddate = line.substring(line.indexOf("=") + 1);
813             }
814           }
815         } catch (Exception ex)
816         {
817           ex.printStackTrace();
818         }
819       }
820     }
821   }
822
823   public static String getBuildDate()
824   {
825     initBuildDetails();
826     return builddate;
827   }
828
829   public static String getVersion()
830   {
831     initBuildDetails();
832     return version;
833   }
834
835   // public JSObject scriptObject = null;
836
837   /**
838    * init method for Jalview Applet
839    */
840   public void init()
841   {
842     // remove any handlers that might be hanging around from an earlier instance
843     try
844     {
845       if (getAppletContext() instanceof sun.plugin.javascript.JSContext) {
846         if (debug)
847         {
848           System.err.println("Applet context implements JSContext - should have callback support!");
849         }
850       }
851        JSObject scriptObject = JSObject.getWindow(this);  
852     } catch (Exception ex)
853     {
854       System.err
855       .println("Warning: No JalviewLite javascript callbacks available.");
856       if (debug)
857       {
858         ex.printStackTrace();
859       }
860     }
861     /**
862      * turn on extra applet debugging
863      */
864     String dbg = getParameter("debug");
865     if (dbg != null)
866     {
867       debug = dbg.toLowerCase().equals("true");
868     }
869     if (debug)
870     {
871
872       System.err.println("JalviewLite Version " + getVersion());
873       System.err.println("Build Date : " + getBuildDate());
874
875     }
876     /**
877      * if true disable the check for jmol
878      */
879     String chkforJmol = getParameter("nojmol");
880     if (chkforJmol != null)
881     {
882       checkForJmol = !chkforJmol.equals("true");
883     }
884     /**
885      * get the separator parameter if present
886      */
887     String sep = getParameter("separator");
888     if (sep != null)
889     {
890       if (sep.length() > 0)
891       {
892         separator = sep;
893         if (debug)
894         {
895           System.err.println("Separator set to '" + separator + "'");
896         }
897       }
898       else
899       {
900         throw new Error(
901                 "Invalid separator parameter - must be non-zero length");
902       }
903     }
904     int r = 255;
905     int g = 255;
906     int b = 255;
907     String param = getParameter("RGB");
908
909     if (param != null)
910     {
911       try
912       {
913         r = Integer.parseInt(param.substring(0, 2), 16);
914         g = Integer.parseInt(param.substring(2, 4), 16);
915         b = Integer.parseInt(param.substring(4, 6), 16);
916       } catch (Exception ex)
917       {
918         r = 255;
919         g = 255;
920         b = 255;
921       }
922     }
923     param = getParameter("label");
924     if (param != null)
925     {
926       launcher.setLabel(param);
927     }
928
929     setBackground(new Color(r, g, b));
930     
931     file = getParameter("file");
932
933     if (file == null)
934     {
935       // Maybe the sequences are added as parameters
936       StringBuffer data = new StringBuffer("PASTE");
937       int i = 1;
938       while ((file = getParameter("sequence" + i)) != null)
939       {
940         data.append(file.toString() + "\n");
941         i++;
942       }
943       if (data.length() > 5)
944       {
945         file = data.toString();
946       }
947     }
948
949     final JalviewLite jvapplet = this;
950     if (getParameter("embedded") != null
951             && getParameter("embedded").equalsIgnoreCase("true"))
952     {
953       // Launch as embedded applet in page
954       embedded = true;
955       LoadingThread loader = new LoadingThread(file, jvapplet);
956       loader.start();
957     }
958     else if (file != null)
959     {
960       if (getParameter("showbutton") == null
961               || !getParameter("showbutton").equalsIgnoreCase(
962                       "false"))
963       {
964         // Add the JalviewLite 'Button' to the page
965         add(launcher);
966         launcher.addActionListener(new java.awt.event.ActionListener()
967         {
968           public void actionPerformed(ActionEvent e)
969           {
970             LoadingThread loader = new LoadingThread(file, jvapplet);
971             loader.start();
972           }
973         });
974       }
975       else
976       {
977         // Open jalviewLite immediately.
978         LoadingThread loader = new LoadingThread(file, jvapplet);
979         loader.start();
980       }
981     }
982     else
983     {
984       // jalview initialisation with no alignment. loadAlignment() method can
985       // still be called to open new alignments.
986       file = "NO FILE";
987       fileFound = false;
988       // callInitCallback();
989     }
990   }
991
992   private void callInitCallback()
993   {
994     String initjscallback = getParameter("oninit");
995     if (initjscallback == null)
996     {
997       return;
998     }
999     initjscallback = initjscallback.trim();
1000     if (initjscallback.length() > 0)
1001     {
1002       JSObject scriptObject = null; 
1003       try {
1004         scriptObject = JSObject.getWindow(this);
1005       } catch (Exception ex) {};
1006       if (scriptObject != null)
1007       {
1008         try
1009         {
1010           // do onInit with the JS executor thread
1011           new JSFunctionExec(this).executeJavascriptFunction(true,
1012                   initjscallback, null, "Calling oninit callback '" + initjscallback
1013                   + "'.");
1014         } catch (Exception e)
1015         {
1016           System.err.println("Exception when executing _oninit callback '"
1017                   + initjscallback + "'.");
1018           e.printStackTrace();
1019         }
1020       }
1021       else
1022       {
1023         System.err.println("Not executing _oninit callback '"
1024                 + initjscallback + "' - no scripting allowed.");
1025       }
1026     }
1027   }
1028
1029   /**
1030    * Initialises and displays a new java.awt.Frame
1031    * 
1032    * @param frame
1033    *          java.awt.Frame to be displayed
1034    * @param title
1035    *          title of new frame
1036    * @param width
1037    *          width if new frame
1038    * @param height
1039    *          height of new frame
1040    */
1041   public static void addFrame(final Frame frame, String title, int width,
1042           int height)
1043   {
1044     frame.setLocation(lastFrameX, lastFrameY);
1045     lastFrameX += 40;
1046     lastFrameY += 40;
1047     frame.setSize(width, height);
1048     frame.setTitle(title);
1049     frame.addWindowListener(new WindowAdapter()
1050     {
1051       public void windowClosing(WindowEvent e)
1052       {
1053         if (frame instanceof AlignFrame)
1054         {
1055           ((AlignFrame) frame).closeMenuItem_actionPerformed();
1056           if (((AlignFrame) frame).viewport.applet.currentAlignFrame == frame)
1057           {
1058             ((AlignFrame) frame).viewport.applet.currentAlignFrame = null;
1059           }
1060         }
1061         lastFrameX -= 40;
1062         lastFrameY -= 40;
1063         if (frame instanceof EmbmenuFrame)
1064         {
1065           ((EmbmenuFrame) frame).destroyMenus();
1066         }
1067         frame.setMenuBar(null);
1068         frame.dispose();
1069       }
1070
1071       public void windowActivated(WindowEvent e)
1072       {
1073         if (frame instanceof AlignFrame)
1074         {
1075           ((AlignFrame) frame).viewport.applet.currentAlignFrame = (AlignFrame) frame;
1076           if (debug)
1077           {
1078             System.err.println("Activated window " + frame);
1079           }
1080         }
1081         // be good.
1082         super.windowActivated(e);
1083       }
1084       /*
1085        * Probably not necessary to do this - see TODO above. (non-Javadoc)
1086        * 
1087        * @see
1088        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
1089        * )
1090        * 
1091        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
1092        * frame) { currentAlignFrame = null; if (debug) {
1093        * System.err.println("Deactivated window "+frame); } }
1094        * super.windowDeactivated(e); }
1095        */
1096     });
1097     frame.setVisible(true);
1098   }
1099
1100   /**
1101    * This paints the background surrounding the "Launch Jalview button" <br>
1102    * <br>
1103    * If file given in parameter not found, displays error message
1104    * 
1105    * @param g
1106    *          graphics context
1107    */
1108   public void paint(Graphics g)
1109   {
1110     if (!fileFound)
1111     {
1112       g.setColor(new Color(200, 200, 200));
1113       g.setColor(Color.cyan);
1114       g.fillRect(0, 0, getSize().width, getSize().height);
1115       g.setColor(Color.red);
1116       g.drawString("Jalview can't open file", 5, 15);
1117       g.drawString("\"" + file + "\"", 5, 30);
1118     }
1119     else if (embedded)
1120     {
1121       g.setColor(Color.black);
1122       g.setFont(new Font("Arial", Font.BOLD, 24));
1123       g.drawString("Jalview Applet", 50, getSize().height / 2 - 30);
1124       g.drawString("Loading Data...", 50, getSize().height / 2);
1125     }
1126   }
1127   /**
1128    * get all components associated with the applet of the given type
1129    * 
1130    * @param class1
1131    * @return
1132    */
1133   public Vector getAppletWindow(Class class1)
1134   {
1135     Vector wnds = new Vector();
1136     Component[] cmp = getComponents();
1137     if (cmp != null)
1138     {
1139       for (int i = 0; i < cmp.length; i++)
1140       {
1141         if (class1.isAssignableFrom(cmp[i].getClass()))
1142         {
1143           wnds.addElement(cmp);
1144         }
1145       }
1146     }
1147     return wnds;
1148   }
1149
1150   class LoadJmolThread extends Thread
1151   {
1152     private boolean running = false;
1153
1154     public void run()
1155     {
1156       if (running || checkedForJmol)
1157       {
1158         return;
1159       }
1160       running = true;
1161       if (checkForJmol)
1162       {
1163         try
1164         {
1165           if (!System.getProperty("java.version").startsWith("1.1"))
1166           {
1167             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
1168             jmolAvailable = true;
1169           }
1170           if (!jmolAvailable)
1171           {
1172             System.out
1173                     .println("Jmol not available - Using MCview for structures");
1174           }
1175         } catch (java.lang.ClassNotFoundException ex)
1176         {
1177         }
1178       }
1179       else
1180       {
1181         jmolAvailable = false;
1182         if (debug)
1183         {
1184           System.err
1185                   .println("Skipping Jmol check. Will use MCView (probably)");
1186         }
1187       }
1188       checkedForJmol = true;
1189       running = false;
1190     }
1191
1192     public boolean notFinished()
1193     {
1194       return running || !checkedForJmol;
1195     }
1196   }
1197
1198   class LoadingThread extends Thread
1199   {
1200     /**
1201      * State variable: File source
1202      */
1203     String file;
1204
1205     /**
1206      * State variable: protocol for access to file source
1207      */
1208     String protocol;
1209
1210     /**
1211      * State variable: format of file source
1212      */
1213     String format;
1214
1215     String _file;
1216
1217     JalviewLite applet;
1218
1219     private void dbgMsg(String msg)
1220     {
1221       if (applet.debug)
1222       {
1223         System.err.println(msg);
1224       }
1225     }
1226
1227     /**
1228      * update the protocol state variable for accessing the datasource located
1229      * by file.
1230      * 
1231      * @param file
1232      * @return possibly updated datasource string
1233      */
1234     public String setProtocolState(String file)
1235     {
1236       if (file.startsWith("PASTE"))
1237       {
1238         file = file.substring(5);
1239         protocol = AppletFormatAdapter.PASTE;
1240       }
1241       else if (inArchive(file))
1242       {
1243         protocol = AppletFormatAdapter.CLASSLOADER;
1244       }
1245       else
1246       {
1247         file = addProtocol(file);
1248         protocol = AppletFormatAdapter.URL;
1249       }
1250       dbgMsg("Protocol identified as '" + protocol + "'");
1251       return file;
1252     }
1253
1254     public LoadingThread(String _file, JalviewLite _applet)
1255     {
1256       this._file = _file;
1257       applet = _applet;
1258     }
1259
1260     public void run()
1261     {
1262       LoadJmolThread jmolchecker = new LoadJmolThread();
1263       jmolchecker.start();
1264       while (jmolchecker.notFinished())
1265       {
1266         // wait around until the Jmol check is complete.
1267         try
1268         {
1269           Thread.sleep(2);
1270         } catch (Exception e)
1271         {
1272         }
1273         ;
1274       }
1275       startLoading();
1276       // applet.callInitCallback();
1277     }
1278
1279     private void startLoading()
1280     {
1281       AlignFrame newAlignFrame;
1282       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
1283       file = setProtocolState(_file);
1284
1285       format = new jalview.io.IdentifyFile().Identify(file, protocol);
1286       dbgMsg("File identified as '" + format + "'");
1287       dbgMsg("Loading started.");
1288       Alignment al = null;
1289       try
1290       {
1291         al = new AppletFormatAdapter().readFile(file, protocol, format);
1292       } catch (java.io.IOException ex)
1293       {
1294         dbgMsg("File load exception.");
1295         ex.printStackTrace();
1296         if (debug)
1297         {
1298           try
1299           {
1300             FileParse fp = new FileParse(file, protocol);
1301             String ln = null;
1302             dbgMsg(">>>Dumping contents of '" + file + "' " + "("
1303                     + protocol + ")");
1304             while ((ln = fp.nextLine()) != null)
1305             {
1306               dbgMsg(ln);
1307             }
1308             dbgMsg(">>>Dump finished.");
1309           } catch (Exception e)
1310           {
1311             System.err
1312                     .println("Exception when trying to dump the content of the file parameter.");
1313             e.printStackTrace();
1314           }
1315         }
1316       }
1317       if ((al != null) && (al.getHeight() > 0))
1318       {
1319         dbgMsg("Successfully loaded file.");
1320         newAlignFrame = new AlignFrame(al, applet, file, embedded);
1321         if (initialAlignFrame == null)
1322         {
1323           initialAlignFrame = newAlignFrame;
1324         }
1325         // update the focus.
1326         currentAlignFrame = newAlignFrame;
1327
1328         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
1329         {
1330           newAlignFrame.setTitle("Sequences from "
1331                   + applet.getDocumentBase());
1332         }
1333
1334         newAlignFrame.statusBar.setText("Successfully loaded file " + file);
1335
1336         String treeFile = applet.getParameter("tree");
1337         if (treeFile == null)
1338         {
1339           treeFile = applet.getParameter("treeFile");
1340         }
1341
1342         if (treeFile != null)
1343         {
1344           try
1345           {
1346             treeFile = setProtocolState(treeFile);
1347             /*
1348              * if (inArchive(treeFile)) { protocol =
1349              * AppletFormatAdapter.CLASSLOADER; } else { protocol =
1350              * AppletFormatAdapter.URL; treeFile = addProtocol(treeFile); }
1351              */
1352             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
1353                     protocol);
1354
1355             fin.parse();
1356
1357             if (fin.getTree() != null)
1358             {
1359               newAlignFrame.loadTree(fin, treeFile);
1360               dbgMsg("Successfuly imported tree.");
1361             }
1362             else
1363             {
1364               dbgMsg("Tree parameter did not resolve to a valid tree.");
1365             }
1366           } catch (Exception ex)
1367           {
1368             ex.printStackTrace();
1369           }
1370         }
1371
1372         String param = applet.getParameter("features");
1373         if (param != null)
1374         {
1375           param = setProtocolState(param);
1376
1377           newAlignFrame.parseFeaturesFile(param, protocol);
1378         }
1379
1380         param = applet.getParameter("showFeatureSettings");
1381         if (param != null && param.equalsIgnoreCase("true"))
1382         {
1383           newAlignFrame.viewport.showSequenceFeatures(true);
1384           new FeatureSettings(newAlignFrame.alignPanel);
1385         }
1386
1387         param = applet.getParameter("annotations");
1388         if (param != null)
1389         {
1390           param = setProtocolState(param);
1391
1392           if (new AnnotationFile().readAnnotationFile(
1393                   newAlignFrame.viewport.getAlignment(), param, protocol))
1394           {
1395             newAlignFrame.alignPanel.fontChanged();
1396             newAlignFrame.alignPanel.setScrollValues(0, 0);
1397           }
1398           else
1399           {
1400             System.err
1401                     .println("Annotations were not added from annotation file '"
1402                             + param + "'");
1403           }
1404
1405         }
1406
1407         param = applet.getParameter("jnetfile");
1408         if (param != null)
1409         {
1410           try
1411           {
1412             param = setProtocolState(param);
1413             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
1414                     param, protocol);
1415             JnetAnnotationMaker.add_annotation(predictions,
1416                     newAlignFrame.viewport.getAlignment(), 0, false); // false==do
1417             // not
1418             // add
1419             // sequence
1420             // profile
1421             // from
1422             // concise
1423             // output
1424             newAlignFrame.alignPanel.fontChanged();
1425             newAlignFrame.alignPanel.setScrollValues(0, 0);
1426           } catch (Exception ex)
1427           {
1428             ex.printStackTrace();
1429           }
1430         }
1431         /*
1432          * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6
1433          * - related to JAL-434
1434          */
1435         applet.setAlignPdbStructures(getDefaultParameter("alignpdbfiles",
1436                 false));
1437         /*
1438          * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
1439          * PDB|1GAQ|1GAQ|C">
1440          * 
1441          * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
1442          * 
1443          * <param name="PDBfile3" value="1q0o Q45135_9MICO">
1444          */
1445
1446         int pdbFileCount = 0;
1447         // Accumulate pdbs here if they are heading for the same view (if
1448         // alignPdbStructures is true)
1449         Vector pdbs = new Vector();
1450         do
1451         {
1452           if (pdbFileCount > 0)
1453             param = applet.getParameter("PDBFILE" + pdbFileCount);
1454           else
1455             param = applet.getParameter("PDBFILE");
1456
1457           if (param != null)
1458           {
1459             PDBEntry pdb = new PDBEntry();
1460
1461             String seqstring;
1462             SequenceI[] seqs = null;
1463             String[] chains = null;
1464
1465             StringTokenizer st = new StringTokenizer(param, " ");
1466
1467             if (st.countTokens() < 2)
1468             {
1469               String sequence = applet.getParameter("PDBSEQ");
1470               if (sequence != null)
1471                 seqs = new SequenceI[]
1472                 { (Sequence) newAlignFrame.getAlignViewport()
1473                         .getAlignment().findName(sequence) };
1474
1475             }
1476             else
1477             {
1478               param = st.nextToken();
1479               Vector tmp = new Vector();
1480               Vector tmp2 = new Vector();
1481
1482               while (st.hasMoreTokens())
1483               {
1484                 seqstring = st.nextToken();
1485                 StringTokenizer st2 = new StringTokenizer(seqstring, "=");
1486                 if (st2.countTokens() > 1)
1487                 {
1488                   // This is the chain
1489                   tmp2.addElement(st2.nextToken());
1490                   seqstring = st2.nextToken();
1491                 }
1492                 tmp.addElement((Sequence) newAlignFrame.getAlignViewport()
1493                         .getAlignment().findName(seqstring));
1494               }
1495
1496               seqs = new SequenceI[tmp.size()];
1497               tmp.copyInto(seqs);
1498               if (tmp2.size() == tmp.size())
1499               {
1500                 chains = new String[tmp2.size()];
1501                 tmp2.copyInto(chains);
1502               }
1503             }
1504             param = setProtocolState(param);
1505
1506             if (// !jmolAvailable
1507             // &&
1508             protocol == AppletFormatAdapter.CLASSLOADER)
1509             {
1510               // TODO: verify this Re:
1511               // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1512               // This exception preserves the current behaviour where, even if
1513               // the local pdb file was identified in the class loader
1514               protocol = AppletFormatAdapter.URL; // this is probably NOT
1515               // CORRECT!
1516               param = addProtocol(param); //
1517             }
1518
1519             pdb.setFile(param);
1520
1521             if (seqs != null)
1522             {
1523               for (int i = 0; i < seqs.length; i++)
1524               {
1525                 if (seqs[i] != null)
1526                 {
1527                   ((Sequence) seqs[i]).addPDBId(pdb);
1528                 }
1529                 else
1530                 {
1531                   if (JalviewLite.debug)
1532                   {
1533                     // this may not really be a problem but we give a warning
1534                     // anyway
1535                     System.err
1536                             .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
1537                                     + i + ")");
1538                   }
1539                 }
1540               }
1541
1542               if (!alignPdbStructures)
1543               {
1544                 newAlignFrame.newStructureView(applet, pdb, seqs, chains,
1545                         protocol);
1546               }
1547               else
1548               {
1549                 pdbs.addElement(new Object[]
1550                 { pdb, seqs, chains, new String(protocol) });
1551               }
1552             }
1553           }
1554
1555           pdbFileCount++;
1556         } while (pdbFileCount < 10);
1557         if (pdbs.size() > 0)
1558         {
1559           SequenceI[][] seqs = new SequenceI[pdbs.size()][];
1560           PDBEntry[] pdb = new PDBEntry[pdbs.size()];
1561           String[][] chains = new String[pdbs.size()][];
1562           String[] protocols = new String[pdbs.size()];
1563           for (int pdbsi = 0, pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
1564           {
1565             Object[] o = (Object[]) pdbs.elementAt(pdbsi);
1566             pdb[pdbsi] = (PDBEntry) o[0];
1567             seqs[pdbsi] = (SequenceI[]) o[1];
1568             chains[pdbsi] = (String[]) o[2];
1569             protocols[pdbsi] = (String) o[3];
1570           }
1571           newAlignFrame.alignedStructureView(applet, pdb, seqs, chains,
1572                   protocols);
1573
1574         }
1575         // ///////////////////////////
1576         // modify display of features
1577         //
1578         // hide specific groups
1579         param = applet.getParameter("hidefeaturegroups");
1580         if (param != null)
1581         {
1582           applet.setFeatureGroupStateOn(newAlignFrame, param, false);
1583         }
1584         // show specific groups
1585         param = applet.getParameter("showfeaturegroups");
1586         if (param != null)
1587         {
1588           applet.setFeatureGroupStateOn(newAlignFrame, param, true);
1589         }
1590       }
1591       else
1592       {
1593         fileFound = false;
1594         applet.remove(launcher);
1595         applet.repaint();
1596       }
1597     }
1598
1599     /**
1600      * Discovers whether the given file is in the Applet Archive
1601      * 
1602      * @param file
1603      *          String
1604      * @return boolean
1605      */
1606     boolean inArchive(String file)
1607     {
1608       // This might throw a security exception in certain browsers
1609       // Netscape Communicator for instance.
1610       try
1611       {
1612         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
1613         if (debug)
1614         {
1615           System.err.println("Resource '" + file + "' was "
1616                   + (rtn ? "" : "not") + " located by classloader.");
1617         }
1618         return rtn;
1619       } catch (Exception ex)
1620       {
1621         System.out.println("Exception checking resources: " + file + " "
1622                 + ex);
1623         return false;
1624       }
1625     }
1626
1627     String addProtocol(String file)
1628     {
1629       if (file.indexOf("://") == -1)
1630       {
1631         file = applet.getCodeBase() + file;
1632         if (debug)
1633         {
1634           System.err.println("Prepended codebase for resource: '" + file
1635                   + "'");
1636         }
1637       }
1638
1639       return file;
1640     }
1641   }
1642
1643   /**
1644    * @return the default alignFrame acted on by the public applet methods. May
1645    *         return null with an error message on System.err indicating the
1646    *         fact.
1647    */
1648   public AlignFrame getDefaultTargetFrame()
1649   {
1650     if (currentAlignFrame != null)
1651     {
1652       return currentAlignFrame;
1653     }
1654     if (initialAlignFrame != null)
1655     {
1656       return initialAlignFrame;
1657     }
1658     System.err
1659             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
1660     return null;
1661   }
1662
1663   /**
1664    * separator used for separatorList
1665    */
1666   protected String separator = ""+((char)0x00AC); // the default used to be '|' but many sequence IDS include pipes.
1667   
1668   /**
1669    * parse the string into a list
1670    * 
1671    * @param list
1672    * @return elements separated by separator
1673    */
1674   public String[] separatorListToArray(String list)
1675   {
1676     return separatorListToArray(list, separator);
1677   }
1678
1679   /**
1680    * parse the string into a list
1681    * 
1682    * @param list
1683    * @param separator
1684    * @return elements separated by separator
1685    */
1686   public String[] separatorListToArray(String list, String separator)
1687   {
1688     // note separator local variable intentionally masks object field
1689     int seplen = separator.length();
1690     if (list == null || list.equals("") || list.equals(separator))
1691       return null;
1692     java.util.Vector jv = new Vector();
1693     int cp = 0, pos;
1694     while ((pos = list.indexOf(separator, cp)) > cp)
1695     {
1696       jv.addElement(list.substring(cp, pos));
1697       cp = pos + seplen;
1698     }
1699     if (cp < list.length())
1700     {
1701       String c = list.substring(cp);
1702       if (!c.equals(separator))
1703       {
1704         jv.addElement(c);
1705       }
1706     }
1707     if (jv.size() > 0)
1708     {
1709       String[] v = new String[jv.size()];
1710       for (int i = 0; i < v.length; i++)
1711       {
1712         v[i] = (String) jv.elementAt(i);
1713       }
1714       jv.removeAllElements();
1715       if (debug)
1716       {
1717         System.err.println("Array from '" + separator
1718                 + "' separated List:\n" + v.length);
1719         for (int i = 0; i < v.length; i++)
1720         {
1721           System.err.println("item " + i + " '" + v[i] + "'");
1722         }
1723       }
1724       return v;
1725     }
1726     if (debug)
1727     {
1728       System.err.println("Empty Array from '" + separator
1729               + "' separated List");
1730     }
1731     return null;
1732   }
1733
1734   /**
1735    * concatenate the list with separator
1736    * 
1737    * @param list
1738    * @return concatenated string
1739    */
1740   public String arrayToSeparatorList(String[] list)
1741   {
1742     return arrayToSeparatorList(list, separator);
1743   }
1744
1745   /**
1746    * concatenate the list with separator
1747    * 
1748    * @param list
1749    * @param separator
1750    * @return concatenated string
1751    */
1752   public String arrayToSeparatorList(String[] list, String separator)
1753   {
1754     StringBuffer v = new StringBuffer();
1755     if (list != null && list.length > 0)
1756     {
1757       for (int i = 0, iSize = list.length; i < iSize; i++)
1758       {
1759         if (list[i] != null)
1760         {
1761           if (i > 0)
1762           {
1763             v.append(separator);
1764           }
1765           v.append(list[i]);
1766         }
1767       }
1768       if (debug)
1769       {
1770         System.err.println("Returning '" + separator
1771                 + "' separated List:\n");
1772         System.err.println(v);
1773       }
1774       return v.toString();
1775     }
1776     if (debug)
1777     {
1778       System.err.println("Returning empty '" + separator
1779               + "' separated List\n");
1780     }
1781     return "" + separator;
1782   }
1783
1784   /**
1785    * @return
1786    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1787    */
1788   public String getFeatureGroups()
1789   {
1790     String lst = arrayToSeparatorList(getDefaultTargetFrame()
1791             .getFeatureGroups());
1792     return lst;
1793   }
1794
1795   /**
1796    * @param alf
1797    *          alignframe to get feature groups on
1798    * @return
1799    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1800    */
1801   public String getFeatureGroupsOn(AlignFrame alf)
1802   {
1803     String lst = arrayToSeparatorList(alf.getFeatureGroups());
1804     return lst;
1805   }
1806
1807   /**
1808    * @param visible
1809    * @return
1810    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1811    */
1812   public String getFeatureGroupsOfState(boolean visible)
1813   {
1814     return arrayToSeparatorList(getDefaultTargetFrame()
1815             .getFeatureGroupsOfState(visible));
1816   }
1817
1818   /**
1819    * @param alf
1820    *          align frame to get groups of state visible
1821    * @param visible
1822    * @return
1823    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1824    */
1825   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
1826   {
1827     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
1828   }
1829
1830   /**
1831    * @param groups
1832    *          tab separated list of group names
1833    * @param state
1834    *          true or false
1835    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[],
1836    *      boolean)
1837    */
1838   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
1839           boolean state)
1840   {
1841     boolean st = state;// !(state==null || state.equals("") ||
1842     // state.toLowerCase().equals("false"));
1843     alf.setFeatureGroupState(separatorListToArray(groups), st);
1844   }
1845
1846   public void setFeatureGroupState(String groups, boolean state)
1847   {
1848     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
1849   }
1850
1851   /**
1852    * List separator string
1853    * 
1854    * @return the separator
1855    */
1856   public String getSeparator()
1857   {
1858     return separator;
1859   }
1860
1861   /**
1862    * List separator string
1863    * 
1864    * @param separator
1865    *          the separator to set. empty string will reset separator to default
1866    */
1867   public void setSeparator(String separator)
1868   {
1869     if (separator==null || separator.length()<1)
1870     {
1871       // reset to default
1872       separator = ""+((char)0x00AC);
1873     }
1874     this.separator = separator;
1875     if (debug)
1876     {
1877       System.err.println("Default Separator now: '"+separator+"'");
1878     }
1879   }
1880
1881   /**
1882    * get boolean value of applet parameter 'name' and return default if
1883    * parameter is not set
1884    * 
1885    * @param name
1886    *          name of paremeter
1887    * @param def
1888    *          the value to return otherwise
1889    * @return true or false
1890    */
1891   public boolean getDefaultParameter(String name, boolean def)
1892   {
1893     String stn;
1894     if ((stn = getParameter(name)) == null)
1895     {
1896       return def;
1897     }
1898     if (stn.toLowerCase().equals("true"))
1899     {
1900       return true;
1901     }
1902     return false;
1903   }
1904
1905   /**
1906    * bind a pdb file to a sequence in the given alignFrame.
1907    * 
1908    * @param alFrame
1909    *          - null or specific alignFrame. This specifies the dataset that
1910    *          will be searched for a seuqence called sequenceId
1911    * @param sequenceId
1912    *          - sequenceId within the dataset.
1913    * @param pdbEntryString
1914    *          - the short name for the PDB file
1915    * @param pdbFile
1916    *          - pdb file - either a URL or a valid PDB file.
1917    * @return true if binding was as success TODO: consider making an exception
1918    *         structure for indicating when PDB parsing or seqeunceId location
1919    *         fails.
1920    */
1921   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
1922           String pdbEntryString, String pdbFile)
1923   {
1924     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
1925   }
1926
1927   protected void setAlignPdbStructures(boolean alignPdbStructures)
1928   {
1929     this.alignPdbStructures = alignPdbStructures;
1930   }
1931
1932   public boolean isAlignPdbStructures()
1933   {
1934     return alignPdbStructures;
1935   }
1936
1937   public void start()
1938   {
1939     callInitCallback();
1940   }
1941
1942   /**
1943    * bind structures in a viewer to any matching sequences in an alignFrame (use
1944    * sequenceIds to limit scope of search to specific sequences)
1945    * 
1946    * @param alFrame
1947    * @param viewer
1948    * @param sequenceIds
1949    * @return TODO: consider making an exception structure for indicating when
1950    *         binding fails public SequenceStructureBinding
1951    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
1952    *         sequenceIds) {
1953    * 
1954    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
1955    *         alFrame.addStructureViewInstance(viewer,
1956    *         separatorListToArray(sequenceIds)); } else { return
1957    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
1958    */
1959 }