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