0fb06629bedb7ce9253fc4f6d9b3fdf062775cfc
[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), null);
181       }
182       else
183       {
184         StructureSelectionManager.getStructureSelectionManager()
185                 .mouseOverVamsasSequence(sq, apos, null);
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 (debug)
846       {
847         System.err.println("Applet context is '"+getAppletContext().getClass().toString()+"'");
848       }
849       JSObject scriptObject = JSObject.getWindow(this);
850       if (debug && scriptObject!=null)
851       {
852         System.err.println("Applet has Javascript callback support.");
853       }
854       
855     } catch (Exception ex)
856     {
857       System.err
858       .println("Warning: No JalviewLite javascript callbacks available.");
859       if (debug)
860       {
861         ex.printStackTrace();
862       }
863     }
864     /**
865      * turn on extra applet debugging
866      */
867     String dbg = getParameter("debug");
868     if (dbg != null)
869     {
870       debug = dbg.toLowerCase().equals("true");
871     }
872     if (debug)
873     {
874
875       System.err.println("JalviewLite Version " + getVersion());
876       System.err.println("Build Date : " + getBuildDate());
877
878     }
879     /**
880      * if true disable the check for jmol
881      */
882     String chkforJmol = getParameter("nojmol");
883     if (chkforJmol != null)
884     {
885       checkForJmol = !chkforJmol.equals("true");
886     }
887     /**
888      * get the separator parameter if present
889      */
890     String sep = getParameter("separator");
891     if (sep != null)
892     {
893       if (sep.length() > 0)
894       {
895         separator = sep;
896         if (debug)
897         {
898           System.err.println("Separator set to '" + separator + "'");
899         }
900       }
901       else
902       {
903         throw new Error(
904                 "Invalid separator parameter - must be non-zero length");
905       }
906     }
907     int r = 255;
908     int g = 255;
909     int b = 255;
910     String param = getParameter("RGB");
911
912     if (param != null)
913     {
914       try
915       {
916         r = Integer.parseInt(param.substring(0, 2), 16);
917         g = Integer.parseInt(param.substring(2, 4), 16);
918         b = Integer.parseInt(param.substring(4, 6), 16);
919       } catch (Exception ex)
920       {
921         r = 255;
922         g = 255;
923         b = 255;
924       }
925     }
926     param = getParameter("label");
927     if (param != null)
928     {
929       launcher.setLabel(param);
930     }
931
932     setBackground(new Color(r, g, b));
933     
934     file = getParameter("file");
935
936     if (file == null)
937     {
938       // Maybe the sequences are added as parameters
939       StringBuffer data = new StringBuffer("PASTE");
940       int i = 1;
941       while ((file = getParameter("sequence" + i)) != null)
942       {
943         data.append(file.toString() + "\n");
944         i++;
945       }
946       if (data.length() > 5)
947       {
948         file = data.toString();
949       }
950     }
951
952     final JalviewLite jvapplet = this;
953     if (getParameter("embedded") != null
954             && getParameter("embedded").equalsIgnoreCase("true"))
955     {
956       // Launch as embedded applet in page
957       embedded = true;
958       LoadingThread loader = new LoadingThread(file, jvapplet);
959       loader.start();
960     }
961     else if (file != null)
962     {
963       if (getParameter("showbutton") == null
964               || !getParameter("showbutton").equalsIgnoreCase(
965                       "false"))
966       {
967         // Add the JalviewLite 'Button' to the page
968         add(launcher);
969         launcher.addActionListener(new java.awt.event.ActionListener()
970         {
971           public void actionPerformed(ActionEvent e)
972           {
973             LoadingThread loader = new LoadingThread(file, jvapplet);
974             loader.start();
975           }
976         });
977       }
978       else
979       {
980         // Open jalviewLite immediately.
981         LoadingThread loader = new LoadingThread(file, jvapplet);
982         loader.start();
983       }
984     }
985     else
986     {
987       // jalview initialisation with no alignment. loadAlignment() method can
988       // still be called to open new alignments.
989       file = "NO FILE";
990       fileFound = false;
991       // callInitCallback();
992     }
993   }
994
995   private void callInitCallback()
996   {
997     String initjscallback = getParameter("oninit");
998     if (initjscallback == null)
999     {
1000       return;
1001     }
1002     initjscallback = initjscallback.trim();
1003     if (initjscallback.length() > 0)
1004     {
1005       JSObject scriptObject = null; 
1006       try {
1007         scriptObject = JSObject.getWindow(this);
1008       } catch (Exception ex) {};
1009       if (scriptObject != null)
1010       {
1011         try
1012         {
1013           // do onInit with the JS executor thread
1014           new JSFunctionExec(this).executeJavascriptFunction(true,
1015                   initjscallback, null, "Calling oninit callback '" + initjscallback
1016                   + "'.");
1017         } catch (Exception e)
1018         {
1019           System.err.println("Exception when executing _oninit callback '"
1020                   + initjscallback + "'.");
1021           e.printStackTrace();
1022         }
1023       }
1024       else
1025       {
1026         System.err.println("Not executing _oninit callback '"
1027                 + initjscallback + "' - no scripting allowed.");
1028       }
1029     }
1030   }
1031
1032   /**
1033    * Initialises and displays a new java.awt.Frame
1034    * 
1035    * @param frame
1036    *          java.awt.Frame to be displayed
1037    * @param title
1038    *          title of new frame
1039    * @param width
1040    *          width if new frame
1041    * @param height
1042    *          height of new frame
1043    */
1044   public static void addFrame(final Frame frame, String title, int width,
1045           int height)
1046   {
1047     frame.setLocation(lastFrameX, lastFrameY);
1048     lastFrameX += 40;
1049     lastFrameY += 40;
1050     frame.setSize(width, height);
1051     frame.setTitle(title);
1052     frame.addWindowListener(new WindowAdapter()
1053     {
1054       public void windowClosing(WindowEvent e)
1055       {
1056         if (frame instanceof AlignFrame)
1057         {
1058           ((AlignFrame) frame).closeMenuItem_actionPerformed();
1059           if (((AlignFrame) frame).viewport.applet.currentAlignFrame == frame)
1060           {
1061             ((AlignFrame) frame).viewport.applet.currentAlignFrame = null;
1062           }
1063         }
1064         lastFrameX -= 40;
1065         lastFrameY -= 40;
1066         if (frame instanceof EmbmenuFrame)
1067         {
1068           ((EmbmenuFrame) frame).destroyMenus();
1069         }
1070         frame.setMenuBar(null);
1071         frame.dispose();
1072       }
1073
1074       public void windowActivated(WindowEvent e)
1075       {
1076         if (frame instanceof AlignFrame)
1077         {
1078           ((AlignFrame) frame).viewport.applet.currentAlignFrame = (AlignFrame) frame;
1079           if (debug)
1080           {
1081             System.err.println("Activated window " + frame);
1082           }
1083         }
1084         // be good.
1085         super.windowActivated(e);
1086       }
1087       /*
1088        * Probably not necessary to do this - see TODO above. (non-Javadoc)
1089        * 
1090        * @see
1091        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
1092        * )
1093        * 
1094        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
1095        * frame) { currentAlignFrame = null; if (debug) {
1096        * System.err.println("Deactivated window "+frame); } }
1097        * super.windowDeactivated(e); }
1098        */
1099     });
1100     frame.setVisible(true);
1101   }
1102
1103   /**
1104    * This paints the background surrounding the "Launch Jalview button" <br>
1105    * <br>
1106    * If file given in parameter not found, displays error message
1107    * 
1108    * @param g
1109    *          graphics context
1110    */
1111   public void paint(Graphics g)
1112   {
1113     if (!fileFound)
1114     {
1115       g.setColor(new Color(200, 200, 200));
1116       g.setColor(Color.cyan);
1117       g.fillRect(0, 0, getSize().width, getSize().height);
1118       g.setColor(Color.red);
1119       g.drawString("Jalview can't open file", 5, 15);
1120       g.drawString("\"" + file + "\"", 5, 30);
1121     }
1122     else if (embedded)
1123     {
1124       g.setColor(Color.black);
1125       g.setFont(new Font("Arial", Font.BOLD, 24));
1126       g.drawString("Jalview Applet", 50, getSize().height / 2 - 30);
1127       g.drawString("Loading Data...", 50, getSize().height / 2);
1128     }
1129   }
1130   /**
1131    * get all components associated with the applet of the given type
1132    * 
1133    * @param class1
1134    * @return
1135    */
1136   public Vector getAppletWindow(Class class1)
1137   {
1138     Vector wnds = new Vector();
1139     Component[] cmp = getComponents();
1140     if (cmp != null)
1141     {
1142       for (int i = 0; i < cmp.length; i++)
1143       {
1144         if (class1.isAssignableFrom(cmp[i].getClass()))
1145         {
1146           wnds.addElement(cmp);
1147         }
1148       }
1149     }
1150     return wnds;
1151   }
1152
1153   class LoadJmolThread extends Thread
1154   {
1155     private boolean running = false;
1156
1157     public void run()
1158     {
1159       if (running || checkedForJmol)
1160       {
1161         return;
1162       }
1163       running = true;
1164       if (checkForJmol)
1165       {
1166         try
1167         {
1168           if (!System.getProperty("java.version").startsWith("1.1"))
1169           {
1170             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
1171             jmolAvailable = true;
1172           }
1173           if (!jmolAvailable)
1174           {
1175             System.out
1176                     .println("Jmol not available - Using MCview for structures");
1177           }
1178         } catch (java.lang.ClassNotFoundException ex)
1179         {
1180         }
1181       }
1182       else
1183       {
1184         jmolAvailable = false;
1185         if (debug)
1186         {
1187           System.err
1188                   .println("Skipping Jmol check. Will use MCView (probably)");
1189         }
1190       }
1191       checkedForJmol = true;
1192       running = false;
1193     }
1194
1195     public boolean notFinished()
1196     {
1197       return running || !checkedForJmol;
1198     }
1199   }
1200
1201   class LoadingThread extends Thread
1202   {
1203     /**
1204      * State variable: File source
1205      */
1206     String file;
1207
1208     /**
1209      * State variable: protocol for access to file source
1210      */
1211     String protocol;
1212
1213     /**
1214      * State variable: format of file source
1215      */
1216     String format;
1217
1218     String _file;
1219
1220     JalviewLite applet;
1221
1222     private void dbgMsg(String msg)
1223     {
1224       if (applet.debug)
1225       {
1226         System.err.println(msg);
1227       }
1228     }
1229
1230     /**
1231      * update the protocol state variable for accessing the datasource located
1232      * by file.
1233      * 
1234      * @param file
1235      * @return possibly updated datasource string
1236      */
1237     public String setProtocolState(String file)
1238     {
1239       if (file.startsWith("PASTE"))
1240       {
1241         file = file.substring(5);
1242         protocol = AppletFormatAdapter.PASTE;
1243       }
1244       else if (inArchive(file))
1245       {
1246         protocol = AppletFormatAdapter.CLASSLOADER;
1247       }
1248       else
1249       {
1250         file = addProtocol(file);
1251         protocol = AppletFormatAdapter.URL;
1252       }
1253       dbgMsg("Protocol identified as '" + protocol + "'");
1254       return file;
1255     }
1256
1257     public LoadingThread(String _file, JalviewLite _applet)
1258     {
1259       this._file = _file;
1260       applet = _applet;
1261     }
1262
1263     public void run()
1264     {
1265       LoadJmolThread jmolchecker = new LoadJmolThread();
1266       jmolchecker.start();
1267       while (jmolchecker.notFinished())
1268       {
1269         // wait around until the Jmol check is complete.
1270         try
1271         {
1272           Thread.sleep(2);
1273         } catch (Exception e)
1274         {
1275         }
1276         ;
1277       }
1278       startLoading();
1279       // applet.callInitCallback();
1280     }
1281
1282     private void startLoading()
1283     {
1284       AlignFrame newAlignFrame;
1285       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
1286       file = setProtocolState(_file);
1287
1288       format = new jalview.io.IdentifyFile().Identify(file, protocol);
1289       dbgMsg("File identified as '" + format + "'");
1290       dbgMsg("Loading started.");
1291       Alignment al = null;
1292       try
1293       {
1294         al = new AppletFormatAdapter().readFile(file, protocol, format);
1295       } catch (java.io.IOException ex)
1296       {
1297         dbgMsg("File load exception.");
1298         ex.printStackTrace();
1299         if (debug)
1300         {
1301           try
1302           {
1303             FileParse fp = new FileParse(file, protocol);
1304             String ln = null;
1305             dbgMsg(">>>Dumping contents of '" + file + "' " + "("
1306                     + protocol + ")");
1307             while ((ln = fp.nextLine()) != null)
1308             {
1309               dbgMsg(ln);
1310             }
1311             dbgMsg(">>>Dump finished.");
1312           } catch (Exception e)
1313           {
1314             System.err
1315                     .println("Exception when trying to dump the content of the file parameter.");
1316             e.printStackTrace();
1317           }
1318         }
1319       }
1320       if ((al != null) && (al.getHeight() > 0))
1321       {
1322         dbgMsg("Successfully loaded file.");
1323         newAlignFrame = new AlignFrame(al, applet, file, embedded);
1324         if (initialAlignFrame == null)
1325         {
1326           initialAlignFrame = newAlignFrame;
1327         }
1328         // update the focus.
1329         currentAlignFrame = newAlignFrame;
1330
1331         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
1332         {
1333           newAlignFrame.setTitle("Sequences from "
1334                   + applet.getDocumentBase());
1335         }
1336
1337         newAlignFrame.statusBar.setText("Successfully loaded file " + file);
1338
1339         String treeFile = applet.getParameter("tree");
1340         if (treeFile == null)
1341         {
1342           treeFile = applet.getParameter("treeFile");
1343         }
1344
1345         if (treeFile != null)
1346         {
1347           try
1348           {
1349             treeFile = setProtocolState(treeFile);
1350             /*
1351              * if (inArchive(treeFile)) { protocol =
1352              * AppletFormatAdapter.CLASSLOADER; } else { protocol =
1353              * AppletFormatAdapter.URL; treeFile = addProtocol(treeFile); }
1354              */
1355             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
1356                     protocol);
1357
1358             fin.parse();
1359
1360             if (fin.getTree() != null)
1361             {
1362               newAlignFrame.loadTree(fin, treeFile);
1363               dbgMsg("Successfuly imported tree.");
1364             }
1365             else
1366             {
1367               dbgMsg("Tree parameter did not resolve to a valid tree.");
1368             }
1369           } catch (Exception ex)
1370           {
1371             ex.printStackTrace();
1372           }
1373         }
1374
1375         String param = applet.getParameter("features");
1376         if (param != null)
1377         {
1378           param = setProtocolState(param);
1379
1380           newAlignFrame.parseFeaturesFile(param, protocol);
1381         }
1382
1383         param = applet.getParameter("showFeatureSettings");
1384         if (param != null && param.equalsIgnoreCase("true"))
1385         {
1386           newAlignFrame.viewport.showSequenceFeatures(true);
1387           new FeatureSettings(newAlignFrame.alignPanel);
1388         }
1389
1390         param = applet.getParameter("annotations");
1391         if (param != null)
1392         {
1393           param = setProtocolState(param);
1394
1395           if (new AnnotationFile().readAnnotationFile(
1396                   newAlignFrame.viewport.getAlignment(), param, protocol))
1397           {
1398             newAlignFrame.alignPanel.fontChanged();
1399             newAlignFrame.alignPanel.setScrollValues(0, 0);
1400           }
1401           else
1402           {
1403             System.err
1404                     .println("Annotations were not added from annotation file '"
1405                             + param + "'");
1406           }
1407
1408         }
1409
1410         param = applet.getParameter("jnetfile");
1411         if (param != null)
1412         {
1413           try
1414           {
1415             param = setProtocolState(param);
1416             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
1417                     param, protocol);
1418             JnetAnnotationMaker.add_annotation(predictions,
1419                     newAlignFrame.viewport.getAlignment(), 0, false); // false==do
1420             // not
1421             // add
1422             // sequence
1423             // profile
1424             // from
1425             // concise
1426             // output
1427             newAlignFrame.alignPanel.fontChanged();
1428             newAlignFrame.alignPanel.setScrollValues(0, 0);
1429           } catch (Exception ex)
1430           {
1431             ex.printStackTrace();
1432           }
1433         }
1434         /*
1435          * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6
1436          * - related to JAL-434
1437          */
1438         applet.setAlignPdbStructures(getDefaultParameter("alignpdbfiles",
1439                 false));
1440         /*
1441          * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
1442          * PDB|1GAQ|1GAQ|C">
1443          * 
1444          * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
1445          * 
1446          * <param name="PDBfile3" value="1q0o Q45135_9MICO">
1447          */
1448
1449         int pdbFileCount = 0;
1450         // Accumulate pdbs here if they are heading for the same view (if
1451         // alignPdbStructures is true)
1452         Vector pdbs = new Vector();
1453         do
1454         {
1455           if (pdbFileCount > 0)
1456             param = applet.getParameter("PDBFILE" + pdbFileCount);
1457           else
1458             param = applet.getParameter("PDBFILE");
1459
1460           if (param != null)
1461           {
1462             PDBEntry pdb = new PDBEntry();
1463
1464             String seqstring;
1465             SequenceI[] seqs = null;
1466             String[] chains = null;
1467
1468             StringTokenizer st = new StringTokenizer(param, " ");
1469
1470             if (st.countTokens() < 2)
1471             {
1472               String sequence = applet.getParameter("PDBSEQ");
1473               if (sequence != null)
1474                 seqs = new SequenceI[]
1475                 { (Sequence) newAlignFrame.getAlignViewport()
1476                         .getAlignment().findName(sequence) };
1477
1478             }
1479             else
1480             {
1481               param = st.nextToken();
1482               Vector tmp = new Vector();
1483               Vector tmp2 = new Vector();
1484
1485               while (st.hasMoreTokens())
1486               {
1487                 seqstring = st.nextToken();
1488                 StringTokenizer st2 = new StringTokenizer(seqstring, "=");
1489                 if (st2.countTokens() > 1)
1490                 {
1491                   // This is the chain
1492                   tmp2.addElement(st2.nextToken());
1493                   seqstring = st2.nextToken();
1494                 }
1495                 tmp.addElement((Sequence) newAlignFrame.getAlignViewport()
1496                         .getAlignment().findName(seqstring));
1497               }
1498
1499               seqs = new SequenceI[tmp.size()];
1500               tmp.copyInto(seqs);
1501               if (tmp2.size() == tmp.size())
1502               {
1503                 chains = new String[tmp2.size()];
1504                 tmp2.copyInto(chains);
1505               }
1506             }
1507             param = setProtocolState(param);
1508
1509             if (// !jmolAvailable
1510             // &&
1511             protocol == AppletFormatAdapter.CLASSLOADER)
1512             {
1513               // TODO: verify this Re:
1514               // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1515               // This exception preserves the current behaviour where, even if
1516               // the local pdb file was identified in the class loader
1517               protocol = AppletFormatAdapter.URL; // this is probably NOT
1518               // CORRECT!
1519               param = addProtocol(param); //
1520             }
1521
1522             pdb.setFile(param);
1523
1524             if (seqs != null)
1525             {
1526               for (int i = 0; i < seqs.length; i++)
1527               {
1528                 if (seqs[i] != null)
1529                 {
1530                   ((Sequence) seqs[i]).addPDBId(pdb);
1531                 }
1532                 else
1533                 {
1534                   if (JalviewLite.debug)
1535                   {
1536                     // this may not really be a problem but we give a warning
1537                     // anyway
1538                     System.err
1539                             .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
1540                                     + i + ")");
1541                   }
1542                 }
1543               }
1544
1545               if (!alignPdbStructures)
1546               {
1547                 newAlignFrame.newStructureView(applet, pdb, seqs, chains,
1548                         protocol);
1549               }
1550               else
1551               {
1552                 pdbs.addElement(new Object[]
1553                 { pdb, seqs, chains, new String(protocol) });
1554               }
1555             }
1556           }
1557
1558           pdbFileCount++;
1559         } while (pdbFileCount < 10);
1560         if (pdbs.size() > 0)
1561         {
1562           SequenceI[][] seqs = new SequenceI[pdbs.size()][];
1563           PDBEntry[] pdb = new PDBEntry[pdbs.size()];
1564           String[][] chains = new String[pdbs.size()][];
1565           String[] protocols = new String[pdbs.size()];
1566           for (int pdbsi = 0, pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
1567           {
1568             Object[] o = (Object[]) pdbs.elementAt(pdbsi);
1569             pdb[pdbsi] = (PDBEntry) o[0];
1570             seqs[pdbsi] = (SequenceI[]) o[1];
1571             chains[pdbsi] = (String[]) o[2];
1572             protocols[pdbsi] = (String) o[3];
1573           }
1574           newAlignFrame.alignedStructureView(applet, pdb, seqs, chains,
1575                   protocols);
1576
1577         }
1578         // ///////////////////////////
1579         // modify display of features
1580         //
1581         // hide specific groups
1582         param = applet.getParameter("hidefeaturegroups");
1583         if (param != null)
1584         {
1585           applet.setFeatureGroupStateOn(newAlignFrame, param, false);
1586         }
1587         // show specific groups
1588         param = applet.getParameter("showfeaturegroups");
1589         if (param != null)
1590         {
1591           applet.setFeatureGroupStateOn(newAlignFrame, param, true);
1592         }
1593       }
1594       else
1595       {
1596         fileFound = false;
1597         applet.remove(launcher);
1598         applet.repaint();
1599       }
1600     }
1601
1602     /**
1603      * Discovers whether the given file is in the Applet Archive
1604      * 
1605      * @param file
1606      *          String
1607      * @return boolean
1608      */
1609     boolean inArchive(String file)
1610     {
1611       // This might throw a security exception in certain browsers
1612       // Netscape Communicator for instance.
1613       try
1614       {
1615         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
1616         if (debug)
1617         {
1618           System.err.println("Resource '" + file + "' was "
1619                   + (rtn ? "" : "not") + " located by classloader.");
1620         }
1621         return rtn;
1622       } catch (Exception ex)
1623       {
1624         System.out.println("Exception checking resources: " + file + " "
1625                 + ex);
1626         return false;
1627       }
1628     }
1629
1630     String addProtocol(String file)
1631     {
1632       if (file.indexOf("://") == -1)
1633       {
1634         file = applet.getCodeBase() + file;
1635         if (debug)
1636         {
1637           System.err.println("Prepended codebase for resource: '" + file
1638                   + "'");
1639         }
1640       }
1641
1642       return file;
1643     }
1644   }
1645
1646   /**
1647    * @return the default alignFrame acted on by the public applet methods. May
1648    *         return null with an error message on System.err indicating the
1649    *         fact.
1650    */
1651   public AlignFrame getDefaultTargetFrame()
1652   {
1653     if (currentAlignFrame != null)
1654     {
1655       return currentAlignFrame;
1656     }
1657     if (initialAlignFrame != null)
1658     {
1659       return initialAlignFrame;
1660     }
1661     System.err
1662             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
1663     return null;
1664   }
1665
1666   /**
1667    * separator used for separatorList
1668    */
1669   protected String separator = ""+((char)0x00AC); // the default used to be '|' but many sequence IDS include pipes.
1670   
1671   /**
1672    * parse the string into a list
1673    * 
1674    * @param list
1675    * @return elements separated by separator
1676    */
1677   public String[] separatorListToArray(String list)
1678   {
1679     return separatorListToArray(list, separator);
1680   }
1681
1682   /**
1683    * parse the string into a list
1684    * 
1685    * @param list
1686    * @param separator
1687    * @return elements separated by separator
1688    */
1689   public String[] separatorListToArray(String list, String separator)
1690   {
1691     // note separator local variable intentionally masks object field
1692     int seplen = separator.length();
1693     if (list == null || list.equals("") || list.equals(separator))
1694       return null;
1695     java.util.Vector jv = new Vector();
1696     int cp = 0, pos;
1697     while ((pos = list.indexOf(separator, cp)) > cp)
1698     {
1699       jv.addElement(list.substring(cp, pos));
1700       cp = pos + seplen;
1701     }
1702     if (cp < list.length())
1703     {
1704       String c = list.substring(cp);
1705       if (!c.equals(separator))
1706       {
1707         jv.addElement(c);
1708       }
1709     }
1710     if (jv.size() > 0)
1711     {
1712       String[] v = new String[jv.size()];
1713       for (int i = 0; i < v.length; i++)
1714       {
1715         v[i] = (String) jv.elementAt(i);
1716       }
1717       jv.removeAllElements();
1718       if (debug)
1719       {
1720         System.err.println("Array from '" + separator
1721                 + "' separated List:\n" + v.length);
1722         for (int i = 0; i < v.length; i++)
1723         {
1724           System.err.println("item " + i + " '" + v[i] + "'");
1725         }
1726       }
1727       return v;
1728     }
1729     if (debug)
1730     {
1731       System.err.println("Empty Array from '" + separator
1732               + "' separated List");
1733     }
1734     return null;
1735   }
1736
1737   /**
1738    * concatenate the list with separator
1739    * 
1740    * @param list
1741    * @return concatenated string
1742    */
1743   public String arrayToSeparatorList(String[] list)
1744   {
1745     return arrayToSeparatorList(list, separator);
1746   }
1747
1748   /**
1749    * concatenate the list with separator
1750    * 
1751    * @param list
1752    * @param separator
1753    * @return concatenated string
1754    */
1755   public String arrayToSeparatorList(String[] list, String separator)
1756   {
1757     StringBuffer v = new StringBuffer();
1758     if (list != null && list.length > 0)
1759     {
1760       for (int i = 0, iSize = list.length; i < iSize; i++)
1761       {
1762         if (list[i] != null)
1763         {
1764           if (i > 0)
1765           {
1766             v.append(separator);
1767           }
1768           v.append(list[i]);
1769         }
1770       }
1771       if (debug)
1772       {
1773         System.err.println("Returning '" + separator
1774                 + "' separated List:\n");
1775         System.err.println(v);
1776       }
1777       return v.toString();
1778     }
1779     if (debug)
1780     {
1781       System.err.println("Returning empty '" + separator
1782               + "' separated List\n");
1783     }
1784     return "" + separator;
1785   }
1786
1787   /**
1788    * @return
1789    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1790    */
1791   public String getFeatureGroups()
1792   {
1793     String lst = arrayToSeparatorList(getDefaultTargetFrame()
1794             .getFeatureGroups());
1795     return lst;
1796   }
1797
1798   /**
1799    * @param alf
1800    *          alignframe to get feature groups on
1801    * @return
1802    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1803    */
1804   public String getFeatureGroupsOn(AlignFrame alf)
1805   {
1806     String lst = arrayToSeparatorList(alf.getFeatureGroups());
1807     return lst;
1808   }
1809
1810   /**
1811    * @param visible
1812    * @return
1813    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1814    */
1815   public String getFeatureGroupsOfState(boolean visible)
1816   {
1817     return arrayToSeparatorList(getDefaultTargetFrame()
1818             .getFeatureGroupsOfState(visible));
1819   }
1820
1821   /**
1822    * @param alf
1823    *          align frame to get groups of state visible
1824    * @param visible
1825    * @return
1826    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1827    */
1828   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
1829   {
1830     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
1831   }
1832
1833   /**
1834    * @param groups
1835    *          tab separated list of group names
1836    * @param state
1837    *          true or false
1838    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[],
1839    *      boolean)
1840    */
1841   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
1842           boolean state)
1843   {
1844     boolean st = state;// !(state==null || state.equals("") ||
1845     // state.toLowerCase().equals("false"));
1846     alf.setFeatureGroupState(separatorListToArray(groups), st);
1847   }
1848
1849   public void setFeatureGroupState(String groups, boolean state)
1850   {
1851     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
1852   }
1853
1854   /**
1855    * List separator string
1856    * 
1857    * @return the separator
1858    */
1859   public String getSeparator()
1860   {
1861     return separator;
1862   }
1863
1864   /**
1865    * List separator string
1866    * 
1867    * @param separator
1868    *          the separator to set. empty string will reset separator to default
1869    */
1870   public void setSeparator(String separator)
1871   {
1872     if (separator==null || separator.length()<1)
1873     {
1874       // reset to default
1875       separator = ""+((char)0x00AC);
1876     }
1877     this.separator = separator;
1878     if (debug)
1879     {
1880       System.err.println("Default Separator now: '"+separator+"'");
1881     }
1882   }
1883
1884   /**
1885    * get boolean value of applet parameter 'name' and return default if
1886    * parameter is not set
1887    * 
1888    * @param name
1889    *          name of paremeter
1890    * @param def
1891    *          the value to return otherwise
1892    * @return true or false
1893    */
1894   public boolean getDefaultParameter(String name, boolean def)
1895   {
1896     String stn;
1897     if ((stn = getParameter(name)) == null)
1898     {
1899       return def;
1900     }
1901     if (stn.toLowerCase().equals("true"))
1902     {
1903       return true;
1904     }
1905     return false;
1906   }
1907
1908   /**
1909    * bind a pdb file to a sequence in the given alignFrame.
1910    * 
1911    * @param alFrame
1912    *          - null or specific alignFrame. This specifies the dataset that
1913    *          will be searched for a seuqence called sequenceId
1914    * @param sequenceId
1915    *          - sequenceId within the dataset.
1916    * @param pdbEntryString
1917    *          - the short name for the PDB file
1918    * @param pdbFile
1919    *          - pdb file - either a URL or a valid PDB file.
1920    * @return true if binding was as success TODO: consider making an exception
1921    *         structure for indicating when PDB parsing or seqeunceId location
1922    *         fails.
1923    */
1924   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
1925           String pdbEntryString, String pdbFile)
1926   {
1927     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
1928   }
1929
1930   protected void setAlignPdbStructures(boolean alignPdbStructures)
1931   {
1932     this.alignPdbStructures = alignPdbStructures;
1933   }
1934
1935   public boolean isAlignPdbStructures()
1936   {
1937     return alignPdbStructures;
1938   }
1939
1940   public void start()
1941   {
1942     callInitCallback();
1943   }
1944
1945   /**
1946    * bind structures in a viewer to any matching sequences in an alignFrame (use
1947    * sequenceIds to limit scope of search to specific sequences)
1948    * 
1949    * @param alFrame
1950    * @param viewer
1951    * @param sequenceIds
1952    * @return TODO: consider making an exception structure for indicating when
1953    *         binding fails public SequenceStructureBinding
1954    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
1955    *         sequenceIds) {
1956    * 
1957    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
1958    *         alFrame.addStructureViewInstance(viewer,
1959    *         separatorListToArray(sequenceIds)); } else { return
1960    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
1961    */
1962 }