patch to fix occasional arrayoutofbounds exception when working with hidden columns...
[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   private boolean checkedForJmol = false; // ensure we don't check for jmol every time the app is re-inited
309
310   public boolean jmolAvailable = false;
311
312   public static boolean debug;
313
314   /**
315    * init method for Jalview Applet
316    */
317   public void init()
318   {
319     /**
320      * turn on extra applet debugging
321      */
322     String dbg = getParameter("debug");
323     if (dbg != null)
324     {
325       debug = dbg.toLowerCase().equals("true");
326     }
327     /**
328      * if true disable the check for jmol
329      */
330     String chkforJmol = getParameter("nojmol");
331     if (chkforJmol != null)
332     {
333       checkForJmol = !chkforJmol.equals("true");
334     }
335     /**
336      * get the separator parameter if present
337      */
338     String sep = getParameter("separator");
339     if (sep != null)
340     {
341       if (sep.length() > 0)
342       {
343         separator = sep;
344         if (debug)
345         {
346           System.err.println("Separator set to '" + separator + "'");
347         }
348       }
349       else
350       {
351         throw new Error(
352                 "Invalid separator parameter - must be non-zero length");
353       }
354     }
355     int r = 255;
356     int g = 255;
357     int b = 255;
358     String param = getParameter("RGB");
359
360     if (param != null)
361     {
362       try
363       {
364         r = Integer.parseInt(param.substring(0, 2), 16);
365         g = Integer.parseInt(param.substring(2, 4), 16);
366         b = Integer.parseInt(param.substring(4, 6), 16);
367       } catch (Exception ex)
368       {
369         r = 255;
370         g = 255;
371         b = 255;
372       }
373     }
374
375     param = getParameter("label");
376     if (param != null)
377     {
378       launcher.setLabel(param);
379     }
380
381     this.setBackground(new Color(r, g, b));
382
383     file = getParameter("file");
384
385     if (file == null)
386     {
387       // Maybe the sequences are added as parameters
388       StringBuffer data = new StringBuffer("PASTE");
389       int i = 1;
390       while ((file = getParameter("sequence" + i)) != null)
391       {
392         data.append(file.toString() + "\n");
393         i++;
394       }
395       if (data.length() > 5)
396       {
397         file = data.toString();
398       }
399     }
400
401     final JalviewLite applet = this;
402     if (getParameter("embedded") != null
403             && getParameter("embedded").equalsIgnoreCase("true"))
404     {
405       // Launch as embedded applet in page
406       embedded = true;
407       LoadingThread loader = new LoadingThread(file, applet);
408       loader.start();
409     }
410     else if (file != null)
411     {
412       if (getParameter("showbutton") == null
413               || !getParameter("showbutton").equalsIgnoreCase("false"))
414       {
415         // Add the JalviewLite 'Button' to the page
416         add(launcher);
417         launcher.addActionListener(new java.awt.event.ActionListener()
418         {
419           public void actionPerformed(ActionEvent e)
420           {
421             LoadingThread loader = new LoadingThread(file, applet);
422             loader.start();
423           }
424         });
425       }
426       else
427       {
428         // Open jalviewLite immediately.
429         LoadingThread loader = new LoadingThread(file, applet);
430         loader.start();
431       }
432     }
433     else
434     {
435       // jalview initialisation with no alignment. loadAlignment() method can
436       // still be called to open new alignments.
437       file = "NO FILE";
438       fileFound = false;
439     }
440   }
441
442   /**
443    * Initialises and displays a new java.awt.Frame
444    * 
445    * @param frame
446    *                java.awt.Frame to be displayed
447    * @param title
448    *                title of new frame
449    * @param width
450    *                width if new frame
451    * @param height
452    *                height of new frame
453    */
454   public static void addFrame(final Frame frame, String title, int width,
455           int height)
456   {
457     frame.setLocation(lastFrameX, lastFrameY);
458     lastFrameX += 40;
459     lastFrameY += 40;
460     frame.setSize(width, height);
461     frame.setTitle(title);
462     frame.addWindowListener(new WindowAdapter()
463     {
464       public void windowClosing(WindowEvent e)
465       {
466         if (frame instanceof AlignFrame)
467         {
468           ((AlignFrame) frame).closeMenuItem_actionPerformed();
469         }
470         if (currentAlignFrame == frame)
471         {
472           currentAlignFrame = null;
473         }
474         lastFrameX -= 40;
475         lastFrameY -= 40;
476         if (frame instanceof EmbmenuFrame)
477         {
478           ((EmbmenuFrame) frame).destroyMenus();
479         }
480         frame.setMenuBar(null);
481         frame.dispose();
482       }
483
484       public void windowActivated(WindowEvent e)
485       {
486         if (frame instanceof AlignFrame)
487         {
488           currentAlignFrame = (AlignFrame) frame;
489           if (debug)
490           {
491             System.err.println("Activated window " + frame);
492           }
493         }
494         // be good.
495         super.windowActivated(e);
496       }
497       /*
498        * Probably not necessary to do this - see TODO above. (non-Javadoc)
499        * 
500        * @see java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent)
501        * 
502        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
503        * frame) { currentAlignFrame = null; if (debug) {
504        * System.err.println("Deactivated window "+frame); } }
505        * super.windowDeactivated(e); }
506        */
507     });
508     frame.setVisible(true);
509   }
510
511   /**
512    * This paints the background surrounding the "Launch Jalview button" <br>
513    * <br>
514    * If file given in parameter not found, displays error message
515    * 
516    * @param g
517    *                graphics context
518    */
519   public void paint(Graphics g)
520   {
521     if (!fileFound)
522     {
523       g.setColor(new Color(200, 200, 200));
524       g.setColor(Color.cyan);
525       g.fillRect(0, 0, getSize().width, getSize().height);
526       g.setColor(Color.red);
527       g.drawString("Jalview can't open file", 5, 15);
528       g.drawString("\"" + file + "\"", 5, 30);
529     }
530     else if (embedded)
531     {
532       g.setColor(Color.black);
533       g.setFont(new Font("Arial", Font.BOLD, 24));
534       g.drawString("Jalview Applet", 50, this.getSize().height / 2 - 30);
535       g.drawString("Loading Data...", 50, this.getSize().height / 2);
536     }
537   }
538
539   class LoadJmolThread extends Thread
540   {
541     private boolean running=false;
542
543     public void run()
544     {
545       if (running || checkedForJmol) {
546         return;
547       }
548       running=true;
549       if (checkForJmol)
550       {
551         try
552         {
553           if (!System.getProperty("java.version").startsWith("1.1"))
554           {
555             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
556             jmolAvailable = true;
557           }
558           if (!jmolAvailable)
559           {
560             System.out
561                     .println("Jmol not available - Using MCview for structures");
562           }
563         } catch (java.lang.ClassNotFoundException ex)
564         {
565         }
566       }
567       else
568       {
569         jmolAvailable = false;
570         if (debug)
571         {
572           System.err
573                   .println("Skipping Jmol check. Will use MCView (probably)");
574         }
575       }
576       checkedForJmol=true;
577       running=false;
578     }
579
580     public boolean notFinished()
581     {
582       return running || !checkedForJmol;
583     }
584   }
585
586   class LoadingThread extends Thread
587   {
588     /**
589      * State variable: File source
590      */
591     String file;
592
593     /**
594      * State variable: protocol for access to file source
595      */
596     String protocol;
597
598     /**
599      * State variable: format of file source
600      */
601     String format;
602     String _file;
603     JalviewLite applet;
604     private void dbgMsg(String msg)
605     {
606       if (applet.debug)
607       {
608         System.err.println(msg);
609       }
610     }
611
612     /**
613      * update the protocol state variable for accessing the datasource located
614      * by file.
615      * 
616      * @param file
617      * @return possibly updated datasource string
618      */
619     public String setProtocolState(String file)
620     {
621       if (file.startsWith("PASTE"))
622       {
623         file = file.substring(5);
624         protocol = AppletFormatAdapter.PASTE;
625       }
626       else if (inArchive(file))
627       {
628         protocol = AppletFormatAdapter.CLASSLOADER;
629       }
630       else
631       {
632         file = addProtocol(file);
633         protocol = AppletFormatAdapter.URL;
634       }
635       dbgMsg("Protocol identified as '" + protocol + "'");
636       return file;
637     }
638     
639     public LoadingThread(String _file, JalviewLite _applet)
640     {
641       this._file=_file;
642       applet = _applet;
643     }
644
645     public void run()
646     {
647       LoadJmolThread jmolchecker = new LoadJmolThread();
648       jmolchecker.start();
649       while (jmolchecker.notFinished())
650       {
651         // wait around until the Jmol check is complete.
652         try { Thread.sleep(2); } catch (Exception e) {};
653       }
654       startLoading();
655     }
656
657     private void startLoading()
658     {
659       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
660       file = setProtocolState(_file);
661
662       format = new jalview.io.IdentifyFile().Identify(file, protocol);
663       dbgMsg("File identified as '" + format + "'");
664       dbgMsg("Loading started.");
665       Alignment al = null;
666       try
667       {
668         al = new AppletFormatAdapter().readFile(file, protocol, format);
669       } catch (java.io.IOException ex)
670       {
671         dbgMsg("File load exception.");
672         ex.printStackTrace();
673       }
674       if ((al != null) && (al.getHeight() > 0))
675       {
676         dbgMsg("Successfully loaded file.");
677         initialAlignFrame = new AlignFrame(al, applet, file, embedded);
678         // update the focus.
679         currentAlignFrame = initialAlignFrame;
680
681         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
682         {
683           currentAlignFrame.setTitle("Sequences from " + getDocumentBase());
684         }
685
686         currentAlignFrame.statusBar.setText("Successfully loaded file "
687                 + file);
688
689         String treeFile = applet.getParameter("tree");
690         if (treeFile == null)
691         {
692           treeFile = applet.getParameter("treeFile");
693         }
694
695         if (treeFile != null)
696         {
697           try
698           {
699             treeFile = setProtocolState(treeFile);
700             /*
701              * if (inArchive(treeFile)) { protocol =
702              * AppletFormatAdapter.CLASSLOADER; } else { protocol =
703              * AppletFormatAdapter.URL; treeFile = addProtocol(treeFile); }
704              */
705             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
706                     protocol);
707
708             fin.parse();
709
710             if (fin.getTree() != null)
711             {
712               currentAlignFrame.loadTree(fin, treeFile);
713               dbgMsg("Successfuly imported tree.");
714             }
715             else
716             {
717               dbgMsg("Tree parameter did not resolve to a valid tree.");
718             }
719           } catch (Exception ex)
720           {
721             ex.printStackTrace();
722           }
723         }
724
725         String param = getParameter("features");
726         if (param != null)
727         {
728           param = setProtocolState(param);
729
730           currentAlignFrame.parseFeaturesFile(param, protocol);
731         }
732
733         param = getParameter("showFeatureSettings");
734         if (param != null && param.equalsIgnoreCase("true"))
735         {
736           currentAlignFrame.viewport.showSequenceFeatures(true);
737           new FeatureSettings(currentAlignFrame.alignPanel);
738         }
739
740         param = getParameter("annotations");
741         if (param != null)
742         {
743           param = setProtocolState(param);
744
745           if (new AnnotationFile().readAnnotationFile(
746                   currentAlignFrame.viewport.getAlignment(), param,
747                   protocol))
748           {
749             currentAlignFrame.alignPanel.fontChanged();
750             currentAlignFrame.alignPanel.setScrollValues(0, 0);
751           }
752           else
753           {
754             System.err
755                     .println("Annotations were not added from annotation file '"
756                             + param + "'");
757           }
758
759         }
760
761         param = getParameter("jnetfile");
762         if (param != null)
763         {
764           try
765           {
766             param = setProtocolState(param);
767             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
768                     param, protocol);
769             JnetAnnotationMaker.add_annotation(predictions,
770                     currentAlignFrame.viewport.getAlignment(), 0, false); // false==do
771             // not
772             // add
773             // sequence
774             // profile
775             // from
776             // concise
777             // output
778             currentAlignFrame.alignPanel.fontChanged();
779             currentAlignFrame.alignPanel.setScrollValues(0, 0);
780           } catch (Exception ex)
781           {
782             ex.printStackTrace();
783           }
784         }
785
786         /*
787          * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
788          * PDB|1GAQ|1GAQ|C">
789          * 
790          * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
791          * 
792          * <param name="PDBfile3" value="1q0o Q45135_9MICO">
793          */
794
795         int pdbFileCount = 0;
796         do
797         {
798           if (pdbFileCount > 0)
799             param = getParameter("PDBFILE" + pdbFileCount);
800           else
801             param = getParameter("PDBFILE");
802
803           if (param != null)
804           {
805             PDBEntry pdb = new PDBEntry();
806
807             String seqstring;
808             SequenceI[] seqs = null;
809             String[] chains = null;
810
811             StringTokenizer st = new StringTokenizer(param, " ");
812
813             if (st.countTokens() < 2)
814             {
815               String sequence = applet.getParameter("PDBSEQ");
816               if (sequence != null)
817                 seqs = new SequenceI[]
818                 { (Sequence) currentAlignFrame.getAlignViewport()
819                         .getAlignment().findName(sequence) };
820
821             }
822             else
823             {
824               param = st.nextToken();
825               Vector tmp = new Vector();
826               Vector tmp2 = new Vector();
827
828               while (st.hasMoreTokens())
829               {
830                 seqstring = st.nextToken();
831                 StringTokenizer st2 = new StringTokenizer(seqstring, "=");
832                 if (st2.countTokens() > 1)
833                 {
834                   // This is the chain
835                   tmp2.addElement(st2.nextToken());
836                   seqstring = st2.nextToken();
837                 }
838                 tmp.addElement((Sequence) currentAlignFrame
839                         .getAlignViewport().getAlignment().findName(
840                                 seqstring));
841               }
842
843               seqs = new SequenceI[tmp.size()];
844               tmp.copyInto(seqs);
845               if (tmp2.size() == tmp.size())
846               {
847                 chains = new String[tmp2.size()];
848                 tmp2.copyInto(chains);
849               }
850             }
851             param = setProtocolState(param);
852
853             if (// !jmolAvailable
854             // &&
855             protocol == AppletFormatAdapter.CLASSLOADER)
856             {
857               // TODO: verify this Re:
858               // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
859               // This exception preserves the current behaviour where, even if
860               // the local pdb file was identified in the class loader
861               protocol = AppletFormatAdapter.URL; // this is probably NOT
862               // CORRECT!
863               param = addProtocol(param); // 
864             }
865
866             pdb.setFile(param);
867
868             if (seqs != null)
869             {
870               for (int i = 0; i < seqs.length; i++)
871               {
872                 if (seqs[i] != null)
873                 {
874                   ((Sequence) seqs[i]).addPDBId(pdb);
875                 }
876                 else
877                 {
878                   if (JalviewLite.debug)
879                   {
880                     // this may not really be a problem but we give a warning
881                     // anyway
882                     System.err
883                             .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
884                                     + i + ")");
885                   }
886                 }
887               }
888
889               if (jmolAvailable)
890               {
891                 new jalview.appletgui.AppletJmol(pdb, seqs, chains,
892                         currentAlignFrame.alignPanel, protocol);
893                 lastFrameX += 40;
894                 lastFrameY += 40;
895               }
896               else
897                 new MCview.AppletPDBViewer(pdb, seqs, chains,
898                         currentAlignFrame.alignPanel, protocol);
899             }
900           }
901
902           pdbFileCount++;
903         } while (pdbFileCount < 10);
904
905         // ///////////////////////////
906         // modify display of features
907         //
908         // hide specific groups
909         param = getParameter("hidefeaturegroups");
910         if (param != null)
911         {
912           applet.setFeatureGroupState(param, false);
913         }
914         // show specific groups
915         param = getParameter("showfeaturegroups");
916         if (param != null)
917         {
918           applet.setFeatureGroupState(param, true);
919         }
920       }
921       else
922       {
923         fileFound = false;
924         remove(launcher);
925         repaint();
926       }
927     }
928
929     /**
930      * Discovers whether the given file is in the Applet Archive
931      * 
932      * @param file
933      *                String
934      * @return boolean
935      */
936     boolean inArchive(String file)
937     {
938       // This might throw a security exception in certain browsers
939       // Netscape Communicator for instance.
940       try
941       {
942         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
943         if (debug)
944         {
945           System.err.println("Resource '" + file + "' was "
946                   + (rtn ? "" : "not") + " located by classloader.");
947         }
948         return rtn;
949       } catch (Exception ex)
950       {
951         System.out.println("Exception checking resources: " + file + " "
952                 + ex);
953         return false;
954       }
955     }
956
957     String addProtocol(String file)
958     {
959       if (file.indexOf("://") == -1)
960       {
961         file = getCodeBase() + file;
962         if (debug)
963         {
964           System.err.println("Prepended codebase for resource: '" + file
965                   + "'");
966         }
967       }
968
969       return file;
970     }
971   }
972
973   /**
974    * @return the default alignFrame acted on by the public applet methods. May
975    *         return null with an error message on System.err indicating the
976    *         fact.
977    */
978   protected AlignFrame getDefaultTargetFrame()
979   {
980     if (currentAlignFrame != null)
981     {
982       return currentAlignFrame;
983     }
984     if (initialAlignFrame != null)
985     {
986       return initialAlignFrame;
987     }
988     System.err
989             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
990     return null;
991   }
992
993   /**
994    * separator used for separatorList
995    */
996   protected String separator = "|"; // this is a safe(ish) separator - tabs
997
998   // don't work for firefox
999
1000   /**
1001    * parse the string into a list
1002    * 
1003    * @param list
1004    * @return elements separated by separator
1005    */
1006   public String[] separatorListToArray(String list)
1007   {
1008     int seplen = separator.length();
1009     if (list == null || list.equals(""))
1010       return null;
1011     java.util.Vector jv = new Vector();
1012     int cp = 0, pos;
1013     while ((pos = list.indexOf(separator, cp)) > cp)
1014     {
1015       jv.addElement(list.substring(cp, pos));
1016       cp = pos + seplen;
1017     }
1018     if (cp < list.length())
1019     {
1020       jv.addElement(list.substring(cp));
1021     }
1022     if (jv.size() > 0)
1023     {
1024       String[] v = new String[jv.size()];
1025       for (int i = 0; i < v.length; i++)
1026       {
1027         v[i] = (String) jv.elementAt(i);
1028       }
1029       jv.removeAllElements();
1030       if (debug)
1031       {
1032         System.err.println("Array from '" + separator
1033                 + "' separated List:\n" + v.length);
1034         for (int i = 0; i < v.length; i++)
1035         {
1036           System.err.println("item " + i + " '" + v[i] + "'");
1037         }
1038       }
1039       return v;
1040     }
1041     if (debug)
1042     {
1043       System.err.println("Empty Array from '" + separator
1044               + "' separated List");
1045     }
1046     return null;
1047   }
1048
1049   /**
1050    * concatenate the list with separator
1051    * 
1052    * @param list
1053    * @return concatenated string
1054    */
1055   public String arrayToSeparatorList(String[] list)
1056   {
1057     StringBuffer v = new StringBuffer();
1058     if (list != null)
1059     {
1060       for (int i = 0, iSize = list.length - 1; i < iSize; i++)
1061       {
1062         if (list[i] != null)
1063         {
1064           v.append(list[i]);
1065         }
1066         v.append(separator);
1067       }
1068       if (list[list.length - 1] != null)
1069       {
1070         v.append(list[list.length - 1]);
1071       }
1072       if (debug)
1073       {
1074         System.err.println("Returning '" + separator
1075                 + "' separated List:\n");
1076         System.err.println(v);
1077       }
1078       return v.toString();
1079     }
1080     if (debug)
1081     {
1082       System.err.println("Returning empty '" + separator
1083               + "' separated List\n");
1084     }
1085     return "";
1086   }
1087
1088   /**
1089    * @return
1090    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1091    */
1092   public String getFeatureGroups()
1093   {
1094     String lst = arrayToSeparatorList(getDefaultTargetFrame()
1095             .getFeatureGroups());
1096     return lst;
1097   }
1098
1099   /**
1100    * @param alf
1101    *                alignframe to get feature groups on
1102    * @return
1103    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1104    */
1105   public String getFeatureGroupsOn(AlignFrame alf)
1106   {
1107     String lst = arrayToSeparatorList(alf.getFeatureGroups());
1108     return lst;
1109   }
1110
1111   /**
1112    * @param visible
1113    * @return
1114    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1115    */
1116   public String getFeatureGroupsOfState(boolean visible)
1117   {
1118     return arrayToSeparatorList(getDefaultTargetFrame()
1119             .getFeatureGroupsOfState(visible));
1120   }
1121
1122   /**
1123    * @param alf
1124    *                align frame to get groups of state visible
1125    * @param visible
1126    * @return
1127    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1128    */
1129   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
1130   {
1131     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
1132   }
1133
1134   /**
1135    * @param groups
1136    *                tab separated list of group names
1137    * @param state
1138    *                true or false
1139    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[],
1140    *      boolean)
1141    */
1142   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
1143           boolean state)
1144   {
1145     boolean st = state;// !(state==null || state.equals("") ||
1146     // state.toLowerCase().equals("false"));
1147     alf.setFeatureGroupState(separatorListToArray(groups), st);
1148   }
1149
1150   public void setFeatureGroupState(String groups, boolean state)
1151   {
1152     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
1153   }
1154
1155   /**
1156    * List separator string
1157    * 
1158    * @return the separator
1159    */
1160   public String getSeparator()
1161   {
1162     return separator;
1163   }
1164
1165   /**
1166    * List separator string
1167    * 
1168    * @param separator
1169    *                the separator to set
1170    */
1171   public void setSeparator(String separator)
1172   {
1173     this.separator = separator;
1174   }
1175 }