merge from 2_4_Release branch
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.bin;
20
21 import java.applet.*;
22
23 import java.awt.*;
24 import java.awt.event.*;
25 import java.util.*;
26
27 import jalview.appletgui.*;
28 import jalview.datamodel.*;
29 import jalview.io.*;
30
31 /**
32  * Jalview Applet. Runs in Java 1.18 runtime
33  * 
34  * @author $author$
35  * @version $Revision$
36  */
37 public class JalviewLite extends Applet
38 {
39
40   // /////////////////////////////////////////
41   // The following public methods maybe called
42   // externally, eg via javascript in HTML page
43   /**
44    * @return String list of selected sequence IDs, each terminated by "¬"
45    *         (¬)
46    */
47   public String getSelectedSequences()
48   {
49     return getSelectedSequencesFrom(getDefaultTargetFrame());
50   }
51
52   /**
53    * @param sep
54    *                separator string or null for default
55    * @return String list of selected sequence IDs, each terminated by sep or
56    *         ("¬" as default)
57    */
58   public String getSelectedSequences(String sep)
59   {
60     return getSelectedSequencesFrom(getDefaultTargetFrame(), sep);
61   }
62
63   /**
64    * @param alf
65    *                alignframe containing selection
66    * @return String list of selected sequence IDs, each terminated by "¬"
67    * 
68    */
69   public String getSelectedSequencesFrom(AlignFrame alf)
70   {
71     return getSelectedSequencesFrom(alf, "¬");
72   }
73
74   /**
75    * get list of selected sequence IDs separated by given separator
76    * 
77    * @param alf
78    *                window containing selection
79    * @param sep
80    *                separator string to use - default is "¬"
81    * @return String list of selected sequence IDs, each terminated by the given
82    *         separator
83    */
84   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
85   {
86     StringBuffer result = new StringBuffer("");
87     if (sep == null || sep.length() == 0)
88     {
89       sep = "¬";
90     }
91     if (alf.viewport.getSelectionGroup() != null)
92     {
93       SequenceI[] seqs = alf.viewport.getSelectionGroup()
94               .getSequencesInOrder(alf.viewport.getAlignment());
95
96       for (int i = 0; i < seqs.length; i++)
97       {
98         result.append(seqs[i].getName());
99         result.append(sep);
100       }
101     }
102
103     return result.toString();
104   }
105
106   /**
107    * get sequences selected in current alignFrame and return their alignment in
108    * format 'format' either with or without suffix
109    * 
110    * @param alf -
111    *                where selection is
112    * @param format -
113    *                format of alignment file
114    * @param suffix -
115    *                "true" to append /start-end string to each sequence ID
116    * @return selected sequences as flat file or empty string if there was no
117    *         current selection
118    */
119   public String getSelectedSequencesAsAlignment(String format, String suffix)
120   {
121     return getSelectedSequencesAsAlignmentFrom(currentAlignFrame, format,
122             suffix);
123   }
124
125   /**
126    * get sequences selected in alf and return their alignment in format 'format'
127    * either with or without suffix
128    * 
129    * @param alf -
130    *                where selection is
131    * @param format -
132    *                format of alignment file
133    * @param suffix -
134    *                "true" to append /start-end string to each sequence ID
135    * @return selected sequences as flat file or empty string if there was no
136    *         current selection
137    */
138   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
139           String format, String suffix)
140   {
141     try
142     {
143       boolean seqlimits = suffix.equalsIgnoreCase("true");
144       if (alf.viewport.getSelectionGroup() != null)
145       {
146         String reply = new AppletFormatAdapter().formatSequences(format,
147                 new Alignment(alf.viewport.getSelectionAsNewSequence()),
148                 seqlimits);
149         return reply;
150       }
151     } catch (Exception ex)
152     {
153       ex.printStackTrace();
154       return "Error retrieving alignment in " + format + " format. ";
155     }
156     return "";
157   }
158
159   public String getAlignment(String format)
160   {
161     return getAlignmentFrom(getDefaultTargetFrame(), format, "true");
162   }
163
164   public String getAlignmentFrom(AlignFrame alf, String format)
165   {
166     return getAlignmentFrom(alf, format, "true");
167   }
168
169   public String getAlignment(String format, String suffix)
170   {
171     return getAlignmentFrom(getDefaultTargetFrame(), format, suffix);
172   }
173
174   public String getAlignmentFrom(AlignFrame alf, String format,
175           String suffix)
176   {
177     try
178     {
179       boolean seqlimits = suffix.equalsIgnoreCase("true");
180
181       String reply = new AppletFormatAdapter().formatSequences(format,
182               alf.viewport.getAlignment(), seqlimits);
183       return reply;
184     } catch (Exception ex)
185     {
186       ex.printStackTrace();
187       return "Error retrieving alignment in " + format + " format. ";
188     }
189   }
190
191   public void loadAnnotation(String annotation)
192   {
193     loadAnnotationFrom(getDefaultTargetFrame(), annotation);
194   }
195
196   public void loadAnnotationFrom(AlignFrame alf, String annotation)
197   {
198     if (new AnnotationFile().readAnnotationFile(alf.getAlignViewport()
199             .getAlignment(), annotation, AppletFormatAdapter.PASTE))
200     {
201       alf.alignPanel.fontChanged();
202       alf.alignPanel.setScrollValues(0, 0);
203     }
204     else
205     {
206       alf.parseFeaturesFile(annotation, AppletFormatAdapter.PASTE);
207     }
208   }
209
210   public String getFeatures(String format)
211   {
212     return getFeaturesFrom(getDefaultTargetFrame(), format);
213   }
214
215   public String getFeaturesFrom(AlignFrame alf, String format)
216   {
217     return alf.outputFeatures(false, format);
218   }
219
220   public String getAnnotation()
221   {
222     return getAnnotationFrom(getDefaultTargetFrame());
223   }
224
225   public String getAnnotationFrom(AlignFrame alf)
226   {
227     return alf.outputAnnotations(false);
228   }
229
230   public AlignFrame newView()
231   {
232     return newViewFrom(getDefaultTargetFrame());
233   }
234
235   public AlignFrame newView(String name)
236   {
237     return newViewFrom(getDefaultTargetFrame(), name);
238   }
239
240   public AlignFrame newViewFrom(AlignFrame alf)
241   {
242     return alf.newView(null);
243   }
244
245   public AlignFrame newViewFrom(AlignFrame alf, String name)
246   {
247     return alf.newView(name);
248   }
249
250   /**
251    * 
252    * @param text
253    *                alignment file as a string
254    * @param title
255    *                window title
256    * @return null or new alignment frame
257    */
258   public AlignFrame loadAlignment(String text, String title)
259   {
260     Alignment al = null;
261     String format = new IdentifyFile().Identify(text,
262             AppletFormatAdapter.PASTE);
263     try
264     {
265       al = new AppletFormatAdapter().readFile(text,
266               AppletFormatAdapter.PASTE, format);
267       if (al.getHeight() > 0)
268       {
269         return new AlignFrame(al, this, title, false);
270       }
271     } catch (java.io.IOException ex)
272     {
273       ex.printStackTrace();
274     }
275     return null;
276   }
277
278   // //////////////////////////////////////////////
279   // //////////////////////////////////////////////
280
281   static int lastFrameX = 200;
282
283   static int lastFrameY = 200;
284
285   boolean fileFound = true;
286
287   String file = "No file";
288
289   Button launcher = new Button("Start Jalview");
290
291   /**
292    * The currentAlignFrame is static, it will change if and when the user
293    * selects a new window. Note that it will *never* point back to the embedded
294    * AlignFrame if the applet is started as embedded on the page and then
295    * afterwards a new view is created.
296    */
297   public static AlignFrame currentAlignFrame;
298
299   /**
300    * This is the first frame to be displayed, and does not change. API calls
301    * will default to this instance if currentAlignFrame is null.
302    */
303   AlignFrame initialAlignFrame;
304
305   boolean embedded = false;
306
307   private boolean checkForJmol = true;
308
309   public boolean jmolAvailable = false;
310
311   public static boolean debug;
312
313   /**
314    * init method for Jalview Applet
315    */
316   public void init()
317   {
318     /**
319      * turn on extra applet debugging
320      */
321     String dbg = getParameter("debug");
322     if (dbg != null)
323     {
324       debug = dbg.toLowerCase().equals("true");
325     }
326     /**
327      * if true disable the check for jmol
328      */
329     String chkforJmol = getParameter("nojmol");
330     if (chkforJmol != null)
331     {
332       checkForJmol = !chkforJmol.equals("true");
333     }
334     /**
335      * get the separator parameter if present
336      */
337     String sep = getParameter("separator");
338     if (sep != null)
339     {
340       if (sep.length() > 0)
341       {
342         separator = sep;
343         if (debug)
344         {
345           System.err.println("Separator set to '" + separator + "'");
346         }
347       }
348       else
349       {
350         throw new Error(
351                 "Invalid separator parameter - must be non-zero length");
352       }
353     }
354     int r = 255;
355     int g = 255;
356     int b = 255;
357     String param = getParameter("RGB");
358
359     if (param != null)
360     {
361       try
362       {
363         r = Integer.parseInt(param.substring(0, 2), 16);
364         g = Integer.parseInt(param.substring(2, 4), 16);
365         b = Integer.parseInt(param.substring(4, 6), 16);
366       } catch (Exception ex)
367       {
368         r = 255;
369         g = 255;
370         b = 255;
371       }
372     }
373
374     param = getParameter("label");
375     if (param != null)
376     {
377       launcher.setLabel(param);
378     }
379
380     this.setBackground(new Color(r, g, b));
381
382     file = getParameter("file");
383
384     if (file == null)
385     {
386       // Maybe the sequences are added as parameters
387       StringBuffer data = new StringBuffer("PASTE");
388       int i = 1;
389       while ((file = getParameter("sequence" + i)) != null)
390       {
391         data.append(file.toString() + "\n");
392         i++;
393       }
394       if (data.length() > 5)
395       {
396         file = data.toString();
397       }
398     }
399
400     LoadJmolThread jmolAvailable = new LoadJmolThread();
401     jmolAvailable.start();
402
403     final JalviewLite applet = this;
404     if (getParameter("embedded") != null
405             && getParameter("embedded").equalsIgnoreCase("true"))
406     {
407       // Launch as embedded applet in page
408       embedded = true;
409       LoadingThread loader = new LoadingThread(file, applet);
410       loader.start();
411     }
412     else if (file != null)
413     {
414       if (getParameter("showbutton") == null
415               || !getParameter("showbutton").equalsIgnoreCase("false"))
416       {
417         // Add the JalviewLite 'Button' to the page
418         add(launcher);
419         launcher.addActionListener(new java.awt.event.ActionListener()
420         {
421           public void actionPerformed(ActionEvent e)
422           {
423             LoadingThread loader = new LoadingThread(file, applet);
424             loader.start();
425           }
426         });
427       }
428       else
429       {
430         // Open jalviewLite immediately.
431         LoadingThread loader = new LoadingThread(file, applet);
432         loader.start();
433       }
434     }
435     else
436     {
437       // jalview initialisation with no alignment. loadAlignment() method can
438       // still be called to open new alignments.
439       file = "NO FILE";
440       fileFound = false;
441     }
442   }
443
444   /**
445    * Initialises and displays a new java.awt.Frame
446    * 
447    * @param frame
448    *                java.awt.Frame to be displayed
449    * @param title
450    *                title of new frame
451    * @param width
452    *                width if new frame
453    * @param height
454    *                height of new frame
455    */
456   public static void addFrame(final Frame frame, String title, int width,
457           int height)
458   {
459     frame.setLocation(lastFrameX, lastFrameY);
460     lastFrameX += 40;
461     lastFrameY += 40;
462     frame.setSize(width, height);
463     frame.setTitle(title);
464     frame.addWindowListener(new WindowAdapter()
465     {
466       public void windowClosing(WindowEvent e)
467       {
468         if (frame instanceof AlignFrame)
469         {
470           ((AlignFrame) frame).closeMenuItem_actionPerformed();
471         }
472         if (currentAlignFrame == frame)
473         {
474           currentAlignFrame = null;
475         }
476         lastFrameX -= 40;
477         lastFrameY -= 40;
478         if (frame instanceof EmbmenuFrame)
479         {
480           ((EmbmenuFrame) frame).destroyMenus();
481         }
482         frame.setMenuBar(null);
483         frame.dispose();
484       }
485
486       public void windowActivated(WindowEvent e)
487       {
488         if (frame instanceof AlignFrame)
489         {
490           currentAlignFrame = (AlignFrame) frame;
491           if (debug)
492           {
493             System.err.println("Activated window " + frame);
494           }
495         }
496         // be good.
497         super.windowActivated(e);
498       }
499       /*
500        * Probably not necessary to do this - see TODO above. (non-Javadoc)
501        * 
502        * @see java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent)
503        * 
504        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
505        * frame) { currentAlignFrame = null; if (debug) {
506        * System.err.println("Deactivated window "+frame); } }
507        * super.windowDeactivated(e); }
508        */
509     });
510     frame.setVisible(true);
511   }
512
513   /**
514    * This paints the background surrounding the "Launch Jalview button" <br>
515    * <br>
516    * If file given in parameter not found, displays error message
517    * 
518    * @param g
519    *                graphics context
520    */
521   public void paint(Graphics g)
522   {
523     if (!fileFound)
524     {
525       g.setColor(new Color(200, 200, 200));
526       g.setColor(Color.cyan);
527       g.fillRect(0, 0, getSize().width, getSize().height);
528       g.setColor(Color.red);
529       g.drawString("Jalview can't open file", 5, 15);
530       g.drawString("\"" + file + "\"", 5, 30);
531     }
532     else if (embedded)
533     {
534       g.setColor(Color.black);
535       g.setFont(new Font("Arial", Font.BOLD, 24));
536       g.drawString("Jalview Applet", 50, this.getSize().height / 2 - 30);
537       g.drawString("Loading Data...", 50, this.getSize().height / 2);
538     }
539   }
540
541   class LoadJmolThread extends Thread
542   {
543     public void run()
544     {
545       if (checkForJmol)
546       {
547         try
548         {
549           if (!System.getProperty("java.version").startsWith("1.1"))
550           {
551             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
552             jmolAvailable = true;
553           }
554           if (!jmolAvailable)
555           {
556             System.out
557                     .println("Jmol not available - Using MCview for structures");
558           }
559         } catch (java.lang.ClassNotFoundException ex)
560         {
561         }
562       }
563       else
564       {
565         jmolAvailable = false;
566         if (debug)
567         {
568           System.err
569                   .println("Skipping Jmol check. Will use MCView (probably)");
570         }
571       }
572     }
573   }
574
575   class LoadingThread extends Thread
576   {
577     /**
578      * State variable: File source
579      */
580     String file;
581
582     /**
583      * State variable: protocol for access to file source
584      */
585     String protocol;
586
587     /**
588      * State variable: format of file source
589      */
590     String format;
591
592     JalviewLite applet;
593
594     private void dbgMsg(String msg)
595     {
596       if (applet.debug)
597       {
598         System.err.println(msg);
599       }
600     }
601
602     /**
603      * update the protocol state variable for accessing the datasource located
604      * by file.
605      * 
606      * @param file
607      * @return possibly updated datasource string
608      */
609     public String setProtocolState(String file)
610     {
611       if (file.startsWith("PASTE"))
612       {
613         file = file.substring(5);
614         protocol = AppletFormatAdapter.PASTE;
615       }
616       else if (inArchive(file))
617       {
618         protocol = AppletFormatAdapter.CLASSLOADER;
619       }
620       else
621       {
622         file = addProtocol(file);
623         protocol = AppletFormatAdapter.URL;
624       }
625       dbgMsg("Protocol identified as '" + protocol + "'");
626       return file;
627     }
628
629     public LoadingThread(String _file, JalviewLite _applet)
630     {
631       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
632       file = setProtocolState(_file);
633
634       format = new jalview.io.IdentifyFile().Identify(file, protocol);
635       dbgMsg("File identified as '" + format + "'");
636       applet = _applet;
637     }
638
639     public void run()
640     {
641       startLoading();
642     }
643
644     private void startLoading()
645     {
646       dbgMsg("Loading started.");
647       Alignment al = null;
648       try
649       {
650         al = new AppletFormatAdapter().readFile(file, protocol, format);
651       } catch (java.io.IOException ex)
652       {
653         dbgMsg("File load exception.");
654         ex.printStackTrace();
655       }
656       if ((al != null) && (al.getHeight() > 0))
657       {
658         dbgMsg("Successfully loaded file.");
659         initialAlignFrame = new AlignFrame(al, applet, file, embedded);
660         // update the focus.
661         currentAlignFrame = initialAlignFrame;
662
663         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
664         {
665           currentAlignFrame.setTitle("Sequences from " + getDocumentBase());
666         }
667
668         currentAlignFrame.statusBar.setText("Successfully loaded file "
669                 + file);
670
671         String treeFile = applet.getParameter("tree");
672         if (treeFile == null)
673         {
674           treeFile = applet.getParameter("treeFile");
675         }
676
677         if (treeFile != null)
678         {
679           try
680           {
681             treeFile = setProtocolState(treeFile);
682             /*
683              * if (inArchive(treeFile)) { protocol =
684              * AppletFormatAdapter.CLASSLOADER; } else { protocol =
685              * AppletFormatAdapter.URL; treeFile = addProtocol(treeFile); }
686              */
687             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
688                     protocol);
689
690             fin.parse();
691
692             if (fin.getTree() != null)
693             {
694               currentAlignFrame.loadTree(fin, treeFile);
695               dbgMsg("Successfuly imported tree.");
696             }
697             else
698             {
699               dbgMsg("Tree parameter did not resolve to a valid tree.");
700             }
701           } catch (Exception ex)
702           {
703             ex.printStackTrace();
704           }
705         }
706
707         String param = getParameter("features");
708         if (param != null)
709         {
710           param = setProtocolState(param);
711
712           currentAlignFrame.parseFeaturesFile(param, protocol);
713         }
714
715         param = getParameter("showFeatureSettings");
716         if (param != null && param.equalsIgnoreCase("true"))
717         {
718           currentAlignFrame.viewport.showSequenceFeatures(true);
719           new FeatureSettings(currentAlignFrame.alignPanel);
720         }
721
722         param = getParameter("annotations");
723         if (param != null)
724         {
725           param = setProtocolState(param);
726
727           if (new AnnotationFile().readAnnotationFile(
728                   currentAlignFrame.viewport.getAlignment(), param,
729                   protocol))
730           {
731             currentAlignFrame.alignPanel.fontChanged();
732             currentAlignFrame.alignPanel.setScrollValues(0, 0);
733           }
734           else
735           {
736             System.err
737                     .println("Annotations were not added from annotation file '"
738                             + param + "'");
739           }
740
741         }
742
743         param = getParameter("jnetfile");
744         if (param != null)
745         {
746           try
747           {
748             param = setProtocolState(param);
749             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
750                     param, protocol);
751             JnetAnnotationMaker.add_annotation(predictions,
752                     currentAlignFrame.viewport.getAlignment(), 0, false); // false==do
753             // not
754             // add
755             // sequence
756             // profile
757             // from
758             // concise
759             // output
760             currentAlignFrame.alignPanel.fontChanged();
761             currentAlignFrame.alignPanel.setScrollValues(0, 0);
762           } catch (Exception ex)
763           {
764             ex.printStackTrace();
765           }
766         }
767
768         /*
769          * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
770          * PDB|1GAQ|1GAQ|C">
771          * 
772          * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
773          * 
774          * <param name="PDBfile3" value="1q0o Q45135_9MICO">
775          */
776
777         int pdbFileCount = 0;
778         do
779         {
780           if (pdbFileCount > 0)
781             param = getParameter("PDBFILE" + pdbFileCount);
782           else
783             param = getParameter("PDBFILE");
784
785           if (param != null)
786           {
787             PDBEntry pdb = new PDBEntry();
788
789             String seqstring;
790             SequenceI[] seqs = null;
791             String[] chains = null;
792
793             StringTokenizer st = new StringTokenizer(param, " ");
794
795             if (st.countTokens() < 2)
796             {
797               String sequence = applet.getParameter("PDBSEQ");
798               if (sequence != null)
799                 seqs = new SequenceI[]
800                 { (Sequence) currentAlignFrame.getAlignViewport()
801                         .getAlignment().findName(sequence) };
802
803             }
804             else
805             {
806               param = st.nextToken();
807               Vector tmp = new Vector();
808               Vector tmp2 = new Vector();
809
810               while (st.hasMoreTokens())
811               {
812                 seqstring = st.nextToken();
813                 StringTokenizer st2 = new StringTokenizer(seqstring, "=");
814                 if (st2.countTokens() > 1)
815                 {
816                   // This is the chain
817                   tmp2.addElement(st2.nextToken());
818                   seqstring = st2.nextToken();
819                 }
820                 tmp.addElement((Sequence) currentAlignFrame
821                         .getAlignViewport().getAlignment().findName(
822                                 seqstring));
823               }
824
825               seqs = new SequenceI[tmp.size()];
826               tmp.copyInto(seqs);
827               if (tmp2.size() == tmp.size())
828               {
829                 chains = new String[tmp2.size()];
830                 tmp2.copyInto(chains);
831               }
832             }
833             param = setProtocolState(param);
834
835             if (// !jmolAvailable
836             // &&
837             protocol == AppletFormatAdapter.CLASSLOADER)
838             {
839               // TODO: verify this Re:
840               // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
841               // This exception preserves the current behaviour where, even if
842               // the local pdb file was identified in the class loader
843               protocol = AppletFormatAdapter.URL; // this is probably NOT
844               // CORRECT!
845               param = addProtocol(param); // 
846             }
847
848             pdb.setFile(param);
849
850             if (seqs != null)
851             {
852               for (int i = 0; i < seqs.length; i++)
853               {
854                 if (seqs[i] != null)
855                 {
856                   ((Sequence) seqs[i]).addPDBId(pdb);
857                 }
858                 else
859                 {
860                   if (JalviewLite.debug)
861                   {
862                     // this may not really be a problem but we give a warning
863                     // anyway
864                     System.err
865                             .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
866                                     + i + ")");
867                   }
868                 }
869               }
870
871               if (jmolAvailable)
872               {
873                 new jalview.appletgui.AppletJmol(pdb, seqs, chains,
874                         currentAlignFrame.alignPanel, protocol);
875                 lastFrameX += 40;
876                 lastFrameY += 40;
877               }
878               else
879                 new MCview.AppletPDBViewer(pdb, seqs, chains,
880                         currentAlignFrame.alignPanel, protocol);
881             }
882           }
883
884           pdbFileCount++;
885         } while (pdbFileCount < 10);
886
887         // ///////////////////////////
888         // modify display of features
889         //
890         // hide specific groups
891         param = getParameter("hidefeaturegroups");
892         if (param != null)
893         {
894           applet.setFeatureGroupState(param, false);
895         }
896         // show specific groups
897         param = getParameter("showfeaturegroups");
898         if (param != null)
899         {
900           applet.setFeatureGroupState(param, true);
901         }
902       }
903       else
904       {
905         fileFound = false;
906         remove(launcher);
907         repaint();
908       }
909     }
910
911     /**
912      * Discovers whether the given file is in the Applet Archive
913      * 
914      * @param file
915      *                String
916      * @return boolean
917      */
918     boolean inArchive(String file)
919     {
920       // This might throw a security exception in certain browsers
921       // Netscape Communicator for instance.
922       try
923       {
924         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
925         if (debug)
926         {
927           System.err.println("Resource '" + file + "' was "
928                   + (rtn ? "" : "not") + " located by classloader.");
929         }
930         return rtn;
931       } catch (Exception ex)
932       {
933         System.out.println("Exception checking resources: " + file + " "
934                 + ex);
935         return false;
936       }
937     }
938
939     String addProtocol(String file)
940     {
941       if (file.indexOf("://") == -1)
942       {
943         file = getCodeBase() + file;
944         if (debug)
945         {
946           System.err.println("Prepended codebase for resource: '" + file
947                   + "'");
948         }
949       }
950
951       return file;
952     }
953   }
954
955   /**
956    * @return the default alignFrame acted on by the public applet methods. May
957    *         return null with an error message on System.err indicating the
958    *         fact.
959    */
960   protected AlignFrame getDefaultTargetFrame()
961   {
962     if (currentAlignFrame != null)
963     {
964       return currentAlignFrame;
965     }
966     if (initialAlignFrame != null)
967     {
968       return initialAlignFrame;
969     }
970     System.err
971             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
972     return null;
973   }
974
975   /**
976    * separator used for separatorList
977    */
978   protected String separator = "|"; // this is a safe(ish) separator - tabs
979
980   // don't work for firefox
981
982   /**
983    * parse the string into a list
984    * 
985    * @param list
986    * @return elements separated by separator
987    */
988   public String[] separatorListToArray(String list)
989   {
990     int seplen = separator.length();
991     if (list == null || list.equals(""))
992       return null;
993     java.util.Vector jv = new Vector();
994     int cp = 0, pos;
995     while ((pos = list.indexOf(separator, cp)) > cp)
996     {
997       jv.addElement(list.substring(cp, pos));
998       cp = pos + seplen;
999     }
1000     if (cp < list.length())
1001     {
1002       jv.addElement(list.substring(cp));
1003     }
1004     if (jv.size() > 0)
1005     {
1006       String[] v = new String[jv.size()];
1007       for (int i = 0; i < v.length; i++)
1008       {
1009         v[i] = (String) jv.elementAt(i);
1010       }
1011       jv.removeAllElements();
1012       if (debug)
1013       {
1014         System.err.println("Array from '" + separator
1015                 + "' separated List:\n" + v.length);
1016         for (int i = 0; i < v.length; i++)
1017         {
1018           System.err.println("item " + i + " '" + v[i] + "'");
1019         }
1020       }
1021       return v;
1022     }
1023     if (debug)
1024     {
1025       System.err.println("Empty Array from '" + separator
1026               + "' separated List");
1027     }
1028     return null;
1029   }
1030
1031   /**
1032    * concatenate the list with separator
1033    * 
1034    * @param list
1035    * @return concatenated string
1036    */
1037   public String arrayToSeparatorList(String[] list)
1038   {
1039     StringBuffer v = new StringBuffer();
1040     if (list != null)
1041     {
1042       for (int i = 0, iSize = list.length - 1; i < iSize; i++)
1043       {
1044         if (list[i] != null)
1045         {
1046           v.append(list[i]);
1047         }
1048         v.append(separator);
1049       }
1050       if (list[list.length - 1] != null)
1051       {
1052         v.append(list[list.length - 1]);
1053       }
1054       if (debug)
1055       {
1056         System.err.println("Returning '" + separator
1057                 + "' separated List:\n");
1058         System.err.println(v);
1059       }
1060       return v.toString();
1061     }
1062     if (debug)
1063     {
1064       System.err.println("Returning empty '" + separator
1065               + "' separated List\n");
1066     }
1067     return "";
1068   }
1069
1070   /**
1071    * @return
1072    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1073    */
1074   public String getFeatureGroups()
1075   {
1076     String lst = arrayToSeparatorList(getDefaultTargetFrame()
1077             .getFeatureGroups());
1078     return lst;
1079   }
1080
1081   /**
1082    * @param alf
1083    *                alignframe to get feature groups on
1084    * @return
1085    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1086    */
1087   public String getFeatureGroupsOn(AlignFrame alf)
1088   {
1089     String lst = arrayToSeparatorList(alf.getFeatureGroups());
1090     return lst;
1091   }
1092
1093   /**
1094    * @param visible
1095    * @return
1096    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1097    */
1098   public String getFeatureGroupsOfState(boolean visible)
1099   {
1100     return arrayToSeparatorList(getDefaultTargetFrame()
1101             .getFeatureGroupsOfState(visible));
1102   }
1103
1104   /**
1105    * @param alf
1106    *                align frame to get groups of state visible
1107    * @param visible
1108    * @return
1109    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1110    */
1111   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
1112   {
1113     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
1114   }
1115
1116   /**
1117    * @param groups
1118    *                tab separated list of group names
1119    * @param state
1120    *                true or false
1121    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[],
1122    *      boolean)
1123    */
1124   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
1125           boolean state)
1126   {
1127     boolean st = state;// !(state==null || state.equals("") ||
1128     // state.toLowerCase().equals("false"));
1129     alf.setFeatureGroupState(separatorListToArray(groups), st);
1130   }
1131
1132   public void setFeatureGroupState(String groups, boolean state)
1133   {
1134     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
1135   }
1136
1137   /**
1138    * List separator string
1139    * 
1140    * @return the separator
1141    */
1142   public String getSeparator()
1143   {
1144     return separator;
1145   }
1146
1147   /**
1148    * List separator string
1149    * 
1150    * @param separator
1151    *                the separator to set
1152    */
1153   public void setSeparator(String separator)
1154   {
1155     this.separator = separator;
1156   }
1157 }