JAL-621 - also renamed list of javascript handlers
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.bin;
19
20 import jalview.appletgui.AlignFrame;
21 import jalview.appletgui.EmbmenuFrame;
22 import jalview.appletgui.FeatureSettings;
23 import jalview.datamodel.Alignment;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.AlignmentOrder;
26 import jalview.datamodel.ColumnSelection;
27 import jalview.datamodel.PDBEntry;
28 import jalview.datamodel.Sequence;
29 import jalview.datamodel.SequenceGroup;
30 import jalview.datamodel.SequenceI;
31 import jalview.io.AnnotationFile;
32 import jalview.io.AppletFormatAdapter;
33 import jalview.io.FileParse;
34 import jalview.io.IdentifyFile;
35 import jalview.io.JnetAnnotationMaker;
36 import jalview.javascript.JSFunctionExec;
37 import jalview.javascript.JsCallBack;
38 import jalview.structure.SelectionListener;
39 import jalview.structure.StructureSelectionManager;
40
41 import java.applet.Applet;
42 import java.awt.Button;
43 import java.awt.Color;
44 import java.awt.Component;
45 import java.awt.Font;
46 import java.awt.Frame;
47 import java.awt.Graphics;
48 import java.awt.event.ActionEvent;
49 import java.awt.event.WindowAdapter;
50 import java.awt.event.WindowEvent;
51 import java.io.BufferedReader;
52 import java.io.InputStreamReader;
53 import java.util.StringTokenizer;
54 import java.util.Vector;
55
56 import netscape.javascript.JSObject;
57
58 /**
59  * Jalview Applet. Runs in Java 1.18 runtime
60  * 
61  * @author $author$
62  * @version $Revision$
63  */
64 public class JalviewLite extends Applet
65 {
66
67   // /////////////////////////////////////////
68   // The following public methods maybe called
69   // externally, eg via javascript in HTML page
70   /**
71    * @return String list of selected sequence IDs, each terminated by the
72    *         'boolean not' character (""+0x00AC) or (&#172;)
73    */
74   public String getSelectedSequences()
75   {
76     return getSelectedSequencesFrom(getDefaultTargetFrame());
77   }
78
79   /**
80    * @param sep
81    *          separator string or null for default
82    * @return String list of selected sequence IDs, each terminated by given
83    *         separator string
84    */
85   public String getSelectedSequences(String sep)
86   {
87     return getSelectedSequencesFrom(getDefaultTargetFrame(), sep);
88   }
89
90   /**
91    * @param alf
92    *          alignframe containing selection
93    * @return String list of selected sequence IDs, each terminated by current
94    *         default separator sequence
95    * 
96    */
97   public String getSelectedSequencesFrom(AlignFrame alf)
98   {
99     return getSelectedSequencesFrom(alf, separator); // ""+0x00AC);
100   }
101
102   /**
103    * get list of selected sequence IDs separated by given separator
104    * 
105    * @param alf
106    *          window containing selection
107    * @param sep
108    *          separator string to use - default is 'boolean not'
109    * @return String list of selected sequence IDs, each terminated by the given
110    *         separator
111    */
112   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
113   {
114     StringBuffer result = new StringBuffer("");
115     if (sep == null || sep.length() == 0)
116     {
117       sep = separator; // "+0x00AC;
118     }
119     if (alf.viewport.getSelectionGroup() != null)
120     {
121       SequenceI[] seqs = alf.viewport.getSelectionGroup()
122               .getSequencesInOrder(alf.viewport.getAlignment());
123
124       for (int i = 0; i < seqs.length; i++)
125       {
126         result.append(seqs[i].getName());
127         result.append(sep);
128       }
129     }
130
131     return result.toString();
132   }
133
134   /**
135    * 
136    * @param sequenceId
137    *          id of sequence to highlight
138    * @param position
139    *          integer position [ tobe implemented or range ] on sequence
140    * @param alignedPosition
141    *          true/false/empty string - indicate if position is an alignment
142    *          column or unaligned sequence position
143    */
144   public void highlight(String sequenceId, String position,
145           String alignedPosition)
146   {
147     highlightIn(getDefaultTargetFrame(), sequenceId, position,
148             alignedPosition);
149   }
150
151   /**
152    * 
153    * @param sequenceId
154    *          id of sequence to highlight
155    * @param position
156    *          integer position [ tobe implemented or range ] on sequence
157    * @param alignedPosition
158    *          false, blank or something else - indicate if position is an
159    *          alignment column or unaligned sequence position
160    */
161   public void highlightIn(AlignFrame alf, String sequenceId,
162           String position, String alignedPosition)
163   {
164     // TODO: could try to highlight in all alignments if alf==null
165     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
166             alf.viewport.getAlignment().getSequencesArray());
167     SequenceI sq = matcher.findIdMatch(sequenceId);
168     if (sq != null)
169     {
170       int pos, apos = -1;
171       try
172       {
173         apos = new Integer(position).intValue();
174         apos--;
175       } catch (NumberFormatException ex)
176       {
177         return;
178       }
179       // use vamsas listener to broadcast to all listeners in scope
180       if (alignedPosition != null
181               && (alignedPosition.trim().length() == 0 || alignedPosition
182                       .toLowerCase().indexOf("false") > -1))
183       {
184         StructureSelectionManager.getStructureSelectionManager()
185                 .mouseOverVamsasSequence(sq, sq.findIndex(apos), null);
186       }
187       else
188       {
189         StructureSelectionManager.getStructureSelectionManager()
190                 .mouseOverVamsasSequence(sq, apos, null);
191       }
192
193     }
194   }
195
196   /**
197    * select regions of the currrent alignment frame
198    * 
199    * @param sequenceIds
200    *          String separated list of sequence ids or empty string
201    * @param columns
202    *          String separated list { column range or column, ..} or empty
203    *          string
204    */
205   public void select(String sequenceIds, String columns)
206   {
207     selectIn(getDefaultTargetFrame(), sequenceIds, columns, separator);
208   }
209
210   /**
211    * select regions of the currrent alignment frame
212    * 
213    * @param toselect
214    *          String separated list { column range, seq1...seqn sequence ids }
215    * @param sep
216    *          separator between toselect fields
217    */
218   public void select(String sequenceIds, String columns, String sep)
219   {
220     selectIn(getDefaultTargetFrame(), sequenceIds, columns, sep);
221   }
222
223   /**
224    * select regions of the given alignment frame
225    * 
226    * @param alf
227    * @param toselect
228    *          String separated list { column range, seq1...seqn sequence ids }
229    * @param sep
230    *          separator between toselect fields
231    */
232   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
233   {
234     selectIn(alf, sequenceIds, columns, separator);
235   }
236
237   /**
238    * select regions of the given alignment frame
239    * 
240    * @param alf
241    * @param toselect
242    *          String separated list { column range, seq1...seqn sequence ids }
243    * @param sep
244    *          separator between toselect fields
245    */
246   public void selectIn(AlignFrame alf, String sequenceIds, String columns,
247           String sep)
248   {
249     if (sep == null || sep.length() == 0)
250     {
251       sep = separator;
252     }
253     else
254     {
255       if (debug)
256       {
257         System.err.println("Selecting region using separator string '"
258                 + separator + "'");
259       }
260     }
261     // deparse fields
262     String[] ids = separatorListToArray(sequenceIds, sep);
263     String[] cols = separatorListToArray(columns, sep);
264     SequenceGroup sel = new SequenceGroup();
265     ColumnSelection csel = new ColumnSelection();
266     AlignmentI al = alf.viewport.getAlignment();
267     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
268             alf.viewport.getAlignment().getSequencesArray());
269     int start = 0, end = al.getWidth(), alw = al.getWidth();
270     boolean seqsfound = true;
271     if (ids != null && ids.length > 0)
272     {
273       seqsfound = false;
274       for (int i = 0; i < ids.length; i++)
275       {
276         if (ids[i].trim().length() == 0)
277         {
278           continue;
279         }
280         SequenceI sq = matcher.findIdMatch(ids[i]);
281         if (sq != null)
282         {
283           seqsfound = true;
284           sel.addSequence(sq, false);
285         }
286       }
287     }
288     boolean inseqpos = false;
289     if (cols != null && cols.length > 0)
290     {
291       boolean seset = false;
292       for (int i = 0; i < cols.length; i++)
293       {
294         String cl = cols[i].trim();
295         if (cl.length() == 0)
296         {
297           continue;
298         }
299         int p;
300         if ((p = cl.indexOf("-")) > -1)
301         {
302           int from = -1, to = -1;
303           try
304           {
305             from = new Integer(cl.substring(0, p)).intValue();
306             from--;
307           } catch (NumberFormatException ex)
308           {
309             System.err
310                     .println("ERROR: Couldn't parse first integer in range element column selection string '"
311                             + cl + "' - format is 'from-to'");
312             return;
313           }
314           try
315           {
316             to = new Integer(cl.substring(p + 1)).intValue();
317             to--;
318           } catch (NumberFormatException ex)
319           {
320             System.err
321                     .println("ERROR: Couldn't parse second integer in range element column selection string '"
322                             + cl + "' - format is 'from-to'");
323             return;
324           }
325           if (from >= 0 && to >= 0)
326           {
327             // valid range
328             if (from < to)
329             {
330               int t = to;
331               to = from;
332               to = t;
333             }
334             if (!seset)
335             {
336               start = from;
337               end = to;
338               seset = true;
339             }
340             else
341             {
342               // comment to prevent range extension
343               if (start > from)
344               {
345                 start = from;
346               }
347               if (end < to)
348               {
349                 end = to;
350               }
351             }
352             for (int r = from; r <= to; r++)
353             {
354               if (r >= 0 && r < alw)
355               {
356                 csel.addElement(r);
357               }
358             }
359             if (debug)
360             {
361               System.err.println("Range '" + cl + "' deparsed as [" + from
362                       + "," + to + "]");
363             }
364           }
365           else
366           {
367             System.err.println("ERROR: Invalid Range '" + cl
368                     + "' deparsed as [" + from + "," + to + "]");
369           }
370         }
371         else
372         {
373           int r = -1;
374           try
375           {
376             r = new Integer(cl).intValue();
377             r--;
378           } catch (NumberFormatException ex)
379           {
380             if (cl.toLowerCase().equals("sequence"))
381             {
382               // we are in the dataset sequence's coordinate frame.
383               inseqpos = true;
384             }
385             else
386             {
387               System.err
388                       .println("ERROR: Couldn't parse integer from point selection element of column selection string '"
389                               + cl + "'");
390               return;
391             }
392           }
393           if (r >= 0 && r <= alw)
394           {
395             if (!seset)
396             {
397               start = r;
398               end = r;
399               seset = true;
400             }
401             else
402             {
403               // comment to prevent range extension
404               if (start > r)
405               {
406                 start = r;
407               }
408               if (end < r)
409               {
410                 end = r;
411               }
412             }
413             csel.addElement(r);
414             if (debug)
415             {
416               System.err.println("Point selection '" + cl
417                       + "' deparsed as [" + r + "]");
418             }
419           }
420           else
421           {
422             System.err.println("ERROR: Invalid Point selection '" + cl
423                     + "' deparsed as [" + r + "]");
424           }
425         }
426       }
427     }
428     if (seqsfound)
429     {
430       // we only propagate the selection when it was the null selection, or the
431       // given sequences were found in the alignment.
432       if (inseqpos && sel.getSize() > 0)
433       {
434         // assume first sequence provides reference frame ?
435         SequenceI rs = sel.getSequenceAt(0);
436         start = rs.findIndex(start);
437         end = rs.findIndex(end);
438         if (csel != null)
439         {
440           Vector cs = csel.getSelected();
441           csel.clear();
442           for (int csi = 0, csiS = cs.size(); csi < csiS; csi++)
443           {
444             csel.addElement(rs.findIndex(((Integer) cs.elementAt(csi))
445                     .intValue()));
446           }
447         }
448       }
449       sel.setStartRes(start);
450       sel.setEndRes(end);
451       alf.select(sel, csel);
452     }
453   }
454
455   /**
456    * get sequences selected in current alignFrame and return their alignment in
457    * format 'format' either with or without suffix
458    * 
459    * @param alf
460    *          - where selection is
461    * @param format
462    *          - format of alignment file
463    * @param suffix
464    *          - "true" to append /start-end string to each sequence ID
465    * @return selected sequences as flat file or empty string if there was no
466    *         current selection
467    */
468   public String getSelectedSequencesAsAlignment(String format, String suffix)
469   {
470     return getSelectedSequencesAsAlignmentFrom(getDefaultTargetFrame(),
471             format, suffix);
472   }
473
474   /**
475    * get sequences selected in alf and return their alignment in format 'format'
476    * either with or without suffix
477    * 
478    * @param alf
479    *          - where selection is
480    * @param format
481    *          - format of alignment file
482    * @param suffix
483    *          - "true" to append /start-end string to each sequence ID
484    * @return selected sequences as flat file or empty string if there was no
485    *         current selection
486    */
487   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
488           String format, String suffix)
489   {
490     try
491     {
492       boolean seqlimits = suffix.equalsIgnoreCase("true");
493       if (alf.viewport.getSelectionGroup() != null)
494       {
495         String reply = new AppletFormatAdapter().formatSequences(format,
496                 new Alignment(alf.viewport.getSelectionAsNewSequence()),
497                 seqlimits);
498         return reply;
499       }
500     } catch (Exception ex)
501     {
502       ex.printStackTrace();
503       return "Error retrieving alignment in " + format + " format. ";
504     }
505     return "";
506   }
507
508   public String getAlignmentOrder()
509   {
510     return getAlignmentOrderFrom(getDefaultTargetFrame());
511   }
512
513   public String getAlignmentOrderFrom(AlignFrame alf)
514   {
515     return getAlignmentOrderFrom(alf, separator);
516   }
517
518   public String getAlignmentOrderFrom(AlignFrame alf, String sep)
519   {
520     AlignmentI alorder = alf.getAlignViewport().getAlignment();
521     String[] order = new String[alorder.getHeight()];
522     for (int i = 0; i < order.length; i++)
523     {
524       order[i] = alorder.getSequenceAt(i).getName();
525     }
526     return arrayToSeparatorList(order);
527   }
528
529   public String orderBy(String order, String undoName)
530   {
531     return orderBy(order, undoName, separator);
532   }
533
534   public String orderBy(String order, String undoName, String sep)
535   {
536     return orderAlignmentBy(getDefaultTargetFrame(), order, undoName, sep);
537   }
538
539   public String orderAlignmentBy(AlignFrame alf, String order,
540           String undoName, String sep)
541   {
542     String[] ids = separatorListToArray(order, sep);
543     SequenceI[] sqs = null;
544     if (ids != null && ids.length > 0)
545     {
546       jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
547               alf.viewport.getAlignment().getSequencesArray());
548       int s = 0;
549       sqs = new SequenceI[ids.length];
550       for (int i = 0; i < ids.length; i++)
551       {
552         if (ids[i].trim().length() == 0)
553         {
554           continue;
555         }
556         SequenceI sq = matcher.findIdMatch(ids[i]);
557         if (sq != null)
558         {
559           sqs[s++] = sq;
560         }
561       }
562       if (s > 0)
563       {
564         SequenceI[] sqq = new SequenceI[s];
565         System.arraycopy(sqs, 0, sqq, 0, s);
566         sqs = sqq;
567       }
568       else
569       {
570         sqs = null;
571       }
572     }
573     if (sqs == null)
574     {
575       return "";
576     }
577     ;
578     AlignmentOrder aorder = new AlignmentOrder(sqs);
579
580     if (undoName != null && undoName.trim().length() == 0)
581     {
582       undoName = null;
583     }
584
585     return alf.sortBy(aorder, undoName) ? "true" : "";
586   }
587
588   public String getAlignment(String format)
589   {
590     return getAlignmentFrom(getDefaultTargetFrame(), format, "true");
591   }
592
593   public String getAlignmentFrom(AlignFrame alf, String format)
594   {
595     return getAlignmentFrom(alf, format, "true");
596   }
597
598   public String getAlignment(String format, String suffix)
599   {
600     return getAlignmentFrom(getDefaultTargetFrame(), format, suffix);
601   }
602
603   public String getAlignmentFrom(AlignFrame alf, String format,
604           String suffix)
605   {
606     try
607     {
608       boolean seqlimits = suffix.equalsIgnoreCase("true");
609
610       String reply = new AppletFormatAdapter().formatSequences(format,
611               alf.viewport.getAlignment(), seqlimits);
612       return reply;
613     } catch (Exception ex)
614     {
615       ex.printStackTrace();
616       return "Error retrieving alignment in " + format + " format. ";
617     }
618   }
619
620   public void loadAnnotation(String annotation)
621   {
622     loadAnnotationFrom(getDefaultTargetFrame(), annotation);
623   }
624
625   public void loadAnnotationFrom(AlignFrame alf, String annotation)
626   {
627     if (new AnnotationFile().readAnnotationFile(alf.getAlignViewport()
628             .getAlignment(), annotation, AppletFormatAdapter.PASTE))
629     {
630       alf.alignPanel.fontChanged();
631       alf.alignPanel.setScrollValues(0, 0);
632     }
633     else
634     {
635       alf.parseFeaturesFile(annotation, AppletFormatAdapter.PASTE);
636     }
637   }
638
639   public String getFeatures(String format)
640   {
641     return getFeaturesFrom(getDefaultTargetFrame(), format);
642   }
643
644   public String getFeaturesFrom(AlignFrame alf, String format)
645   {
646     return alf.outputFeatures(false, format);
647   }
648
649   public String getAnnotation()
650   {
651     return getAnnotationFrom(getDefaultTargetFrame());
652   }
653
654   public String getAnnotationFrom(AlignFrame alf)
655   {
656     return alf.outputAnnotations(false);
657   }
658
659   public AlignFrame newView()
660   {
661     return newViewFrom(getDefaultTargetFrame());
662   }
663
664   public AlignFrame newView(String name)
665   {
666     return newViewFrom(getDefaultTargetFrame(), name);
667   }
668
669   public AlignFrame newViewFrom(AlignFrame alf)
670   {
671     return alf.newView(null);
672   }
673
674   public AlignFrame newViewFrom(AlignFrame alf, String name)
675   {
676     return alf.newView(name);
677   }
678
679   /**
680    * 
681    * @param text
682    *          alignment file as a string
683    * @param title
684    *          window title
685    * @return null or new alignment frame
686    */
687   public AlignFrame loadAlignment(String text, String title)
688   {
689     Alignment al = null;
690
691     String format = new IdentifyFile().Identify(text,
692             AppletFormatAdapter.PASTE);
693     try
694     {
695       al = new AppletFormatAdapter().readFile(text,
696               AppletFormatAdapter.PASTE, format);
697       if (al.getHeight() > 0)
698       {
699         return new AlignFrame(al, this, title, false);
700       }
701     } catch (java.io.IOException ex)
702     {
703       ex.printStackTrace();
704     }
705     return null;
706   }
707
708   public void setMouseoverListener(String listener)
709   {
710     setMouseoverListener(currentAlignFrame, listener);
711   }
712
713   private Vector javascriptListeners = new Vector();
714
715   public void setMouseoverListener(AlignFrame af, String listener)
716   {
717     if (listener != null)
718     {
719       listener = listener.trim();
720       if (listener.length() == 0)
721       {
722         System.err
723                 .println("jalview Javascript error: Ignoring empty function for mouseover listener.");
724         return;
725       }
726     }
727     jalview.javascript.MouseOverListener mol = new jalview.javascript.MouseOverListener(
728             this, af, listener);
729     javascriptListeners.addElement(mol);
730     StructureSelectionManager.getStructureSelectionManager()
731             .addStructureViewerListener(mol);
732     if (debug)
733     {
734       System.err.println("Added a mouseover listener for "
735               + ((af == null) ? "All frames" : "Just views for "
736                       + af.getAlignViewport().getSequenceSetId()));
737       System.err.println("There are now " + javascriptListeners.size()
738               + " listeners in total.");
739     }
740   }
741
742   public void setSelectionListener(String listener)
743   {
744     setSelectionListener(null, listener);
745   }
746
747   public void setSelectionListener(AlignFrame af, String listener)
748   {
749     if (listener != null)
750     {
751       listener = listener.trim();
752       if (listener.length() == 0)
753       {
754         System.err
755                 .println("jalview Javascript error: Ignoring empty function for selection listener.");
756         return;
757       }
758     }
759     jalview.javascript.JsSelectionSender mol = new jalview.javascript.JsSelectionSender(
760             this, af, listener);
761     javascriptListeners.addElement(mol);
762     StructureSelectionManager.getStructureSelectionManager()
763             .addSelectionListener(mol);
764     if (debug)
765     {
766       System.err.println("Added a selection listener for "
767               + ((af == null) ? "All frames" : "Just views for "
768                       + af.getAlignViewport().getSequenceSetId()));
769       System.err.println("There are now " + javascriptListeners.size()
770               + " listeners in total.");
771     }
772   }
773
774   public void setStructureListener(String listener)
775   {
776     if (listener != null)
777     {
778       listener = listener.trim();
779       if (listener.length() == 0)
780       {
781         System.err
782                 .println("jalview Javascript error: Ignoring empty function for selection listener.");
783         return;
784       }
785     }
786     jalview.javascript.MouseOverStructureListener mol = new jalview.javascript.MouseOverStructureListener(this, listener);
787     javascriptListeners.addElement(mol);
788     StructureSelectionManager.getStructureSelectionManager()
789             .addStructureViewerListener(mol);
790     if (debug)
791     {
792       System.err.println("Added a javascript structure viewer listener '"+listener+"'");
793       System.err.println("There are now " + javascriptListeners.size()
794               + " listeners in total.");
795     }
796   }
797   /**
798    * remove any callback using the given listener function and associated with
799    * the given alignFrame (or null for all callbacks)
800    * 
801    * @param af
802    *          (may be null)
803    * @param listener
804    *          (may be null)
805    */
806   public void removeJavascriptListener(AlignFrame af, String listener)
807   {
808     if (listener != null)
809     {
810       listener = listener.trim();
811       if (listener.length() == 0)
812       {
813         listener = null;
814       }
815     }
816     boolean rprt = false;
817     for (int ms = 0, msSize = javascriptListeners.size(); ms < msSize;)
818     {
819       Object lstn = javascriptListeners.elementAt(ms);
820       JsCallBack lstner = (JsCallBack) lstn;
821       if ((af == null || lstner.getAlignFrame() == af)
822               && (listener == null || lstner.getListenerFunction().equals(
823                       listener)))
824       {
825         javascriptListeners.removeElement(lstner);
826         msSize--;
827         if (lstner instanceof SelectionListener)
828         {
829           StructureSelectionManager.getStructureSelectionManager()
830                   .removeSelectionListener((SelectionListener) lstner);
831         }
832         else
833         {
834           StructureSelectionManager.getStructureSelectionManager()
835                   .removeStructureViewerListener(lstner, null);
836         }
837         rprt = debug;
838         if (debug)
839         {
840           System.err.println("Removed listener '" + listener + "'");
841         }
842       }
843       else
844       {
845         ms++;
846       }
847     }
848     if (rprt)
849     {
850       System.err.println("There are now " + javascriptListeners.size()
851               + " listeners in total.");
852     }
853   }
854
855   public void stop()
856   {
857     if (javascriptListeners != null)
858     {
859       while (javascriptListeners.size() > 0)
860       {
861         Object mol = javascriptListeners.elementAt(0);
862         javascriptListeners.removeElement(mol);
863         if (mol instanceof SelectionListener)
864         {
865           StructureSelectionManager.getStructureSelectionManager()
866                   .removeSelectionListener((SelectionListener) mol);
867         }
868         else
869         {
870           StructureSelectionManager.getStructureSelectionManager()
871                   .removeStructureViewerListener(mol, null);
872         }
873       }
874     }
875     jalview.javascript.JSFunctionExec.stopQueue();
876   }
877
878   /**
879    * send a mouseover message to all the alignment windows associated with the
880    * given residue in the pdbfile
881    * 
882    * @param pdbResNum
883    * @param chain
884    * @param pdbfile
885    */
886   public void mouseOverStructure(String pdbResNum, String chain, String pdbfile)
887   {
888     try {
889     StructureSelectionManager.getStructureSelectionManager()
890             .mouseOverStructure(new Integer(pdbResNum).intValue(), chain, pdbfile);
891     if (debug)
892     {
893       System.err.println("mouseOver for '"+pdbResNum+"' in chain '"+chain+"' in structure '"+pdbfile+"'");
894     }
895     } catch (NumberFormatException e)
896     {
897         System.err.println("Ignoring invalid residue number string '"+pdbResNum+"'");
898     }
899   }
900
901   // //////////////////////////////////////////////
902   // //////////////////////////////////////////////
903
904   public static int lastFrameX = 200;
905
906   public static int lastFrameY = 200;
907
908   boolean fileFound = true;
909
910   String file = "No file";
911
912   Button launcher = new Button("Start Jalview");
913
914   /**
915    * The currentAlignFrame is static, it will change if and when the user
916    * selects a new window. Note that it will *never* point back to the embedded
917    * AlignFrame if the applet is started as embedded on the page and then
918    * afterwards a new view is created.
919    */
920   public AlignFrame currentAlignFrame = null;
921
922   /**
923    * This is the first frame to be displayed, and does not change. API calls
924    * will default to this instance if currentAlignFrame is null.
925    */
926   AlignFrame initialAlignFrame = null;
927
928   boolean embedded = false;
929
930   private boolean checkForJmol = true;
931
932   private boolean checkedForJmol = false; // ensure we don't check for jmol
933
934   // every time the app is re-inited
935
936   public boolean jmolAvailable = false;
937
938   private boolean alignPdbStructures = false;
939
940   /**
941    * use an external structure viewer exclusively (no jmols or MCViews will be opened by JalviewLite itself)
942    */
943   public boolean useXtrnalSviewer=false;
944
945   public static boolean debug = false;
946
947   static String builddate = null, version = null;
948
949   private static void initBuildDetails()
950   {
951     if (builddate == null)
952     {
953       builddate = "unknown";
954       version = "test";
955       java.net.URL url = JalviewLite.class
956               .getResource("/.build_properties");
957       if (url != null)
958       {
959         try
960         {
961           BufferedReader reader = new BufferedReader(new InputStreamReader(
962                   url.openStream()));
963           String line;
964           while ((line = reader.readLine()) != null)
965           {
966             if (line.indexOf("VERSION") > -1)
967             {
968               version = line.substring(line.indexOf("=") + 1);
969             }
970             if (line.indexOf("BUILD_DATE") > -1)
971             {
972               builddate = line.substring(line.indexOf("=") + 1);
973             }
974           }
975         } catch (Exception ex)
976         {
977           ex.printStackTrace();
978         }
979       }
980     }
981   }
982
983   public static String getBuildDate()
984   {
985     initBuildDetails();
986     return builddate;
987   }
988
989   public static String getVersion()
990   {
991     initBuildDetails();
992     return version;
993   }
994
995   // public JSObject scriptObject = null;
996
997   /**
998    * init method for Jalview Applet
999    */
1000   public void init()
1001   {
1002     // remove any handlers that might be hanging around from an earlier instance
1003     try
1004     {
1005       if (debug)
1006       {
1007         System.err.println("Applet context is '"
1008                 + getAppletContext().getClass().toString() + "'");
1009       }
1010       JSObject scriptObject = JSObject.getWindow(this);
1011       if (debug && scriptObject != null)
1012       {
1013         System.err.println("Applet has Javascript callback support.");
1014       }
1015
1016     } catch (Exception ex)
1017     {
1018       System.err
1019               .println("Warning: No JalviewLite javascript callbacks available.");
1020       if (debug)
1021       {
1022         ex.printStackTrace();
1023       }
1024     }
1025     /**
1026      * turn on extra applet debugging
1027      */
1028     String dbg = getParameter("debug");
1029     if (dbg != null)
1030     {
1031       debug = dbg.toLowerCase().equals("true");
1032     }
1033     if (debug)
1034     {
1035
1036       System.err.println("JalviewLite Version " + getVersion());
1037       System.err.println("Build Date : " + getBuildDate());
1038
1039     }
1040     String externalsviewer = getParameter("externalstructureviewer");
1041     if (externalsviewer!=null)
1042     {
1043       useXtrnalSviewer=externalsviewer.trim().toLowerCase().equals("true");
1044     }
1045     /**
1046      * if true disable the check for jmol
1047      */
1048     String chkforJmol = getParameter("nojmol");
1049     if (chkforJmol != null)
1050     {
1051       checkForJmol = !chkforJmol.equals("true");
1052     }
1053     /**
1054      * get the separator parameter if present
1055      */
1056     String sep = getParameter("separator");
1057     if (sep != null)
1058     {
1059       if (sep.length() > 0)
1060       {
1061         separator = sep;
1062         if (debug)
1063         {
1064           System.err.println("Separator set to '" + separator + "'");
1065         }
1066       }
1067       else
1068       {
1069         throw new Error(
1070                 "Invalid separator parameter - must be non-zero length");
1071       }
1072     }
1073     int r = 255;
1074     int g = 255;
1075     int b = 255;
1076     String param = getParameter("RGB");
1077
1078     if (param != null)
1079     {
1080       try
1081       {
1082         r = Integer.parseInt(param.substring(0, 2), 16);
1083         g = Integer.parseInt(param.substring(2, 4), 16);
1084         b = Integer.parseInt(param.substring(4, 6), 16);
1085       } catch (Exception ex)
1086       {
1087         r = 255;
1088         g = 255;
1089         b = 255;
1090       }
1091     }
1092     param = getParameter("label");
1093     if (param != null)
1094     {
1095       launcher.setLabel(param);
1096     }
1097
1098     setBackground(new Color(r, g, b));
1099
1100     file = getParameter("file");
1101
1102     if (file == null)
1103     {
1104       // Maybe the sequences are added as parameters
1105       StringBuffer data = new StringBuffer("PASTE");
1106       int i = 1;
1107       while ((file = getParameter("sequence" + i)) != null)
1108       {
1109         data.append(file.toString() + "\n");
1110         i++;
1111       }
1112       if (data.length() > 5)
1113       {
1114         file = data.toString();
1115       }
1116     }
1117
1118     final JalviewLite jvapplet = this;
1119     if (getParameter("embedded") != null
1120             && getParameter("embedded").equalsIgnoreCase("true"))
1121     {
1122       // Launch as embedded applet in page
1123       embedded = true;
1124       LoadingThread loader = new LoadingThread(file, jvapplet);
1125       loader.start();
1126     }
1127     else if (file != null)
1128     {
1129       if (getParameter("showbutton") == null
1130               || !getParameter("showbutton").equalsIgnoreCase("false"))
1131       {
1132         // Add the JalviewLite 'Button' to the page
1133         add(launcher);
1134         launcher.addActionListener(new java.awt.event.ActionListener()
1135         {
1136           public void actionPerformed(ActionEvent e)
1137           {
1138             LoadingThread loader = new LoadingThread(file, jvapplet);
1139             loader.start();
1140           }
1141         });
1142       }
1143       else
1144       {
1145         // Open jalviewLite immediately.
1146         LoadingThread loader = new LoadingThread(file, jvapplet);
1147         loader.start();
1148       }
1149     }
1150     else
1151     {
1152       // jalview initialisation with no alignment. loadAlignment() method can
1153       // still be called to open new alignments.
1154       file = "NO FILE";
1155       fileFound = false;
1156       // callInitCallback();
1157     }
1158   }
1159
1160   private void callInitCallback()
1161   {
1162     String initjscallback = getParameter("oninit");
1163     if (initjscallback == null)
1164     {
1165       return;
1166     }
1167     initjscallback = initjscallback.trim();
1168     if (initjscallback.length() > 0)
1169     {
1170       JSObject scriptObject = null;
1171       try
1172       {
1173         scriptObject = JSObject.getWindow(this);
1174       } catch (Exception ex)
1175       {
1176       }
1177       ;
1178       if (scriptObject != null)
1179       {
1180         try
1181         {
1182           // do onInit with the JS executor thread
1183           new JSFunctionExec(this).executeJavascriptFunction(true,
1184                   initjscallback, null, "Calling oninit callback '"
1185                           + initjscallback + "'.");
1186         } catch (Exception e)
1187         {
1188           System.err.println("Exception when executing _oninit callback '"
1189                   + initjscallback + "'.");
1190           e.printStackTrace();
1191         }
1192       }
1193       else
1194       {
1195         System.err.println("Not executing _oninit callback '"
1196                 + initjscallback + "' - no scripting allowed.");
1197       }
1198     }
1199   }
1200
1201   /**
1202    * Initialises and displays a new java.awt.Frame
1203    * 
1204    * @param frame
1205    *          java.awt.Frame to be displayed
1206    * @param title
1207    *          title of new frame
1208    * @param width
1209    *          width if new frame
1210    * @param height
1211    *          height of new frame
1212    */
1213   public static void addFrame(final Frame frame, String title, int width,
1214           int height)
1215   {
1216     frame.setLocation(lastFrameX, lastFrameY);
1217     lastFrameX += 40;
1218     lastFrameY += 40;
1219     frame.setSize(width, height);
1220     frame.setTitle(title);
1221     frame.addWindowListener(new WindowAdapter()
1222     {
1223       public void windowClosing(WindowEvent e)
1224       {
1225         if (frame instanceof AlignFrame)
1226         {
1227           ((AlignFrame) frame).closeMenuItem_actionPerformed();
1228           if (((AlignFrame) frame).viewport.applet.currentAlignFrame == frame)
1229           {
1230             ((AlignFrame) frame).viewport.applet.currentAlignFrame = null;
1231           }
1232         }
1233         lastFrameX -= 40;
1234         lastFrameY -= 40;
1235         if (frame instanceof EmbmenuFrame)
1236         {
1237           ((EmbmenuFrame) frame).destroyMenus();
1238         }
1239         frame.setMenuBar(null);
1240         frame.dispose();
1241       }
1242
1243       public void windowActivated(WindowEvent e)
1244       {
1245         if (frame instanceof AlignFrame)
1246         {
1247           ((AlignFrame) frame).viewport.applet.currentAlignFrame = (AlignFrame) frame;
1248           if (debug)
1249           {
1250             System.err.println("Activated window " + frame);
1251           }
1252         }
1253         // be good.
1254         super.windowActivated(e);
1255       }
1256       /*
1257        * Probably not necessary to do this - see TODO above. (non-Javadoc)
1258        * 
1259        * @see
1260        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
1261        * )
1262        * 
1263        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
1264        * frame) { currentAlignFrame = null; if (debug) {
1265        * System.err.println("Deactivated window "+frame); } }
1266        * super.windowDeactivated(e); }
1267        */
1268     });
1269     frame.setVisible(true);
1270   }
1271
1272   /**
1273    * This paints the background surrounding the "Launch Jalview button" <br>
1274    * <br>
1275    * If file given in parameter not found, displays error message
1276    * 
1277    * @param g
1278    *          graphics context
1279    */
1280   public void paint(Graphics g)
1281   {
1282     if (!fileFound)
1283     {
1284       g.setColor(new Color(200, 200, 200));
1285       g.setColor(Color.cyan);
1286       g.fillRect(0, 0, getSize().width, getSize().height);
1287       g.setColor(Color.red);
1288       g.drawString("Jalview can't open file", 5, 15);
1289       g.drawString("\"" + file + "\"", 5, 30);
1290     }
1291     else if (embedded)
1292     {
1293       g.setColor(Color.black);
1294       g.setFont(new Font("Arial", Font.BOLD, 24));
1295       g.drawString("Jalview Applet", 50, getSize().height / 2 - 30);
1296       g.drawString("Loading Data...", 50, getSize().height / 2);
1297     }
1298   }
1299
1300   /**
1301    * get all components associated with the applet of the given type
1302    * 
1303    * @param class1
1304    * @return
1305    */
1306   public Vector getAppletWindow(Class class1)
1307   {
1308     Vector wnds = new Vector();
1309     Component[] cmp = getComponents();
1310     if (cmp != null)
1311     {
1312       for (int i = 0; i < cmp.length; i++)
1313       {
1314         if (class1.isAssignableFrom(cmp[i].getClass()))
1315         {
1316           wnds.addElement(cmp);
1317         }
1318       }
1319     }
1320     return wnds;
1321   }
1322
1323   class LoadJmolThread extends Thread
1324   {
1325     private boolean running = false;
1326
1327     public void run()
1328     {
1329       if (running || checkedForJmol)
1330       {
1331         return;
1332       }
1333       running = true;
1334       if (checkForJmol)
1335       {
1336         try
1337         {
1338           if (!System.getProperty("java.version").startsWith("1.1"))
1339           {
1340             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
1341             jmolAvailable = true;
1342           }
1343           if (!jmolAvailable)
1344           {
1345             System.out
1346                     .println("Jmol not available - Using MCview for structures");
1347           }
1348         } catch (java.lang.ClassNotFoundException ex)
1349         {
1350         }
1351       }
1352       else
1353       {
1354         jmolAvailable = false;
1355         if (debug)
1356         {
1357           System.err
1358                   .println("Skipping Jmol check. Will use MCView (probably)");
1359         }
1360       }
1361       checkedForJmol = true;
1362       running = false;
1363     }
1364
1365     public boolean notFinished()
1366     {
1367       return running || !checkedForJmol;
1368     }
1369   }
1370
1371   class LoadingThread extends Thread
1372   {
1373     /**
1374      * State variable: File source
1375      */
1376     String file;
1377
1378     /**
1379      * State variable: protocol for access to file source
1380      */
1381     String protocol;
1382
1383     /**
1384      * State variable: format of file source
1385      */
1386     String format;
1387
1388     String _file;
1389
1390     JalviewLite applet;
1391
1392     private void dbgMsg(String msg)
1393     {
1394       if (applet.debug)
1395       {
1396         System.err.println(msg);
1397       }
1398     }
1399
1400     /**
1401      * update the protocol state variable for accessing the datasource located
1402      * by file.
1403      * 
1404      * @param file
1405      * @return possibly updated datasource string
1406      */
1407     public String setProtocolState(String file)
1408     {
1409       if (file.startsWith("PASTE"))
1410       {
1411         file = file.substring(5);
1412         protocol = AppletFormatAdapter.PASTE;
1413       }
1414       else if (inArchive(file))
1415       {
1416         protocol = AppletFormatAdapter.CLASSLOADER;
1417       }
1418       else
1419       {
1420         file = addProtocol(file);
1421         protocol = AppletFormatAdapter.URL;
1422       }
1423       dbgMsg("Protocol identified as '" + protocol + "'");
1424       return file;
1425     }
1426
1427     public LoadingThread(String _file, JalviewLite _applet)
1428     {
1429       this._file = _file;
1430       applet = _applet;
1431     }
1432
1433     public void run()
1434     {
1435       LoadJmolThread jmolchecker = new LoadJmolThread();
1436       jmolchecker.start();
1437       while (jmolchecker.notFinished())
1438       {
1439         // wait around until the Jmol check is complete.
1440         try
1441         {
1442           Thread.sleep(2);
1443         } catch (Exception e)
1444         {
1445         }
1446         ;
1447       }
1448       startLoading();
1449       // applet.callInitCallback();
1450     }
1451
1452     private void startLoading()
1453     {
1454       AlignFrame newAlignFrame;
1455       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
1456       file = setProtocolState(_file);
1457
1458       format = new jalview.io.IdentifyFile().Identify(file, protocol);
1459       dbgMsg("File identified as '" + format + "'");
1460       dbgMsg("Loading started.");
1461       Alignment al = null;
1462       try
1463       {
1464         al = new AppletFormatAdapter().readFile(file, protocol, format);
1465       } catch (java.io.IOException ex)
1466       {
1467         dbgMsg("File load exception.");
1468         ex.printStackTrace();
1469         if (debug)
1470         {
1471           try
1472           {
1473             FileParse fp = new FileParse(file, protocol);
1474             String ln = null;
1475             dbgMsg(">>>Dumping contents of '" + file + "' " + "("
1476                     + protocol + ")");
1477             while ((ln = fp.nextLine()) != null)
1478             {
1479               dbgMsg(ln);
1480             }
1481             dbgMsg(">>>Dump finished.");
1482           } catch (Exception e)
1483           {
1484             System.err
1485                     .println("Exception when trying to dump the content of the file parameter.");
1486             e.printStackTrace();
1487           }
1488         }
1489       }
1490       if ((al != null) && (al.getHeight() > 0))
1491       {
1492         dbgMsg("Successfully loaded file.");
1493         newAlignFrame = new AlignFrame(al, applet, file, embedded);
1494         if (initialAlignFrame == null)
1495         {
1496           initialAlignFrame = newAlignFrame;
1497         }
1498         // update the focus.
1499         currentAlignFrame = newAlignFrame;
1500
1501         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
1502         {
1503           newAlignFrame.setTitle("Sequences from "
1504                   + applet.getDocumentBase());
1505         }
1506
1507         newAlignFrame.statusBar.setText("Successfully loaded file " + file);
1508
1509         String treeFile = applet.getParameter("tree");
1510         if (treeFile == null)
1511         {
1512           treeFile = applet.getParameter("treeFile");
1513         }
1514
1515         if (treeFile != null)
1516         {
1517           try
1518           {
1519             treeFile = setProtocolState(treeFile);
1520             /*
1521              * if (inArchive(treeFile)) { protocol =
1522              * AppletFormatAdapter.CLASSLOADER; } else { protocol =
1523              * AppletFormatAdapter.URL; treeFile = addProtocol(treeFile); }
1524              */
1525             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
1526                     protocol);
1527
1528             fin.parse();
1529
1530             if (fin.getTree() != null)
1531             {
1532               newAlignFrame.loadTree(fin, treeFile);
1533               dbgMsg("Successfuly imported tree.");
1534             }
1535             else
1536             {
1537               dbgMsg("Tree parameter did not resolve to a valid tree.");
1538             }
1539           } catch (Exception ex)
1540           {
1541             ex.printStackTrace();
1542           }
1543         }
1544
1545         String param = applet.getParameter("features");
1546         if (param != null)
1547         {
1548           param = setProtocolState(param);
1549
1550           newAlignFrame.parseFeaturesFile(param, protocol);
1551         }
1552
1553         param = applet.getParameter("showFeatureSettings");
1554         if (param != null && param.equalsIgnoreCase("true"))
1555         {
1556           newAlignFrame.viewport.showSequenceFeatures(true);
1557           new FeatureSettings(newAlignFrame.alignPanel);
1558         }
1559
1560         param = applet.getParameter("annotations");
1561         if (param != null)
1562         {
1563           param = setProtocolState(param);
1564
1565           if (new AnnotationFile().readAnnotationFile(
1566                   newAlignFrame.viewport.getAlignment(), param, protocol))
1567           {
1568             newAlignFrame.alignPanel.fontChanged();
1569             newAlignFrame.alignPanel.setScrollValues(0, 0);
1570           }
1571           else
1572           {
1573             System.err
1574                     .println("Annotations were not added from annotation file '"
1575                             + param + "'");
1576           }
1577
1578         }
1579
1580         param = applet.getParameter("jnetfile");
1581         if (param != null)
1582         {
1583           try
1584           {
1585             param = setProtocolState(param);
1586             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
1587                     param, protocol);
1588             JnetAnnotationMaker.add_annotation(predictions,
1589                     newAlignFrame.viewport.getAlignment(), 0, false); // false==do
1590             // not
1591             // add
1592             // sequence
1593             // profile
1594             // from
1595             // concise
1596             // output
1597             newAlignFrame.alignPanel.fontChanged();
1598             newAlignFrame.alignPanel.setScrollValues(0, 0);
1599           } catch (Exception ex)
1600           {
1601             ex.printStackTrace();
1602           }
1603         }
1604         /*
1605          * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6
1606          * - related to JAL-434
1607          */
1608         applet.setAlignPdbStructures(getDefaultParameter("alignpdbfiles",
1609                 false));
1610         /*
1611          * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
1612          * PDB|1GAQ|1GAQ|C">
1613          * 
1614          * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
1615          * 
1616          * <param name="PDBfile3" value="1q0o Q45135_9MICO">
1617          */
1618
1619         int pdbFileCount = 0;
1620         // Accumulate pdbs here if they are heading for the same view (if
1621         // alignPdbStructures is true)
1622         Vector pdbs = new Vector();
1623         // create a lazy matcher if we're asked to
1624         jalview.analysis.SequenceIdMatcher matcher = (applet.getDefaultParameter("relaxedidmatch", false)) ? new jalview.analysis.SequenceIdMatcher(
1625                 newAlignFrame.getAlignViewport().getAlignment().getSequencesArray()) : null;
1626
1627         do
1628         {
1629           if (pdbFileCount > 0)
1630             param = applet.getParameter("PDBFILE" + pdbFileCount);
1631           else
1632             param = applet.getParameter("PDBFILE");
1633
1634           if (param != null)
1635           {
1636             PDBEntry pdb = new PDBEntry();
1637
1638             String seqstring;
1639             SequenceI[] seqs = null;
1640             String[] chains = null;
1641
1642             StringTokenizer st = new StringTokenizer(param, " ");
1643
1644             if (st.countTokens() < 2)
1645             {
1646               String sequence = applet.getParameter("PDBSEQ");
1647               if (sequence != null)
1648                 seqs = new SequenceI[]
1649                 { matcher==null ? (Sequence) newAlignFrame.getAlignViewport()
1650                         .getAlignment().findName(sequence) : matcher.findIdMatch(sequence) };
1651
1652             }
1653             else
1654             {
1655               param = st.nextToken();
1656               Vector tmp = new Vector();
1657               Vector tmp2 = new Vector();
1658
1659               while (st.hasMoreTokens())
1660               {
1661                 seqstring = st.nextToken();
1662                 StringTokenizer st2 = new StringTokenizer(seqstring, "=");
1663                 if (st2.countTokens() > 1)
1664                 {
1665                   // This is the chain
1666                   tmp2.addElement(st2.nextToken());
1667                   seqstring = st2.nextToken();
1668                 }
1669                 tmp.addElement(matcher==null ? (Sequence) newAlignFrame.getAlignViewport()
1670                         .getAlignment().findName(seqstring) : matcher.findIdMatch(seqstring));
1671               }
1672
1673               seqs = new SequenceI[tmp.size()];
1674               tmp.copyInto(seqs);
1675               if (tmp2.size() == tmp.size())
1676               {
1677                 chains = new String[tmp2.size()];
1678                 tmp2.copyInto(chains);
1679               }
1680             }
1681             param = setProtocolState(param);
1682
1683             if (// !jmolAvailable
1684             // &&
1685             protocol == AppletFormatAdapter.CLASSLOADER)
1686             {
1687               // TODO: verify this Re:
1688               // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1689               // This exception preserves the current behaviour where, even if
1690               // the local pdb file was identified in the class loader
1691               protocol = AppletFormatAdapter.URL; // this is probably NOT
1692               // CORRECT!
1693               param = addProtocol(param); //
1694             }
1695
1696             pdb.setFile(param);
1697
1698             if (seqs != null)
1699             {
1700               for (int i = 0; i < seqs.length; i++)
1701               {
1702                 if (seqs[i] != null)
1703                 {
1704                   ((Sequence) seqs[i]).addPDBId(pdb);
1705                 }
1706                 else
1707                 {
1708                   if (JalviewLite.debug)
1709                   {
1710                     // this may not really be a problem but we give a warning
1711                     // anyway
1712                     System.err
1713                             .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
1714                                     + i + ")");
1715                   }
1716                 }
1717               }
1718
1719               if (!alignPdbStructures)
1720               {
1721                 newAlignFrame.newStructureView(applet, pdb, seqs, chains,
1722                         protocol);
1723               }
1724               else
1725               {
1726                 pdbs.addElement(new Object[]
1727                 { pdb, seqs, chains, new String(protocol) });
1728               }
1729             }
1730           }
1731
1732           pdbFileCount++;
1733         } while (pdbFileCount < 10);
1734         if (pdbs.size() > 0)
1735         {
1736           SequenceI[][] seqs = new SequenceI[pdbs.size()][];
1737           PDBEntry[] pdb = new PDBEntry[pdbs.size()];
1738           String[][] chains = new String[pdbs.size()][];
1739           String[] protocols = new String[pdbs.size()];
1740           for (int pdbsi = 0, pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
1741           {
1742             Object[] o = (Object[]) pdbs.elementAt(pdbsi);
1743             pdb[pdbsi] = (PDBEntry) o[0];
1744             seqs[pdbsi] = (SequenceI[]) o[1];
1745             chains[pdbsi] = (String[]) o[2];
1746             protocols[pdbsi] = (String) o[3];
1747           }
1748           newAlignFrame.alignedStructureView(applet, pdb, seqs, chains,
1749                   protocols);
1750
1751         }
1752         // ///////////////////////////
1753         // modify display of features
1754         //
1755         // hide specific groups
1756         param = applet.getParameter("hidefeaturegroups");
1757         if (param != null)
1758         {
1759           applet.setFeatureGroupStateOn(newAlignFrame, param, false);
1760         }
1761         // show specific groups
1762         param = applet.getParameter("showfeaturegroups");
1763         if (param != null)
1764         {
1765           applet.setFeatureGroupStateOn(newAlignFrame, param, true);
1766         }
1767       }
1768       else
1769       {
1770         fileFound = false;
1771         applet.remove(launcher);
1772         applet.repaint();
1773       }
1774     }
1775
1776     /**
1777      * Discovers whether the given file is in the Applet Archive
1778      * 
1779      * @param file
1780      *          String
1781      * @return boolean
1782      */
1783     boolean inArchive(String file)
1784     {
1785       // This might throw a security exception in certain browsers
1786       // Netscape Communicator for instance.
1787       try
1788       {
1789         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
1790         if (debug)
1791         {
1792           System.err.println("Resource '" + file + "' was "
1793                   + (rtn ? "" : "not") + " located by classloader.");
1794         }
1795         return rtn;
1796       } catch (Exception ex)
1797       {
1798         System.out.println("Exception checking resources: " + file + " "
1799                 + ex);
1800         return false;
1801       }
1802     }
1803
1804     String addProtocol(String file)
1805     {
1806       if (file.indexOf("://") == -1)
1807       {
1808         file = applet.getCodeBase() + file;
1809         if (debug)
1810         {
1811           System.err.println("Prepended codebase for resource: '" + file
1812                   + "'");
1813         }
1814       }
1815
1816       return file;
1817     }
1818   }
1819
1820   /**
1821    * @return the default alignFrame acted on by the public applet methods. May
1822    *         return null with an error message on System.err indicating the
1823    *         fact.
1824    */
1825   public AlignFrame getDefaultTargetFrame()
1826   {
1827     if (currentAlignFrame != null)
1828     {
1829       return currentAlignFrame;
1830     }
1831     if (initialAlignFrame != null)
1832     {
1833       return initialAlignFrame;
1834     }
1835     System.err
1836             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
1837     return null;
1838   }
1839
1840   /**
1841    * separator used for separatorList
1842    */
1843   protected String separator = "" + ((char) 0x00AC); // the default used to be
1844                                                      // '|' but many sequence
1845                                                      // IDS include pipes.
1846
1847   /**
1848    * parse the string into a list
1849    * 
1850    * @param list
1851    * @return elements separated by separator
1852    */
1853   public String[] separatorListToArray(String list)
1854   {
1855     return separatorListToArray(list, separator);
1856   }
1857
1858   /**
1859    * parse the string into a list
1860    * 
1861    * @param list
1862    * @param separator
1863    * @return elements separated by separator
1864    */
1865   public String[] separatorListToArray(String list, String separator)
1866   {
1867     // note separator local variable intentionally masks object field
1868     int seplen = separator.length();
1869     if (list == null || list.equals("") || list.equals(separator))
1870       return null;
1871     java.util.Vector jv = new Vector();
1872     int cp = 0, pos;
1873     while ((pos = list.indexOf(separator, cp)) > cp)
1874     {
1875       jv.addElement(list.substring(cp, pos));
1876       cp = pos + seplen;
1877     }
1878     if (cp < list.length())
1879     {
1880       String c = list.substring(cp);
1881       if (!c.equals(separator))
1882       {
1883         jv.addElement(c);
1884       }
1885     }
1886     if (jv.size() > 0)
1887     {
1888       String[] v = new String[jv.size()];
1889       for (int i = 0; i < v.length; i++)
1890       {
1891         v[i] = (String) jv.elementAt(i);
1892       }
1893       jv.removeAllElements();
1894       if (debug)
1895       {
1896         System.err.println("Array from '" + separator
1897                 + "' separated List:\n" + v.length);
1898         for (int i = 0; i < v.length; i++)
1899         {
1900           System.err.println("item " + i + " '" + v[i] + "'");
1901         }
1902       }
1903       return v;
1904     }
1905     if (debug)
1906     {
1907       System.err.println("Empty Array from '" + separator
1908               + "' separated List");
1909     }
1910     return null;
1911   }
1912
1913   /**
1914    * concatenate the list with separator
1915    * 
1916    * @param list
1917    * @return concatenated string
1918    */
1919   public String arrayToSeparatorList(String[] list)
1920   {
1921     return arrayToSeparatorList(list, separator);
1922   }
1923
1924   /**
1925    * concatenate the list with separator
1926    * 
1927    * @param list
1928    * @param separator
1929    * @return concatenated string
1930    */
1931   public String arrayToSeparatorList(String[] list, String separator)
1932   {
1933     StringBuffer v = new StringBuffer();
1934     if (list != null && list.length > 0)
1935     {
1936       for (int i = 0, iSize = list.length; i < iSize; i++)
1937       {
1938         if (list[i] != null)
1939         {
1940           if (i > 0)
1941           {
1942             v.append(separator);
1943           }
1944           v.append(list[i]);
1945         }
1946       }
1947       if (debug)
1948       {
1949         System.err.println("Returning '" + separator
1950                 + "' separated List:\n");
1951         System.err.println(v);
1952       }
1953       return v.toString();
1954     }
1955     if (debug)
1956     {
1957       System.err.println("Returning empty '" + separator
1958               + "' separated List\n");
1959     }
1960     return "" + separator;
1961   }
1962
1963   /**
1964    * @return
1965    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1966    */
1967   public String getFeatureGroups()
1968   {
1969     String lst = arrayToSeparatorList(getDefaultTargetFrame()
1970             .getFeatureGroups());
1971     return lst;
1972   }
1973
1974   /**
1975    * @param alf
1976    *          alignframe to get feature groups on
1977    * @return
1978    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1979    */
1980   public String getFeatureGroupsOn(AlignFrame alf)
1981   {
1982     String lst = arrayToSeparatorList(alf.getFeatureGroups());
1983     return lst;
1984   }
1985
1986   /**
1987    * @param visible
1988    * @return
1989    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1990    */
1991   public String getFeatureGroupsOfState(boolean visible)
1992   {
1993     return arrayToSeparatorList(getDefaultTargetFrame()
1994             .getFeatureGroupsOfState(visible));
1995   }
1996
1997   /**
1998    * @param alf
1999    *          align frame to get groups of state visible
2000    * @param visible
2001    * @return
2002    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
2003    */
2004   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
2005   {
2006     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
2007   }
2008
2009   /**
2010    * @param groups
2011    *          tab separated list of group names
2012    * @param state
2013    *          true or false
2014    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[],
2015    *      boolean)
2016    */
2017   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
2018           boolean state)
2019   {
2020     boolean st = state;// !(state==null || state.equals("") ||
2021     // state.toLowerCase().equals("false"));
2022     alf.setFeatureGroupState(separatorListToArray(groups), st);
2023   }
2024
2025   public void setFeatureGroupState(String groups, boolean state)
2026   {
2027     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
2028   }
2029
2030   /**
2031    * List separator string
2032    * 
2033    * @return the separator
2034    */
2035   public String getSeparator()
2036   {
2037     return separator;
2038   }
2039
2040   /**
2041    * List separator string
2042    * 
2043    * @param separator
2044    *          the separator to set. empty string will reset separator to default
2045    */
2046   public void setSeparator(String separator)
2047   {
2048     if (separator == null || separator.length() < 1)
2049     {
2050       // reset to default
2051       separator = "" + ((char) 0x00AC);
2052     }
2053     this.separator = separator;
2054     if (debug)
2055     {
2056       System.err.println("Default Separator now: '" + separator + "'");
2057     }
2058   }
2059
2060   /**
2061    * get boolean value of applet parameter 'name' and return default if
2062    * parameter is not set
2063    * 
2064    * @param name
2065    *          name of paremeter
2066    * @param def
2067    *          the value to return otherwise
2068    * @return true or false
2069    */
2070   public boolean getDefaultParameter(String name, boolean def)
2071   {
2072     String stn;
2073     if ((stn = getParameter(name)) == null)
2074     {
2075       return def;
2076     }
2077     if (stn.toLowerCase().equals("true"))
2078     {
2079       return true;
2080     }
2081     return false;
2082   }
2083
2084   /**
2085    * bind a pdb file to a sequence in the given alignFrame.
2086    * 
2087    * @param alFrame
2088    *          - null or specific alignFrame. This specifies the dataset that
2089    *          will be searched for a seuqence called sequenceId
2090    * @param sequenceId
2091    *          - sequenceId within the dataset.
2092    * @param pdbEntryString
2093    *          - the short name for the PDB file
2094    * @param pdbFile
2095    *          - pdb file - either a URL or a valid PDB file.
2096    * @return true if binding was as success TODO: consider making an exception
2097    *         structure for indicating when PDB parsing or seqeunceId location
2098    *         fails.
2099    */
2100   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
2101           String pdbEntryString, String pdbFile)
2102   {
2103     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
2104   }
2105
2106   protected void setAlignPdbStructures(boolean alignPdbStructures)
2107   {
2108     this.alignPdbStructures = alignPdbStructures;
2109   }
2110
2111   public boolean isAlignPdbStructures()
2112   {
2113     return alignPdbStructures;
2114   }
2115
2116   public void start()
2117   {
2118     callInitCallback();
2119   }
2120
2121   /**
2122    * bind structures in a viewer to any matching sequences in an alignFrame (use
2123    * sequenceIds to limit scope of search to specific sequences)
2124    * 
2125    * @param alFrame
2126    * @param viewer
2127    * @param sequenceIds
2128    * @return TODO: consider making an exception structure for indicating when
2129    *         binding fails public SequenceStructureBinding
2130    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
2131    *         sequenceIds) {
2132    * 
2133    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
2134    *         alFrame.addStructureViewInstance(viewer,
2135    *         separatorListToArray(sequenceIds)); } else { return
2136    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
2137    */
2138 }