43f7f1965191fd2e8d845038c03f2ad4abdeb9ae
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 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     
262     String format = new IdentifyFile().Identify(text,
263             AppletFormatAdapter.PASTE);
264     try
265     {
266       al = new AppletFormatAdapter().readFile(text,
267               AppletFormatAdapter.PASTE, format);
268       if (al.getHeight() > 0)
269       {
270         return new AlignFrame(al, this, title, false);
271       }
272     } catch (java.io.IOException ex)
273     {
274       ex.printStackTrace();
275     }
276     return null;
277   }
278
279   // //////////////////////////////////////////////
280   // //////////////////////////////////////////////
281
282   static int lastFrameX = 200;
283
284   static int lastFrameY = 200;
285
286   boolean fileFound = true;
287
288   String file = "No file";
289
290   Button launcher = new Button("Start Jalview");
291
292   /**
293    * The currentAlignFrame is static, it will change if and when the user
294    * selects a new window. Note that it will *never* point back to the embedded
295    * AlignFrame if the applet is started as embedded on the page and then
296    * afterwards a new view is created.
297    */
298   public static AlignFrame currentAlignFrame=null;
299
300   /**
301    * This is the first frame to be displayed, and does not change. API calls
302    * will default to this instance if currentAlignFrame is null.
303    */
304   AlignFrame initialAlignFrame=null;
305
306   boolean embedded = false;
307
308   private boolean checkForJmol = true;
309   private boolean checkedForJmol = false; // ensure we don't check for jmol every time the app is re-inited
310
311   public boolean jmolAvailable = false;
312
313   public static boolean debug=false;
314
315   /**
316    * init method for Jalview Applet
317    */
318   public void init()
319   {
320     
321     /**
322      * turn on extra applet debugging
323      */
324     String dbg = getParameter("debug");
325     if (dbg != null)
326     {
327       debug = dbg.toLowerCase().equals("true");
328     }
329     /**
330      * if true disable the check for jmol
331      */
332     String chkforJmol = getParameter("nojmol");
333     if (chkforJmol != null)
334     {
335       checkForJmol = !chkforJmol.equals("true");
336     }
337     /**
338      * get the separator parameter if present
339      */
340     String sep = getParameter("separator");
341     if (sep != null)
342     {
343       if (sep.length() > 0)
344       {
345         separator = sep;
346         if (debug)
347         {
348           System.err.println("Separator set to '" + separator + "'");
349         }
350       }
351       else
352       {
353         throw new Error(
354                 "Invalid separator parameter - must be non-zero length");
355       }
356     }
357     int r = 255;
358     int g = 255;
359     int b = 255;
360     String param = getParameter("RGB");
361
362     if (param != null)
363     {
364       try
365       {
366         r = Integer.parseInt(param.substring(0, 2), 16);
367         g = Integer.parseInt(param.substring(2, 4), 16);
368         b = Integer.parseInt(param.substring(4, 6), 16);
369       } catch (Exception ex)
370       {
371         r = 255;
372         g = 255;
373         b = 255;
374       }
375     }
376
377     param = getParameter("label");
378     if (param != null)
379     {
380       launcher.setLabel(param);
381     }
382
383     this.setBackground(new Color(r, g, b));
384
385     file = getParameter("file");
386
387     if (file == null)
388     {
389       // Maybe the sequences are added as parameters
390       StringBuffer data = new StringBuffer("PASTE");
391       int i = 1;
392       while ((file = getParameter("sequence" + i)) != null)
393       {
394         data.append(file.toString() + "\n");
395         i++;
396       }
397       if (data.length() > 5)
398       {
399         file = data.toString();
400       }
401     }
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     private boolean running=false;
544
545     public void run()
546     {
547       if (running || checkedForJmol) {
548         return;
549       }
550       running=true;
551       if (checkForJmol)
552       {
553         try
554         {
555           if (!System.getProperty("java.version").startsWith("1.1"))
556           {
557             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
558             jmolAvailable = true;
559           }
560           if (!jmolAvailable)
561           {
562             System.out
563                     .println("Jmol not available - Using MCview for structures");
564           }
565         } catch (java.lang.ClassNotFoundException ex)
566         {
567         }
568       }
569       else
570       {
571         jmolAvailable = false;
572         if (debug)
573         {
574           System.err
575                   .println("Skipping Jmol check. Will use MCView (probably)");
576         }
577       }
578       checkedForJmol=true;
579       running=false;
580     }
581
582     public boolean notFinished()
583     {
584       return running || !checkedForJmol;
585     }
586   }
587
588   class LoadingThread extends Thread
589   {
590     /**
591      * State variable: File source
592      */
593     String file;
594
595     /**
596      * State variable: protocol for access to file source
597      */
598     String protocol;
599
600     /**
601      * State variable: format of file source
602      */
603     String format;
604     String _file;
605     JalviewLite applet;
606     private void dbgMsg(String msg)
607     {
608       if (applet.debug)
609       {
610         System.err.println(msg);
611       }
612     }
613
614     /**
615      * update the protocol state variable for accessing the datasource located
616      * by file.
617      * 
618      * @param file
619      * @return possibly updated datasource string
620      */
621     public String setProtocolState(String file)
622     {
623       if (file.startsWith("PASTE"))
624       {
625         file = file.substring(5);
626         protocol = AppletFormatAdapter.PASTE;
627       }
628       else if (inArchive(file))
629       {
630         protocol = AppletFormatAdapter.CLASSLOADER;
631       }
632       else
633       {
634         file = addProtocol(file);
635         protocol = AppletFormatAdapter.URL;
636       }
637       dbgMsg("Protocol identified as '" + protocol + "'");
638       return file;
639     }
640     
641     public LoadingThread(String _file, JalviewLite _applet)
642     {
643       this._file=_file;
644       applet = _applet;
645     }
646
647     public void run()
648     {
649       LoadJmolThread jmolchecker = new LoadJmolThread();
650       jmolchecker.start();
651       while (jmolchecker.notFinished())
652       {
653         // wait around until the Jmol check is complete.
654         try { Thread.sleep(2); } catch (Exception e) {};
655       }
656       startLoading();
657     }
658
659     private void startLoading()
660     {
661       AlignFrame newAlignFrame;
662       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
663       file = setProtocolState(_file);
664
665       format = new jalview.io.IdentifyFile().Identify(file, protocol);
666       dbgMsg("File identified as '" + format + "'");
667       dbgMsg("Loading started.");
668       Alignment al = null;
669       try
670       {
671         al = new AppletFormatAdapter().readFile(file, protocol, format);
672       } catch (java.io.IOException ex)
673       {
674         dbgMsg("File load exception.");
675         ex.printStackTrace();
676         if (debug) {
677           try {
678             FileParse fp = new FileParse(file, protocol);
679             String ln = null;
680             dbgMsg(">>>Dumping contents of '"+file+"' "+"("+protocol+")");
681             while ((ln=fp.nextLine())!=null) {
682               dbgMsg(ln);
683             }
684             dbgMsg(">>>Dump finished.");
685           } catch (Exception e)
686           {
687             System.err.println("Exception when trying to dump the content of the file parameter.");
688             e.printStackTrace();
689           }
690         }
691       }
692       if ((al != null) && (al.getHeight() > 0))
693       {
694         dbgMsg("Successfully loaded file.");
695         newAlignFrame = new AlignFrame(al, applet, file, embedded);
696         if (initialAlignFrame==null)
697         {
698           initialAlignFrame = newAlignFrame;
699         }
700         // update the focus.
701         currentAlignFrame = newAlignFrame;
702
703         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
704         {
705           newAlignFrame.setTitle("Sequences from " + getDocumentBase());
706         }
707
708         newAlignFrame.statusBar.setText("Successfully loaded file "
709                 + file);
710         
711         String treeFile = applet.getParameter("tree");
712         if (treeFile == null)
713         {
714           treeFile = applet.getParameter("treeFile");
715         }
716
717         if (treeFile != null)
718         {
719           try
720           {
721             treeFile = setProtocolState(treeFile);
722             /*
723              * if (inArchive(treeFile)) { protocol =
724              * AppletFormatAdapter.CLASSLOADER; } else { protocol =
725              * AppletFormatAdapter.URL; treeFile = addProtocol(treeFile); }
726              */
727             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
728                     protocol);
729
730             fin.parse();
731
732             if (fin.getTree() != null)
733             {
734               newAlignFrame.loadTree(fin, treeFile);
735               dbgMsg("Successfuly imported tree.");
736             }
737             else
738             {
739               dbgMsg("Tree parameter did not resolve to a valid tree.");
740             }
741           } catch (Exception ex)
742           {
743             ex.printStackTrace();
744           }
745         }
746
747         String param = getParameter("features");
748         if (param != null)
749         {
750           param = setProtocolState(param);
751
752           newAlignFrame.parseFeaturesFile(param, protocol);
753         }
754
755         param = getParameter("showFeatureSettings");
756         if (param != null && param.equalsIgnoreCase("true"))
757         {
758           newAlignFrame.viewport.showSequenceFeatures(true);
759           new FeatureSettings(newAlignFrame.alignPanel);
760         }
761
762         param = getParameter("annotations");
763         if (param != null)
764         {
765           param = setProtocolState(param);
766
767           if (new AnnotationFile().readAnnotationFile(
768                   newAlignFrame.viewport.getAlignment(), param,
769                   protocol))
770           {
771             newAlignFrame.alignPanel.fontChanged();
772             newAlignFrame.alignPanel.setScrollValues(0, 0);
773           }
774           else
775           {
776             System.err
777                     .println("Annotations were not added from annotation file '"
778                             + param + "'");
779           }
780
781         }
782
783         param = getParameter("jnetfile");
784         if (param != null)
785         {
786           try
787           {
788             param = setProtocolState(param);
789             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
790                     param, protocol);
791             JnetAnnotationMaker.add_annotation(predictions,
792                     newAlignFrame.viewport.getAlignment(), 0, false); // false==do
793             // not
794             // add
795             // sequence
796             // profile
797             // from
798             // concise
799             // output
800             newAlignFrame.alignPanel.fontChanged();
801             newAlignFrame.alignPanel.setScrollValues(0, 0);
802           } catch (Exception ex)
803           {
804             ex.printStackTrace();
805           }
806         }
807
808         /*
809          * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
810          * PDB|1GAQ|1GAQ|C">
811          * 
812          * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
813          * 
814          * <param name="PDBfile3" value="1q0o Q45135_9MICO">
815          */
816
817         int pdbFileCount = 0;
818         do
819         {
820           if (pdbFileCount > 0)
821             param = getParameter("PDBFILE" + pdbFileCount);
822           else
823             param = getParameter("PDBFILE");
824
825           if (param != null)
826           {
827             PDBEntry pdb = new PDBEntry();
828
829             String seqstring;
830             SequenceI[] seqs = null;
831             String[] chains = null;
832
833             StringTokenizer st = new StringTokenizer(param, " ");
834
835             if (st.countTokens() < 2)
836             {
837               String sequence = applet.getParameter("PDBSEQ");
838               if (sequence != null)
839                 seqs = new SequenceI[]
840                 { (Sequence) newAlignFrame.getAlignViewport()
841                         .getAlignment().findName(sequence) };
842
843             }
844             else
845             {
846               param = st.nextToken();
847               Vector tmp = new Vector();
848               Vector tmp2 = new Vector();
849
850               while (st.hasMoreTokens())
851               {
852                 seqstring = st.nextToken();
853                 StringTokenizer st2 = new StringTokenizer(seqstring, "=");
854                 if (st2.countTokens() > 1)
855                 {
856                   // This is the chain
857                   tmp2.addElement(st2.nextToken());
858                   seqstring = st2.nextToken();
859                 }
860                 tmp.addElement((Sequence) newAlignFrame
861                         .getAlignViewport().getAlignment().findName(
862                                 seqstring));
863               }
864
865               seqs = new SequenceI[tmp.size()];
866               tmp.copyInto(seqs);
867               if (tmp2.size() == tmp.size())
868               {
869                 chains = new String[tmp2.size()];
870                 tmp2.copyInto(chains);
871               }
872             }
873             param = setProtocolState(param);
874
875             if (// !jmolAvailable
876             // &&
877             protocol == AppletFormatAdapter.CLASSLOADER)
878             {
879               // TODO: verify this Re:
880               // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
881               // This exception preserves the current behaviour where, even if
882               // the local pdb file was identified in the class loader
883               protocol = AppletFormatAdapter.URL; // this is probably NOT
884               // CORRECT!
885               param = addProtocol(param); // 
886             }
887
888             pdb.setFile(param);
889
890             if (seqs != null)
891             {
892               for (int i = 0; i < seqs.length; i++)
893               {
894                 if (seqs[i] != null)
895                 {
896                   ((Sequence) seqs[i]).addPDBId(pdb);
897                 }
898                 else
899                 {
900                   if (JalviewLite.debug)
901                   {
902                     // this may not really be a problem but we give a warning
903                     // anyway
904                     System.err
905                             .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
906                                     + i + ")");
907                   }
908                 }
909               }
910
911               if (jmolAvailable)
912               {
913                 new jalview.appletgui.AppletJmol(pdb, seqs, chains,
914                         newAlignFrame.alignPanel, protocol);
915                 lastFrameX += 40;
916                 lastFrameY += 40;
917               }
918               else
919                 new MCview.AppletPDBViewer(pdb, seqs, chains,
920                         newAlignFrame.alignPanel, protocol);
921             }
922           }
923
924           pdbFileCount++;
925         } while (pdbFileCount < 10);
926
927         // ///////////////////////////
928         // modify display of features
929         //
930         // hide specific groups
931         param = getParameter("hidefeaturegroups");
932         if (param != null)
933         {
934           applet.setFeatureGroupStateOn(newAlignFrame,param, false);
935         }
936         // show specific groups
937         param = getParameter("showfeaturegroups");
938         if (param != null)
939         {
940           applet.setFeatureGroupStateOn(newAlignFrame,param, true);
941         }
942       }
943       else
944       {
945         fileFound = false;
946         remove(launcher);
947         repaint();
948       }
949     }
950
951     /**
952      * Discovers whether the given file is in the Applet Archive
953      * 
954      * @param file
955      *                String
956      * @return boolean
957      */
958     boolean inArchive(String file)
959     {
960       // This might throw a security exception in certain browsers
961       // Netscape Communicator for instance.
962       try
963       {
964         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
965         if (debug)
966         {
967           System.err.println("Resource '" + file + "' was "
968                   + (rtn ? "" : "not") + " located by classloader.");
969         }
970         return rtn;
971       } catch (Exception ex)
972       {
973         System.out.println("Exception checking resources: " + file + " "
974                 + ex);
975         return false;
976       }
977     }
978
979     String addProtocol(String file)
980     {
981       if (file.indexOf("://") == -1)
982       {
983         file = getCodeBase() + file;
984         if (debug)
985         {
986           System.err.println("Prepended codebase for resource: '" + file
987                   + "'");
988         }
989       }
990
991       return file;
992     }
993   }
994
995   /**
996    * @return the default alignFrame acted on by the public applet methods. May
997    *         return null with an error message on System.err indicating the
998    *         fact.
999    */
1000   protected AlignFrame getDefaultTargetFrame()
1001   {
1002     if (currentAlignFrame != null)
1003     {
1004       return currentAlignFrame;
1005     }
1006     if (initialAlignFrame != null)
1007     {
1008       return initialAlignFrame;
1009     }
1010     System.err
1011             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
1012     return null;
1013   }
1014
1015   /**
1016    * separator used for separatorList
1017    */
1018   protected String separator = "|"; // this is a safe(ish) separator - tabs
1019
1020   // don't work for firefox
1021
1022   /**
1023    * parse the string into a list
1024    * 
1025    * @param list
1026    * @return elements separated by separator
1027    */
1028   public String[] separatorListToArray(String list)
1029   {
1030     int seplen = separator.length();
1031     if (list == null || list.equals(""))
1032       return null;
1033     java.util.Vector jv = new Vector();
1034     int cp = 0, pos;
1035     while ((pos = list.indexOf(separator, cp)) > cp)
1036     {
1037       jv.addElement(list.substring(cp, pos));
1038       cp = pos + seplen;
1039     }
1040     if (cp < list.length())
1041     {
1042       jv.addElement(list.substring(cp));
1043     }
1044     if (jv.size() > 0)
1045     {
1046       String[] v = new String[jv.size()];
1047       for (int i = 0; i < v.length; i++)
1048       {
1049         v[i] = (String) jv.elementAt(i);
1050       }
1051       jv.removeAllElements();
1052       if (debug)
1053       {
1054         System.err.println("Array from '" + separator
1055                 + "' separated List:\n" + v.length);
1056         for (int i = 0; i < v.length; i++)
1057         {
1058           System.err.println("item " + i + " '" + v[i] + "'");
1059         }
1060       }
1061       return v;
1062     }
1063     if (debug)
1064     {
1065       System.err.println("Empty Array from '" + separator
1066               + "' separated List");
1067     }
1068     return null;
1069   }
1070
1071   /**
1072    * concatenate the list with separator
1073    * 
1074    * @param list
1075    * @return concatenated string
1076    */
1077   public String arrayToSeparatorList(String[] list)
1078   {
1079     StringBuffer v = new StringBuffer();
1080     if (list != null)
1081     {
1082       for (int i = 0, iSize = list.length - 1; i < iSize; i++)
1083       {
1084         if (list[i] != null)
1085         {
1086           v.append(list[i]);
1087         }
1088         v.append(separator);
1089       }
1090       if (list[list.length - 1] != null)
1091       {
1092         v.append(list[list.length - 1]);
1093       }
1094       if (debug)
1095       {
1096         System.err.println("Returning '" + separator
1097                 + "' separated List:\n");
1098         System.err.println(v);
1099       }
1100       return v.toString();
1101     }
1102     if (debug)
1103     {
1104       System.err.println("Returning empty '" + separator
1105               + "' separated List\n");
1106     }
1107     return "";
1108   }
1109
1110   /**
1111    * @return
1112    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1113    */
1114   public String getFeatureGroups()
1115   {
1116     String lst = arrayToSeparatorList(getDefaultTargetFrame()
1117             .getFeatureGroups());
1118     return lst;
1119   }
1120
1121   /**
1122    * @param alf
1123    *                alignframe to get feature groups on
1124    * @return
1125    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1126    */
1127   public String getFeatureGroupsOn(AlignFrame alf)
1128   {
1129     String lst = arrayToSeparatorList(alf.getFeatureGroups());
1130     return lst;
1131   }
1132
1133   /**
1134    * @param visible
1135    * @return
1136    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1137    */
1138   public String getFeatureGroupsOfState(boolean visible)
1139   {
1140     return arrayToSeparatorList(getDefaultTargetFrame()
1141             .getFeatureGroupsOfState(visible));
1142   }
1143
1144   /**
1145    * @param alf
1146    *                align frame to get groups of state visible
1147    * @param visible
1148    * @return
1149    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1150    */
1151   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
1152   {
1153     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
1154   }
1155
1156   /**
1157    * @param groups
1158    *                tab separated list of group names
1159    * @param state
1160    *                true or false
1161    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[],
1162    *      boolean)
1163    */
1164   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
1165           boolean state)
1166   {
1167     boolean st = state;// !(state==null || state.equals("") ||
1168     // state.toLowerCase().equals("false"));
1169     alf.setFeatureGroupState(separatorListToArray(groups), st);
1170   }
1171
1172   public void setFeatureGroupState(String groups, boolean state)
1173   {
1174     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
1175   }
1176
1177   /**
1178    * List separator string
1179    * 
1180    * @return the separator
1181    */
1182   public String getSeparator()
1183   {
1184     return separator;
1185   }
1186
1187   /**
1188    * List separator string
1189    * 
1190    * @param separator
1191    *                the separator to set
1192    */
1193   public void setSeparator(String separator)
1194   {
1195     this.separator = separator;
1196   }
1197
1198   /**
1199    * get boolean value of applet parameter 'name' and return default if parameter is not set 
1200    * @param name name of paremeter
1201    * @param def the value to return otherwise
1202    * @return true or false
1203    */
1204   public boolean getDefaultParameter(String name, boolean def)
1205   {
1206     String stn;
1207    if ((stn=getParameter(name)) == null)
1208    {
1209      return def;
1210    }
1211    if (stn.toLowerCase().equals("true"))
1212    {
1213      return true;
1214    }
1215    return false;
1216   }
1217 }