4d45f8ca70d418a6da7a742a85b842d6eaaaeff8
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 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
38     extends Applet
39 {
40
41
42
43   ///////////////////////////////////////////
44   //The following public methods maybe called
45   //externally, eg via javascript in HTML page
46   /**
47    * @return list of selected sequences separated by "¬"
48    */
49   public String getSelectedSequences()
50   {
51     return getSelectedSequencesFrom(getDefaultTargetFrame());
52   }
53   public String getSelectedSequencesFrom(AlignFrame alf)
54   {
55     StringBuffer result = new StringBuffer("");
56
57     if (alf.viewport.getSelectionGroup() != null)
58     {
59       SequenceI[] seqs = alf.viewport.getSelectionGroup().
60           getSequencesInOrder(
61                   alf.viewport.getAlignment());
62
63       for (int i = 0; i < seqs.length; i++)
64       {
65         result.append(seqs[i].getName() + "¬");
66       }
67     }
68
69     return result.toString();
70   }
71   
72   public String getAlignment(String format)
73   {
74     return getAlignmentFrom(getDefaultTargetFrame(), format, "true");
75   }
76   public String getAlignmentFrom(AlignFrame alf, String format)
77   {
78     return getAlignmentFrom(alf, format, "true");
79   }
80   public String getAlignment(String format, String suffix)
81   {
82     return getAlignmentFrom(getDefaultTargetFrame(), format, suffix);
83   }
84   public String getAlignmentFrom(AlignFrame alf, String format, String suffix)
85   {
86     try
87     {
88       boolean seqlimits = suffix.equalsIgnoreCase("true");
89
90       String reply = new AppletFormatAdapter().formatSequences(format,
91           alf.viewport.getAlignment(), seqlimits);
92       return reply;
93     }
94     catch (Exception ex)
95     {
96       ex.printStackTrace();
97       return "Error retrieving alignment in " + format + " format. ";
98     }
99   }
100
101   public void loadAnnotation(String annotation)
102   {
103     loadAnnotationFrom(getDefaultTargetFrame(), annotation);
104   }
105   public void loadAnnotationFrom(AlignFrame alf, String annotation)
106   {
107     if (new AnnotationFile().readAnnotationFile(
108         alf.getAlignViewport().getAlignment(), annotation,
109         AppletFormatAdapter.PASTE))
110     {
111       alf.alignPanel.fontChanged();
112       alf.alignPanel.setScrollValues(0, 0);
113     }
114     else
115     {
116       alf.parseFeaturesFile(annotation, AppletFormatAdapter.PASTE);
117     }
118   }
119
120   public String getFeatures(String format)
121   {
122     return getFeaturesFrom(getDefaultTargetFrame(), format);
123   }
124   public String getFeaturesFrom(AlignFrame alf, String format)
125   {
126     return alf.outputFeatures(false, format);
127   }
128   public String getAnnotation()
129   {
130     return getAnnotationFrom(getDefaultTargetFrame());
131   }
132   public String getAnnotationFrom(AlignFrame alf)
133   {
134     return alf.outputAnnotations(false);
135   }
136   public AlignFrame newView()
137   {
138     return newViewFrom(getDefaultTargetFrame());
139   }
140   public AlignFrame newView(String name)
141   {
142     return newViewFrom(getDefaultTargetFrame(), name);
143   }
144
145   public AlignFrame newViewFrom(AlignFrame alf)
146   {
147     return alf.newView(null);
148   }
149   public AlignFrame newViewFrom(AlignFrame alf, String name)
150   {
151     return alf.newView(name);
152   }
153   /**
154    * 
155    * @param text alignment file as a string
156    * @param title window title 
157    * @return null or new alignment frame
158    */
159   public AlignFrame loadAlignment(String text, String title)
160   {
161     Alignment al = null;
162     String format = new IdentifyFile().Identify(text, AppletFormatAdapter.PASTE);
163     try
164     {
165       al = new AppletFormatAdapter().readFile(text,
166                                               AppletFormatAdapter.PASTE,
167                                               format);
168       if (al.getHeight() > 0)
169       {
170         return new AlignFrame(al, this, title, false);
171       }
172     }
173     catch (java.io.IOException ex)
174     {
175       ex.printStackTrace();
176     }
177     return null;
178   }
179
180   ////////////////////////////////////////////////
181   ////////////////////////////////////////////////
182
183
184
185   static int lastFrameX = 200;
186   static int lastFrameY = 200;
187   boolean fileFound = true;
188   String file = "No file";
189   Button launcher = new Button("Start Jalview");
190
191   /**
192    * The currentAlignFrame is static, it will change
193    * if and when the user selects a new window.
194    * Note that it will *never* point back to the embedded AlignFrame 
195    * if the applet is started as embedded on the page and then afterwards a new view is created.
196    */
197   public static AlignFrame currentAlignFrame;
198
199   /** 
200    * This is the first frame to be displayed, and does not change.
201    * API calls will default to this instance if currentAlignFrame is null.
202    */
203   AlignFrame initialAlignFrame;
204
205   boolean embedded = false;
206
207   public boolean jmolAvailable = false;
208   public static boolean debug;
209
210   /**
211    * init method for Jalview Applet
212    */
213   public void init()
214   {
215     String dbg = getParameter("debug");
216     if (dbg!=null)
217     {
218       debug = dbg.toLowerCase().equals("true");
219     }
220     /**
221      * get the separator parameter if present
222      */
223     String sep = getParameter("separator");
224     if (sep!=null)
225     {
226       if (sep.length()>0)
227       {      separator = sep;
228         if (debug)
229         {
230           System.err.println("Separator set to '"+separator+"'");
231         }
232       } else {
233         throw new Error("Invalid separator parameter - must be non-zero length");
234       }
235     }
236     int r = 255;
237     int g = 255;
238     int b = 255;
239     String param = getParameter("RGB");
240
241     if (param != null)
242     {
243       try
244       {
245         r = Integer.parseInt(param.substring(0, 2), 16);
246         g = Integer.parseInt(param.substring(2, 4), 16);
247         b = Integer.parseInt(param.substring(4, 6), 16);
248       }
249       catch (Exception ex)
250       {
251         r = 255;
252         g = 255;
253         b = 255;
254       }
255     }
256
257     param = getParameter("label");
258     if (param != null)
259     {
260       launcher.setLabel(param);
261     }
262
263     this.setBackground(new Color(r, g, b));
264
265     file = getParameter("file");
266
267     if (file == null)
268     {
269       //Maybe the sequences are added as parameters
270       StringBuffer data = new StringBuffer("PASTE");
271       int i = 1;
272       while ( (file = getParameter("sequence" + i)) != null)
273       {
274         data.append(file.toString() + "\n");
275         i++;
276       }
277       if (data.length() > 5)
278       {
279         file = data.toString();
280       }
281     }
282
283     LoadJmolThread jmolAvailable = new LoadJmolThread();
284     jmolAvailable.start();
285
286     final JalviewLite applet = this;
287     if (getParameter("embedded") != null
288         && getParameter("embedded").equalsIgnoreCase("true"))
289     {
290       embedded = true;
291       LoadingThread loader = new LoadingThread(file, applet);
292       loader.start();
293     }
294     else if (file != null)
295     {
296       add(launcher);
297
298       launcher.addActionListener(new java.awt.event.ActionListener()
299       {
300         public void actionPerformed(ActionEvent e)
301         {
302           LoadingThread loader = new LoadingThread(file,
303               applet);
304           loader.start();
305         }
306       });
307     }
308     else
309     {
310       file = "NO FILE";
311       fileFound = false;
312     }
313   }
314
315
316   /**
317    * Initialises and displays a new java.awt.Frame
318    *
319    * @param frame java.awt.Frame to be displayed
320    * @param title title of new frame
321    * @param width width if new frame
322    * @param height height of new frame
323    */
324   public static void addFrame(final Frame frame, String title, int width,
325                               int height)
326   {
327     frame.setLocation(lastFrameX, lastFrameY);
328     lastFrameX += 40;
329     lastFrameY += 40;
330     frame.setSize(width, height);
331     frame.setTitle(title);
332     frame.addWindowListener(new WindowAdapter()
333     {
334       public void windowClosing(WindowEvent e)
335       {
336         if (frame instanceof AlignFrame)
337         {
338           ( (AlignFrame) frame).closeMenuItem_actionPerformed();
339         }
340         if (currentAlignFrame == frame)
341         {
342           currentAlignFrame = null;
343         }
344         lastFrameX -= 40;
345         lastFrameY -= 40;
346         if (frame instanceof EmbmenuFrame)
347         {
348           ((EmbmenuFrame) frame).destroyMenus();
349         }
350         frame.setMenuBar(null);
351         frame.dispose();
352       }
353       
354       public void windowActivated(WindowEvent e)
355       {
356         if (frame instanceof AlignFrame)
357         {
358           currentAlignFrame = (AlignFrame) frame;
359           if (debug)
360           {
361             System.err.println("Activated window "+frame);
362           }
363         }
364         // be good.
365         super.windowActivated(e);
366       }
367       /* Probably not necessary to do this - see TODO above.
368        * (non-Javadoc)
369        * @see java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent)
370        *
371       public void windowDeactivated(WindowEvent e)
372       {
373         if (currentAlignFrame == frame)
374         {
375           currentAlignFrame = null;
376           if (debug)
377           {
378             System.err.println("Deactivated window "+frame);
379           }
380         }
381         super.windowDeactivated(e);
382       }
383        */
384     });
385     frame.setVisible(true);
386   }
387
388   /**
389    * This paints the background surrounding the "Launch Jalview button"
390    * <br>
391    * <br>If file given in parameter not found, displays error message
392    *
393    * @param g graphics context
394    */
395   public void paint(Graphics g)
396   {
397     if (!fileFound)
398     {
399       g.setColor(new Color(200, 200, 200));
400       g.setColor(Color.cyan);
401       g.fillRect(0, 0, getSize().width, getSize().height);
402       g.setColor(Color.red);
403       g.drawString("Jalview can't open file", 5, 15);
404       g.drawString("\"" + file + "\"", 5, 30);
405     }
406     else if (embedded)
407     {
408       g.setColor(Color.black);
409       g.setFont(new Font("Arial", Font.BOLD, 24));
410       g.drawString("Jalview Applet", 50, this.getSize().height / 2 - 30);
411       g.drawString("Loading Data...", 50, this.getSize().height / 2);
412     }
413   }
414
415
416   class LoadJmolThread extends Thread
417   {
418     public void run()
419     {
420       try
421       {
422         if (!System.getProperty("java.version").startsWith("1.1"))
423         {
424           Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
425           jmolAvailable = true;
426         }
427       }
428       catch (java.lang.ClassNotFoundException ex)
429       {
430         System.out.println("Jmol not available - Using MCview for structures");
431       }
432     }
433   }
434
435
436   class LoadingThread
437       extends Thread
438   {
439     String file;
440     String protocol;
441     String format;
442     JalviewLite applet;
443
444     public LoadingThread(String _file,
445                          JalviewLite _applet)
446     {
447       if (applet.debug)
448       {
449         System.err.println("Loading thread started with:\n>>file\n"+_file+">>endfile");
450       }
451       file = _file;
452       if (file.startsWith("PASTE"))
453       {
454         file = file.substring(5);
455         protocol = AppletFormatAdapter.PASTE;
456       }
457       else if (inArchive(file))
458       {
459         protocol = AppletFormatAdapter.CLASSLOADER;
460       }
461       else
462       {
463         file = addProtocol(file);
464         protocol = AppletFormatAdapter.URL;
465       }
466       if (applet.debug)
467       {
468         System.err.println("Protocol identified as '"+protocol+"'");
469       }
470       format = new jalview.io.IdentifyFile().Identify(file, protocol);
471       if (applet.debug)
472       {
473         System.err.println("File identified as '"+format+"'");
474       }
475       applet = _applet;
476     }
477
478     public void run()
479     {
480       startLoading();
481     }
482
483     private void startLoading()
484     {
485       Alignment al = null;
486       try
487       {
488         al = new AppletFormatAdapter().readFile(file, protocol,
489                                                 format);
490       }
491       catch (java.io.IOException ex)
492       {
493         ex.printStackTrace();
494       }
495       if ( (al != null) && (al.getHeight() > 0))
496       {
497         initialAlignFrame =  new AlignFrame(al,
498                                            applet,
499                                            file,
500                                            embedded);
501         // update the focus.
502         currentAlignFrame = initialAlignFrame;
503
504         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
505         {
506           currentAlignFrame.setTitle("Sequences from " + getDocumentBase());
507         }
508
509         currentAlignFrame.statusBar.setText("Successfully loaded file " + file);
510
511         String treeFile = applet.getParameter("tree");
512         if (treeFile == null)
513         {
514           treeFile = applet.getParameter("treeFile");
515         }
516
517         if (treeFile != null)
518         {
519           try
520           {
521             if (inArchive(treeFile))
522             {
523               protocol = AppletFormatAdapter.CLASSLOADER;
524             }
525             else
526             {
527               protocol = AppletFormatAdapter.URL;
528               treeFile = addProtocol(treeFile);
529             }
530
531             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
532                 protocol);
533
534             fin.parse();
535
536             if (fin.getTree() != null)
537             {
538               currentAlignFrame.loadTree(fin, treeFile);
539             }
540           }
541           catch (Exception ex)
542           {
543             ex.printStackTrace();
544           }
545         }
546
547         String param = getParameter("features");
548         if (param != null)
549         {
550           if (!inArchive(param))
551           {
552             param = addProtocol(param);
553           }
554
555           currentAlignFrame.parseFeaturesFile(param, protocol);
556         }
557
558         param = getParameter("showFeatureSettings");
559         if (param != null && param.equalsIgnoreCase("true"))
560         {
561           currentAlignFrame.viewport.showSequenceFeatures(true);
562           new FeatureSettings(currentAlignFrame.alignPanel);
563         }
564
565         param = getParameter("annotations");
566         if (param != null)
567         {
568           if (!inArchive(param))
569           {
570             param = addProtocol(param);
571           }
572
573           new AnnotationFile().readAnnotationFile(
574               currentAlignFrame.viewport.getAlignment(),
575               param,
576               protocol);
577
578           currentAlignFrame.alignPanel.fontChanged();
579           currentAlignFrame.alignPanel.setScrollValues(0, 0);
580
581         }
582
583         param = getParameter("jnetfile");
584         if (param != null)
585         {
586           try
587           {
588             if (inArchive(param))
589             {
590               protocol = AppletFormatAdapter.CLASSLOADER;
591             }
592             else
593             {
594               protocol = AppletFormatAdapter.URL;
595               param = addProtocol(param);
596             }
597
598             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
599                 param, protocol);
600             new JnetAnnotationMaker().add_annotation(predictions,
601                 currentAlignFrame.viewport.getAlignment(),
602                 0, false); // do not add sequence profile from concise output
603             currentAlignFrame.alignPanel.fontChanged();
604             currentAlignFrame.alignPanel.setScrollValues(0, 0);
605           }
606           catch (Exception ex)
607           {
608             ex.printStackTrace();
609           }
610         }
611
612         /*
613          <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B PDB|1GAQ|1GAQ|C">
614
615          <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
616
617          <param name="PDBfile3" value="1q0o Q45135_9MICO">
618         */
619
620
621         int pdbFileCount = 0;
622         do{
623           if (pdbFileCount > 0)
624             param = getParameter("PDBFILE" + pdbFileCount);
625           else
626             param = getParameter("PDBFILE");
627
628           if (param != null)
629           {
630             PDBEntry pdb = new PDBEntry();
631
632             String seqstring;
633             SequenceI[] seqs = null;
634             String [] chains = null;
635
636             StringTokenizer st = new StringTokenizer(param, " ");
637
638             if (st.countTokens() < 2)
639             {
640               String sequence = applet.getParameter("PDBSEQ");
641               if (sequence != null)
642                 seqs = new SequenceI[]
643                     {
644                     (Sequence) currentAlignFrame.
645                     getAlignViewport().getAlignment().
646                     findName(sequence)};
647
648             }
649             else
650             {
651               param = st.nextToken();
652               Vector tmp = new Vector();
653               Vector tmp2 = new Vector();
654
655               while (st.hasMoreTokens())
656               {
657                 seqstring = st.nextToken();
658                 StringTokenizer st2 = new StringTokenizer(seqstring,"=");
659                 if(st2.countTokens()>1)
660                 {
661                   //This is the chain
662                   tmp2.addElement(st2.nextToken());
663                   seqstring = st2.nextToken();
664                 }
665                 tmp.addElement( (Sequence) currentAlignFrame.
666                                  getAlignViewport().getAlignment().
667                                  findName(seqstring));
668               }
669
670               seqs = new SequenceI[tmp.size()];
671               tmp.copyInto(seqs);
672               if(tmp2.size()==tmp.size())
673               {
674                 chains = new String[tmp2.size()];
675                 tmp2.copyInto(chains);
676               }
677             }
678
679             if (inArchive(param) && !jmolAvailable)
680             {
681               protocol = AppletFormatAdapter.CLASSLOADER;
682             }
683             else
684             {
685               protocol = AppletFormatAdapter.URL;
686               param = addProtocol(param);
687             }
688
689             pdb.setFile(param);
690
691             if(seqs!=null)
692             {
693               for (int i = 0; i < seqs.length; i++)
694               {
695                 ( (Sequence) seqs[i]).addPDBId(pdb);
696               }
697
698               if (jmolAvailable)
699               {
700                 new jalview.appletgui.AppletJmol(pdb,
701                                                  seqs,
702                                                  chains,
703                                                  currentAlignFrame.alignPanel,
704                                                  protocol);
705                 lastFrameX += 40;
706                 lastFrameY+=40;
707               }
708               else
709                     new MCview.AppletPDBViewer(pdb,
710                                            seqs,
711                                            chains,
712                                            currentAlignFrame.alignPanel,
713                                            protocol);
714             }
715           }
716
717           pdbFileCount++;
718         }
719         while(pdbFileCount < 10);
720         
721         /////////////////////////////
722         // modify display of features
723         //
724         // hide specific groups
725         param = getParameter("hidefeaturegroups");
726         if (param != null)
727         {
728           applet.setFeatureGroupState(param, false);
729         }
730         // show specific groups
731         param = getParameter("showfeaturegroups");
732         if (param != null)
733         {
734           applet.setFeatureGroupState(param, true);
735         }
736       }
737       else
738       {
739         fileFound = false;
740         remove(launcher);
741         repaint();
742       }
743     }
744
745     /**
746      * Discovers whether the given file is in the Applet Archive
747      * @param file String
748      * @return boolean
749      */
750     boolean inArchive(String file)
751     {
752       //This might throw a security exception in certain browsers
753       //Netscape Communicator for instance.
754       try
755       {
756         return (getClass().getResourceAsStream("/" + file) != null);
757       }
758       catch (Exception ex)
759       {
760         System.out.println("Exception checking resources: " + file + " " + ex);
761         return false;
762       }
763     }
764
765     String addProtocol(String file)
766     {
767       if (file.indexOf("://") == -1)
768       {
769         file = getCodeBase() + file;
770       }
771
772       return file;
773     }
774   }
775   /**
776    * @return the default alignFrame acted on by the public applet methods.
777    * May return null with an error message on System.err indicating the fact. 
778    */
779   protected AlignFrame getDefaultTargetFrame()
780   {
781     if (currentAlignFrame!=null)
782     {
783       return currentAlignFrame;
784     }
785     if (initialAlignFrame!=null)
786     {
787       return initialAlignFrame;
788     }
789     System.err.println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
790     return null;
791   }
792   /**
793    * separator used for separatorList
794    */
795   protected String separator = "|"; // this is a safe(ish) separator - tabs don't work for firefox
796   /**
797    * parse the string into a list
798    * @param list
799    * @return elements separated by separator
800    */
801   public String[] separatorListToArray(String list)
802   {
803     int seplen = separator.length();
804     if (list==null || list.equals(""))
805       return null;
806     java.util.Vector jv = new Vector();
807     int cp=0,pos;
808     while ((pos=list.indexOf(separator,cp))>cp)
809     {
810       jv.addElement(list.substring(cp,pos));
811       cp = pos+seplen;
812     }
813     if (cp<list.length())
814     {
815       jv.addElement(list.substring(cp));
816     }
817     if (jv.size()>0)
818     { String[] v = new String[jv.size()];
819       for (int i=0; i<v.length; i++)
820       {
821         v[i] = (String) jv.elementAt(i);
822       }
823       jv.removeAllElements();
824       if (debug)
825       {
826         System.err.println("Array from '"+separator+"' separated List:\n"+v.length);
827         for (int i=0; i<v.length;i++)
828         {
829           System.err.println("item "+i+" '"+v[i]+"'");
830         }
831       }
832       return v;
833     }
834     if (debug)
835     {
836       System.err.println("Empty Array from '"+separator+"' separated List");
837     }
838     return null;
839   }
840   /**
841    * concatenate the list with separator
842    * @param list
843    * @return concatenated string
844    */
845   public String arrayToSeparatorList(String[] list)
846   {
847     StringBuffer v = new StringBuffer();
848     if (list!=null)
849     {
850       for (int i=0,iSize=list.length-1;i<iSize;i++)
851       { 
852         if (list[i]!=null)
853         {  
854           v.append(list[i]); 
855         }
856         v.append(separator);
857       }
858       if (list[list.length-1]!=null)
859       { v.append(list[list.length-1]);
860       }
861       if (debug)
862       {
863         System.err.println("Returning '"+separator+"' separated List:\n");
864         System.err.println(v);
865       }
866       return v.toString();
867     }
868     if (debug)
869     {
870       System.err.println("Returning empty '"+separator+"' separated List\n");
871     }
872     return "";
873   }
874   /**
875    * @return
876    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
877    */
878   public String getFeatureGroups()
879   {
880     String lst = arrayToSeparatorList(getDefaultTargetFrame().getFeatureGroups());
881     return lst;
882   }
883   /**
884    * @param alf alignframe to get feature groups on
885    * @return
886    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
887    */
888   public String getFeatureGroupsOn(AlignFrame alf)
889   {
890     String lst = arrayToSeparatorList(alf.getFeatureGroups());
891     return lst;
892   }
893   /**
894    * @param visible
895    * @return
896    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
897    */
898   public String getFeatureGroupsOfState(boolean visible)
899   {
900     return arrayToSeparatorList(getDefaultTargetFrame().getFeatureGroupsOfState(visible));
901   }
902   /**
903    * @param alf align frame to get groups of state visible
904    * @param visible
905    * @return
906    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
907    */
908   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
909   {
910     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
911   }  /**
912    * @param groups tab separated list of group names 
913    * @param state true or false
914    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[], boolean)
915    */
916   public void setFeatureGroupStateOn(AlignFrame alf, String groups, boolean state)
917   {
918     boolean st = state;//!(state==null || state.equals("") || state.toLowerCase().equals("false"));
919     alf.setFeatureGroupState(separatorListToArray(groups), st);
920   }
921   public void setFeatureGroupState(String groups, boolean state)
922   {
923     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
924   }
925   /**
926    * List separator string
927    * @return the separator
928    */
929   public String getSeparator()
930   {
931     return separator;
932   }
933   /**
934    * List separator string
935    * @param separator the separator to set
936    */
937   public void setSeparator(String separator)
938   {
939     this.separator = separator;
940   }
941 }