JAL-1807 - Bob's last(?) before leaving Dundee -- adds fast file loading
[jalviewjs.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
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
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.bin;
22
23 import jalview.analysis.SequenceIdMatcher;
24 import jalview.api.StructureSelectionManagerProvider;
25 import jalview.appletgui.AlignFrame;
26 import jalview.appletgui.AlignViewport;
27 import jalview.appletgui.EmbmenuFrame;
28 import jalview.appletgui.FeatureSettings;
29 import jalview.appletgui.SplitFrame;
30 //import jalview.appletgui.AlignViewport;
31 //import jalview.appletgui.EmbmenuFrame;
32 //import jalview.appletgui.FeatureSettings;
33 //import jalview.appletgui.SplitFrame;
34 import jalview.datamodel.Alignment;
35 import jalview.datamodel.AlignmentI;
36 import jalview.datamodel.AlignmentOrder;
37 import jalview.datamodel.ColumnSelection;
38 import jalview.datamodel.PDBEntry;
39 import jalview.datamodel.Sequence;
40 import jalview.datamodel.SequenceGroup;
41 import jalview.datamodel.SequenceI;
42 import jalview.io.AlignFile;
43 import jalview.io.AnnotationFile;
44 import jalview.io.AppletFormatAdapter;
45 import jalview.io.FileParse;
46 import jalview.io.IdentifyFile;
47 import jalview.io.JnetAnnotationMaker;
48 //import jalview.io.JPredFile;
49 //import jalview.io.JnetAnnotationMaker;
50 import jalview.io.NewickFile;
51 import jalview.javascript.JSFunctionExec;
52 import jalview.javascript.JalviewLiteJsApi;
53 import jalview.javascript.JsCallBack;
54 import jalview.javascript.JsSelectionSender;
55 import jalview.javascript.MouseOverListener;
56 import jalview.javascript.MouseOverStructureListener;
57 import jalview.jsdev.GenericFileAdapter;
58 //import jalview.javascript.MouseOverListener;
59 //import jalview.javascript.MouseOverStructureListener;
60 import jalview.schemes.ColourSchemeProperty;
61 import jalview.schemes.UserColourScheme;
62 import jalview.structure.SelectionListener;
63 import jalview.structure.StructureSelectionManager;
64 import jalview.util.MessageManager;
65
66 import java.awt.Color;
67 import java.awt.Component;
68 import java.awt.EventQueue;
69 import java.awt.Font;
70 import java.awt.Graphics;
71 import java.awt.event.ActionEvent;
72 import java.awt.event.WindowAdapter;
73 import java.awt.event.WindowEvent;
74 import java.io.BufferedReader;
75 import java.io.IOException;
76 import java.io.InputStream;
77 import java.io.InputStreamReader;
78 import java.net.URL;
79 import java.util.Hashtable;
80 import java.util.List;
81 import java.util.StringTokenizer;
82 import java.util.Vector;
83
84 import javax.swing.JApplet;
85 import javax.swing.JButton;
86 import javax.swing.JFrame;
87
88 import netscape.javascript.JSObject;
89
90 //import netscape.javascript.JSObject;
91
92 /**
93  * Jalview Applet. Runs in Java 1.18 runtime
94  * 
95  * @author $author$
96  * @version $Revision: 1.92 $
97  */
98 public class JalviewLite extends JApplet implements
99         StructureSelectionManagerProvider, JalviewLiteJsApi
100 {
101
102   private static final String TRUE = "true";
103
104   private static final String FALSE = "false";
105
106   public static boolean debug = false;
107   
108   public boolean embedded         = false;
109   public boolean enableSplitFrame = false;
110   public boolean showButton       = true;
111   public boolean checkForJmol     = true;
112
113   
114         public String jalviewServletURL;
115   public String startupFile = "No file";
116   public String helpUrl;
117   public String externalstructureviewer;
118   public String sep;
119   public String rgb;
120   public String labelColour;
121         public String initjscallback;
122         public String pdbFile;
123         public String sequence;
124         public String jnetFile;
125         public String annotations;
126         public String hideFeatureGroups; 
127         public String showFeatureGroups; 
128         public String features; 
129         public String showFeatureSettings; 
130   public String scoreFile;
131   public String treeFile;
132   public String windowWidth;
133   public String windowHeight;
134   public String defaultColour;
135         public String sortBy;
136         public String wrap;
137         public String centrecolumnlabels;
138         public String userDefinedColour;
139         public String widthScale;
140         public String heightScale;
141         public String upperCase;
142         public String file2;
143         
144         private void setParams() {
145     debug            = TRUE.equalsIgnoreCase(getParameter("debug"));
146     enableSplitFrame = TRUE.equalsIgnoreCase(getParameter("enableSplitFrame"));
147     embedded         = TRUE.equalsIgnoreCase(getParameter("embedded"));
148     showButton       = !FALSE.equalsIgnoreCase(getParameter("showbutton"));
149
150     jalviewServletURL    = getParameter("APPLICATION_URL");
151                 startupFile          = getParameter("file");
152     helpUrl              = getParameter("jalviewhelpurl");
153     externalstructureviewer = getParameter("externalstructureviewer");
154     checkForJmol         = !TRUE.equals(getParameter("nojmol"));
155     sep                  = getParameter("separator");
156     rgb                  = getParameter("RGB");
157     labelColour          = getParameter("label");
158         initjscallback       = getParameter("oninit");
159         pdbFile              = getParameter("PDBFILE");
160     sequence             = getParameter("PDBSEQ");
161     jnetFile             = getParameter("jnetfile");
162     annotations          = getParameter("annotations");
163         hideFeatureGroups    = getParameter("hidefeaturegroups");
164         showFeatureGroups    = getParameter("showfeaturegroups");
165         features             = getParameter("features");
166         showFeatureSettings  = getParameter("showFeatureSettings");     
167     scoreFile            = getParameter("scoreFile");
168     treeFile             = getParameter("tree");
169     if (treeFile == null)
170       treeFile             = getParameter("treeFile");          
171     windowWidth          = getParameter("windowWidth");
172     windowHeight         = getParameter("windowHeight");
173     defaultColour        = getParameter("defaultColour");
174
175     sortBy               = getParameter("sortBy");
176     wrap                 = getParameter("wrap");
177     centrecolumnlabels   = getParameter("centrecolumnlabels");
178     userDefinedColour    = getParameter("userDefinedColour");
179     widthScale           = getParameter("widthScale");
180     heightScale          = getParameter("heightScale");
181     upperCase            = getParameter("upperCase");
182     file2                = getParameter("file2");
183
184   }
185
186   /**
187    * get boolean value of applet parameter 'name' and return default if
188    * parameter is not set
189    * 
190    * @param name
191    *          name of paremeter
192    * @param def
193    *          the value to return otherwise
194    * @return true or false
195    */
196   public boolean getDefaultParameter(String name, boolean def)
197   {
198     String stn;
199     if ((stn = getParameter(name)) == null)
200     {
201       return def;
202     }
203     if (stn.toLowerCase().equals(TRUE))
204     {
205       return true;
206     }
207     return false;
208   }
209
210         public void getLinkParams(Vector links) {
211     String label, url;
212     for (int i = 1; i < 10; i++)
213     {
214       label = getParameter("linkLabel_" + i);
215       url = getParameter("linkURL_" + i);
216       if (label != null && url != null)
217       {
218         links.addElement(label + "|" + url);
219       }
220
221     }
222         }
223
224
225   public StructureSelectionManager getStructureSelectionManager()
226   {
227     return StructureSelectionManager.getStructureSelectionManager(this);
228   }
229
230   // /////////////////////////////////////////
231   // The following public methods maybe called
232   // externally, eg via javascript in HTML page
233   /*
234    * (non-Javadoc)
235    * 
236    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
237    */
238   public String getSelectedSequences()
239   {
240     return getSelectedSequencesFrom(getDefaultTargetFrame());
241   }
242
243   /*
244    * (non-Javadoc)
245    * 
246    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
247    */
248   public String getSelectedSequences(String sep)
249   {
250     return getSelectedSequencesFrom(getDefaultTargetFrame(), sep);
251   }
252
253   /*
254    * (non-Javadoc)
255    * 
256    * @see
257    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
258    * .AlignFrame)
259    */
260   public String getSelectedSequencesFrom(AlignFrame alf)
261   {
262     return getSelectedSequencesFrom(alf, separator); // ""+0x00AC);
263   }
264
265   /*
266    * (non-Javadoc)
267    * 
268    * @see
269    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
270    * .AlignFrame, java.lang.String)
271    */
272   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
273   {
274     StringBuffer result = new StringBuffer("");
275     if (sep == null || sep.length() == 0)
276     {
277       sep = separator; // "+0x00AC;
278     }
279     if (alf.viewport.getSelectionGroup() != null)
280     {
281       SequenceI[] seqs = alf.viewport.getSelectionGroup()
282               .getSequencesInOrder(alf.viewport.getAlignment());
283
284       for (int i = 0; i < seqs.length; i++)
285       {
286         result.append(seqs[i].getName());
287         result.append(sep);
288       }
289     }
290
291     return result.toString();
292   }
293
294   /*
295    * (non-Javadoc)
296    * 
297    * @see jalview.bin.JalviewLiteJsApi#highlight(java.lang.String,
298    * java.lang.String, java.lang.String)
299    */
300   public void highlight(String sequenceId, String position,
301           String alignedPosition)
302   {
303     highlightIn(getDefaultTargetFrame(), sequenceId, position,
304             alignedPosition);
305   }
306
307   /*
308    * (non-Javadoc)
309    * 
310    * @see jalview.bin.JalviewLiteJsApi#highlightIn(jalview.appletgui.AlignFrame,
311    * java.lang.String, java.lang.String, java.lang.String)
312    */
313   public void highlightIn(final AlignFrame alf, final String sequenceId,
314           final String position, final String alignedPosition)
315   {
316     // TODO: could try to highlight in all alignments if alf==null
317     SequenceIdMatcher matcher = new SequenceIdMatcher(
318             alf.viewport.getAlignment().getSequencesArray());
319     final SequenceI sq = matcher.findIdMatch(sequenceId);
320     if (sq != null)
321     {
322       int apos = -1;
323       try
324       {
325         apos = new Integer(position).intValue();
326         apos--;
327       } catch (NumberFormatException ex)
328       {
329         return;
330       }
331       final StructureSelectionManagerProvider me = this;
332       final int pos = apos;
333       // use vamsas listener to broadcast to all listeners in scope
334       if (alignedPosition != null
335               && (alignedPosition.trim().length() == 0 || alignedPosition
336                       .toLowerCase().indexOf("false") > -1))
337       {
338         java.awt.EventQueue.invokeLater(new Runnable()
339         {
340           @Override
341           public void run()
342           {
343             StructureSelectionManager.getStructureSelectionManager(me)
344                     .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
345           }
346         });
347       }
348       else
349       {
350         java.awt.EventQueue.invokeLater(new Runnable()
351         {
352           @Override
353           public void run()
354           {
355             StructureSelectionManager.getStructureSelectionManager(me)
356                     .mouseOverVamsasSequence(sq, pos, null);
357           }
358         });
359       }
360     }
361   }
362
363   /*
364    * (non-Javadoc)
365    * 
366    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
367    * java.lang.String)
368    */
369   public void select(String sequenceIds, String columns)
370   {
371     selectIn(getDefaultTargetFrame(), sequenceIds, columns, separator);
372   }
373
374   /*
375    * (non-Javadoc)
376    * 
377    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
378    * java.lang.String, java.lang.String)
379    */
380   public void select(String sequenceIds, String columns, String sep)
381   {
382     selectIn(getDefaultTargetFrame(), sequenceIds, columns, sep);
383   }
384
385   /*
386    * (non-Javadoc)
387    * 
388    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
389    * java.lang.String, java.lang.String)
390    */
391   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
392   {
393     selectIn(alf, sequenceIds, columns, separator);
394   }
395
396   /*
397    * (non-Javadoc)
398    * 
399    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
400    * java.lang.String, java.lang.String, java.lang.String)
401    */
402   public void selectIn(final AlignFrame alf, String sequenceIds,
403           String columns, String sep)
404   {
405     if (sep == null || sep.length() == 0)
406     {
407       sep = separator;
408     }
409     else
410     {
411       if (debug)
412       {
413         System.err.println("Selecting region using separator string '"
414                 + separator + "'");
415       }
416     }
417     // deparse fields
418     String[] ids = separatorListToArray(sequenceIds, sep);
419     String[] cols = separatorListToArray(columns, sep);
420     final SequenceGroup sel = new SequenceGroup();
421     final ColumnSelection csel = new ColumnSelection();
422     AlignmentI al = alf.viewport.getAlignment();
423     SequenceIdMatcher matcher = new SequenceIdMatcher(
424             alf.viewport.getAlignment().getSequencesArray());
425     int start = 0, end = al.getWidth(), alw = al.getWidth();
426     boolean seqsfound = true;
427     if (ids != null && ids.length > 0)
428     {
429       seqsfound = false;
430       for (int i = 0; i < ids.length; i++)
431       {
432         if (ids[i].trim().length() == 0)
433         {
434           continue;
435         }
436         SequenceI sq = matcher.findIdMatch(ids[i]);
437         if (sq != null)
438         {
439           seqsfound = true;
440           sel.addSequence(sq, false);
441         }
442       }
443     }
444     boolean inseqpos = false;
445     if (cols != null && cols.length > 0)
446     {
447       boolean seset = false;
448       for (int i = 0; i < cols.length; i++)
449       {
450         String cl = cols[i].trim();
451         if (cl.length() == 0)
452         {
453           continue;
454         }
455         int p;
456         if ((p = cl.indexOf("-")) > -1)
457         {
458           int from = -1, to = -1;
459           try
460           {
461             from = new Integer(cl.substring(0, p)).intValue();
462             from--;
463           } catch (NumberFormatException ex)
464           {
465             System.err
466                     .println("ERROR: Couldn't parse first integer in range element column selection string '"
467                             + cl + "' - format is 'from-to'");
468             return;
469           }
470           try
471           {
472             to = new Integer(cl.substring(p + 1)).intValue();
473             to--;
474           } catch (NumberFormatException ex)
475           {
476             System.err
477                     .println("ERROR: Couldn't parse second integer in range element column selection string '"
478                             + cl + "' - format is 'from-to'");
479             return;
480           }
481           if (from >= 0 && to >= 0)
482           {
483             // valid range
484             if (from < to)
485             {
486               int t = to;
487               to = from;
488               to = t;
489             }
490             if (!seset)
491             {
492               start = from;
493               end = to;
494               seset = true;
495             }
496             else
497             {
498               // comment to prevent range extension
499               if (start > from)
500               {
501                 start = from;
502               }
503               if (end < to)
504               {
505                 end = to;
506               }
507             }
508             for (int r = from; r <= to; r++)
509             {
510               if (r >= 0 && r < alw)
511               {
512                 csel.addElement(r);
513               }
514             }
515             if (debug)
516             {
517               System.err.println("Range '" + cl + "' deparsed as [" + from
518                       + "," + to + "]");
519             }
520           }
521           else
522           {
523             System.err.println("ERROR: Invalid Range '" + cl
524                     + "' deparsed as [" + from + "," + to + "]");
525           }
526         }
527         else
528         {
529           int r = -1;
530           try
531           {
532             r = new Integer(cl).intValue();
533             r--;
534           } catch (NumberFormatException ex)
535           {
536             if (cl.toLowerCase().equals("sequence"))
537             {
538               // we are in the dataset sequence's coordinate frame.
539               inseqpos = true;
540             }
541             else
542             {
543               System.err
544                       .println("ERROR: Couldn't parse integer from point selection element of column selection string '"
545                               + cl + "'");
546               return;
547             }
548           }
549           if (r >= 0 && r <= alw)
550           {
551             if (!seset)
552             {
553               start = r;
554               end = r;
555               seset = true;
556             }
557             else
558             {
559               // comment to prevent range extension
560               if (start > r)
561               {
562                 start = r;
563               }
564               if (end < r)
565               {
566                 end = r;
567               }
568             }
569             csel.addElement(r);
570             if (debug)
571             {
572               System.err.println("Point selection '" + cl
573                       + "' deparsed as [" + r + "]");
574             }
575           }
576           else
577           {
578             System.err.println("ERROR: Invalid Point selection '" + cl
579                     + "' deparsed as [" + r + "]");
580           }
581         }
582       }
583     }
584     if (seqsfound)
585     {
586       // we only propagate the selection when it was the null selection, or the
587       // given sequences were found in the alignment.
588       if (inseqpos && sel.getSize() > 0)
589       {
590         // assume first sequence provides reference frame ?
591         SequenceI rs = sel.getSequenceAt(0);
592         start = rs.findIndex(start);
593         end = rs.findIndex(end);
594         if (csel != null)
595         {
596           List<Integer> cs = csel.getSelected();
597           csel.clear();
598           for (Integer selectedCol : cs)
599           {
600             csel.addElement(rs.findIndex(selectedCol));
601           }
602         }
603       }
604       sel.setStartRes(start);
605       sel.setEndRes(end);
606       EventQueue.invokeLater(new Runnable()
607       {
608         @Override
609         public void run()
610         {
611           alf.select(sel, csel);
612         }
613       });
614     }
615   }
616
617   /*
618    * (non-Javadoc)
619    * 
620    * @see
621    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesAsAlignment(java.lang.
622    * String, java.lang.String)
623    */
624   public String getSelectedSequencesAsAlignment(String format, String suffix)
625   {
626     return getSelectedSequencesAsAlignmentFrom(getDefaultTargetFrame(),
627             format, suffix);
628   }
629
630   /*
631    * (non-Javadoc)
632    * 
633    * @see
634    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesAsAlignmentFrom(jalview
635    * .appletgui.AlignFrame, java.lang.String, java.lang.String)
636    */
637   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
638           String format, String suffix)
639   {
640     try
641     {
642       boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
643       if (alf.viewport.getSelectionGroup() != null)
644       {
645         // JBPNote: getSelectionAsNewSequence behaviour has changed - this
646         // method now returns a full copy of sequence data
647         // TODO consider using getSequenceSelection instead here
648         String reply = new AppletFormatAdapter().formatSequences(format,
649                 new Alignment(alf.viewport.getSelectionAsNewSequence()),
650                 seqlimits);
651         return reply;
652       }
653     } catch (Exception ex)
654     {
655       ex.printStackTrace();
656       return "Error retrieving alignment in " + format + " format. ";
657     }
658     return "";
659   }
660
661   /*
662    * (non-Javadoc)
663    * 
664    * @see jalview.bin.JalviewLiteJsApi#getAlignmentOrder()
665    */
666   public String getAlignmentOrder()
667   {
668     return getAlignmentOrderFrom(getDefaultTargetFrame());
669   }
670
671   /*
672    * (non-Javadoc)
673    * 
674    * @see
675    * jalview.bin.JalviewLiteJsApi#getAlignmentOrderFrom(jalview.appletgui.AlignFrame
676    * )
677    */
678   public String getAlignmentOrderFrom(AlignFrame alf)
679   {
680     return getAlignmentOrderFrom(alf, separator);
681   }
682
683   /*
684    * (non-Javadoc)
685    * 
686    * @see
687    * jalview.bin.JalviewLiteJsApi#getAlignmentOrderFrom(jalview.appletgui.AlignFrame
688    * , java.lang.String)
689    */
690   public String getAlignmentOrderFrom(AlignFrame alf, String sep)
691   {
692     AlignmentI alorder = alf.getAlignViewport().getAlignment();
693     String[] order = new String[alorder.getHeight()];
694     for (int i = 0; i < order.length; i++)
695     {
696       order[i] = alorder.getSequenceAt(i).getName();
697     }
698     return arrayToSeparatorList(order);
699   }
700
701   /*
702    * (non-Javadoc)
703    * 
704    * @see jalview.bin.JalviewLiteJsApi#orderBy(java.lang.String,
705    * java.lang.String)
706    */
707   public String orderBy(String order, String undoName)
708   {
709     return orderBy(order, undoName, separator);
710   }
711
712   /*
713    * (non-Javadoc)
714    * 
715    * @see jalview.bin.JalviewLiteJsApi#orderBy(java.lang.String,
716    * java.lang.String, java.lang.String)
717    */
718   public String orderBy(String order, String undoName, String sep)
719   {
720     return orderAlignmentBy(getDefaultTargetFrame(), order, undoName, sep);
721   }
722
723   /*
724    * (non-Javadoc)
725    * 
726    * @see
727    * jalview.bin.JalviewLiteJsApi#orderAlignmentBy(jalview.appletgui.AlignFrame,
728    * java.lang.String, java.lang.String, java.lang.String)
729    */
730   public String orderAlignmentBy(AlignFrame alf, String order,
731           String undoName, String sep)
732   {
733     String[] ids = separatorListToArray(order, sep);
734     SequenceI[] sqs = null;
735     if (ids != null && ids.length > 0)
736     {
737       SequenceIdMatcher matcher = new SequenceIdMatcher(
738               alf.viewport.getAlignment().getSequencesArray());
739       int s = 0;
740       sqs = new SequenceI[ids.length];
741       for (int i = 0; i < ids.length; i++)
742       {
743         if (ids[i].trim().length() == 0)
744         {
745           continue;
746         }
747         SequenceI sq = matcher.findIdMatch(ids[i]);
748         if (sq != null)
749         {
750           sqs[s++] = sq;
751         }
752       }
753       if (s > 0)
754       {
755         SequenceI[] sqq = new SequenceI[s];
756         System.arraycopy(sqs, 0, sqq, 0, s);
757         sqs = sqq;
758       }
759       else
760       {
761         sqs = null;
762       }
763     }
764     if (sqs == null)
765     {
766       return "";
767     }
768     ;
769     final AlignmentOrder aorder = new AlignmentOrder(sqs);
770
771     if (undoName != null && undoName.trim().length() == 0)
772     {
773       undoName = null;
774     }
775     final String _undoName = undoName;
776     // TODO: deal with synchronization here: cannot raise any events until after
777     // this has returned.
778     return alf.sortBy(aorder, _undoName) ? TRUE : "";
779   }
780
781   /*
782    * (non-Javadoc)
783    * 
784    * @see jalview.bin.JalviewLiteJsApi#getAlignment(java.lang.String)
785    */
786   public String getAlignment(String format)
787   {
788     return getAlignmentFrom(getDefaultTargetFrame(), format, TRUE);
789   }
790
791   /*
792    * (non-Javadoc)
793    * 
794    * @see
795    * jalview.bin.JalviewLiteJsApi#getAlignmentFrom(jalview.appletgui.AlignFrame,
796    * java.lang.String)
797    */
798   public String getAlignmentFrom(AlignFrame alf, String format)
799   {
800     return getAlignmentFrom(alf, format, TRUE);
801   }
802
803   /*
804    * (non-Javadoc)
805    * 
806    * @see jalview.bin.JalviewLiteJsApi#getAlignment(java.lang.String,
807    * java.lang.String)
808    */
809   public String getAlignment(String format, String suffix)
810   {
811     return getAlignmentFrom(getDefaultTargetFrame(), format, suffix);
812   }
813
814   /*
815    * (non-Javadoc)
816    * 
817    * @see
818    * jalview.bin.JalviewLiteJsApi#getAlignmentFrom(jalview.appletgui.AlignFrame,
819    * java.lang.String, java.lang.String)
820    */
821   public String getAlignmentFrom(AlignFrame alf, String format,
822           String suffix)
823   {
824     try
825     {
826       boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
827
828       String reply = new AppletFormatAdapter().formatSequences(format,
829               alf.viewport.getAlignment(), seqlimits);
830       return reply;
831     } catch (Exception ex)
832     {
833       ex.printStackTrace();
834       return "Error retrieving alignment in " + format + " format. ";
835     }
836   }
837
838   /*
839    * (non-Javadoc)
840    * 
841    * @see jalview.bin.JalviewLiteJsApi#loadAnnotation(java.lang.String)
842    */
843   public void loadAnnotation(String annotation)
844   {
845     loadAnnotationFrom(getDefaultTargetFrame(), annotation);
846   }
847
848   /*
849    * (non-Javadoc)
850    * 
851    * @see
852    * jalview.bin.JalviewLiteJsApi#loadAnnotationFrom(jalview.appletgui.AlignFrame
853    * , java.lang.String)
854    */
855   public void loadAnnotationFrom(AlignFrame alf, String annotation)
856   {
857     if (new AnnotationFile().annotateAlignmentView(alf.getAlignViewport(),
858             annotation, AppletFormatAdapter.PASTE))
859     {
860       alf.alignPanel.fontChanged();
861       alf.alignPanel.setScrollValues(0, 0);
862     }
863     else
864     {
865       alf.parseFeaturesFile(annotation, AppletFormatAdapter.PASTE);
866     }
867   }
868
869   /*
870    * (non-Javadoc)
871    * 
872    * @see jalview.bin.JalviewLiteJsApi#loadAnnotation(java.lang.String)
873    */
874   public void loadFeatures(String features, boolean autoenabledisplay)
875   {
876     loadFeaturesFrom(getDefaultTargetFrame(), features, autoenabledisplay);
877   }
878
879   /*
880    * (non-Javadoc)
881    * 
882    * @see
883    * jalview.bin.JalviewLiteJsApi#loadAnnotationFrom(jalview.appletgui.AlignFrame
884    * , java.lang.String)
885    */
886   public boolean loadFeaturesFrom(AlignFrame alf, String features,
887           boolean autoenabledisplay)
888   {
889     return alf.parseFeaturesFile(features, AppletFormatAdapter.PASTE,
890             autoenabledisplay);
891   }
892
893   /*
894    * (non-Javadoc)
895    * 
896    * @see jalview.bin.JalviewLiteJsApi#getFeatures(java.lang.String)
897    */
898   public String getFeatures(String format)
899   {
900     return getFeaturesFrom(getDefaultTargetFrame(), format);
901   }
902
903   /*
904    * (non-Javadoc)
905    * 
906    * @see
907    * jalview.bin.JalviewLiteJsApi#getFeaturesFrom(jalview.appletgui.AlignFrame,
908    * java.lang.String)
909    */
910   public String getFeaturesFrom(AlignFrame alf, String format)
911   {
912     return alf.outputFeatures(false, format);
913   }
914
915   /*
916    * (non-Javadoc)
917    * 
918    * @see jalview.bin.JalviewLiteJsApi#getAnnotation()
919    */
920   public String getAnnotation()
921   {
922     return getAnnotationFrom(getDefaultTargetFrame());
923   }
924
925   /*
926    * (non-Javadoc)
927    * 
928    * @see
929    * jalview.bin.JalviewLiteJsApi#getAnnotationFrom(jalview.appletgui.AlignFrame
930    * )
931    */
932   public String getAnnotationFrom(AlignFrame alf)
933   {
934     return alf.outputAnnotations(false);
935   }
936
937   /*
938    * (non-Javadoc)
939    * 
940    * @see jalview.bin.JalviewLiteJsApi#newView()
941    */
942   public AlignFrame newView()
943   {
944     return newViewFrom(getDefaultTargetFrame());
945   }
946
947   /*
948    * (non-Javadoc)
949    * 
950    * @see jalview.bin.JalviewLiteJsApi#newView(java.lang.String)
951    */
952   public AlignFrame newView(String name)
953   {
954     return newViewFrom(getDefaultTargetFrame(), name);
955   }
956
957   /*
958    * (non-Javadoc)
959    * 
960    * @see jalview.bin.JalviewLiteJsApi#newViewFrom(jalview.appletgui.AlignFrame)
961    */
962   public AlignFrame newViewFrom(AlignFrame alf)
963   {
964     return alf.newView(null);
965   }
966
967   /*
968    * (non-Javadoc)
969    * 
970    * @see jalview.bin.JalviewLiteJsApi#newViewFrom(jalview.appletgui.AlignFrame,
971    * java.lang.String)
972    */
973   public AlignFrame newViewFrom(AlignFrame alf, String name)
974   {
975     return alf.newView(name);
976   }
977
978   /*
979    * (non-Javadoc)
980    * 
981    * @see jalview.bin.JalviewLiteJsApi#loadAlignment(java.lang.String,
982    * java.lang.String)
983    */
984   public AlignFrame loadAlignment(String text, String title)
985   {
986     AlignmentI al = null;
987
988     String format = new IdentifyFile().Identify(text,
989             AppletFormatAdapter.PASTE);
990     try
991     {
992       al = new AppletFormatAdapter().readFile(text,
993               AppletFormatAdapter.PASTE, format);
994       if (al.getHeight() > 0)
995       {
996         return new AlignFrame(al, this, title, false);
997       }
998     } catch (java.io.IOException ex)
999     {
1000       ex.printStackTrace();
1001     }
1002     return null;
1003   }
1004
1005   /*
1006    * (non-Javadoc)
1007    * 
1008    * @see jalview.bin.JalviewLiteJsApi#setMouseoverListener(java.lang.String)
1009    */
1010   public void setMouseoverListener(String listener)
1011   {
1012     setMouseoverListener(currentAlignFrame, listener);
1013   }
1014
1015   private Vector<JSFunctionExec> javascriptListeners = new Vector<JSFunctionExec>();
1016
1017   /*
1018    * (non-Javadoc)
1019    * 
1020    * @see
1021    * jalview.bin.JalviewLiteJsApi#setMouseoverListener(jalview.appletgui.AlignFrame
1022    * , java.lang.String)
1023    */
1024   public void setMouseoverListener(AlignFrame af, String listener)
1025   {
1026     if (listener != null)
1027     {
1028       listener = listener.trim();
1029       if (listener.length() == 0)
1030       {
1031         System.err
1032                 .println("jalview Javascript error: Ignoring empty function for mouseover listener.");
1033         return;
1034       }
1035     }
1036     MouseOverListener mol = new MouseOverListener(
1037             this, af, listener);
1038     javascriptListeners.addElement(mol);
1039     StructureSelectionManager.getStructureSelectionManager(this)
1040             .addStructureViewerListener(mol);
1041     if (debug)
1042     {
1043       System.err.println("Added a mouseover listener for "
1044               + ((af == null) ? "All frames" : "Just views for "
1045                       + af.getAlignViewport().getSequenceSetId()));
1046       System.err.println("There are now " + javascriptListeners.size()
1047               + " listeners in total.");
1048     }
1049   }
1050
1051   /*
1052    * (non-Javadoc)
1053    * 
1054    * @see jalview.bin.JalviewLiteJsApi#setSelectionListener(java.lang.String)
1055    */
1056   public void setSelectionListener(String listener)
1057   {
1058     setSelectionListener(null, listener);
1059   }
1060
1061   /*
1062    * (non-Javadoc)
1063    * 
1064    * @see
1065    * jalview.bin.JalviewLiteJsApi#setSelectionListener(jalview.appletgui.AlignFrame
1066    * , java.lang.String)
1067    */
1068   public void setSelectionListener(AlignFrame af, String listener)
1069   {
1070     if (listener != null)
1071     {
1072       listener = listener.trim();
1073       if (listener.length() == 0)
1074       {
1075         System.err
1076                 .println("jalview Javascript error: Ignoring empty function for selection listener.");
1077         return;
1078       }
1079     }
1080     JsSelectionSender mol = new JsSelectionSender(this, af, listener);
1081     javascriptListeners.addElement(mol);
1082     StructureSelectionManager.getStructureSelectionManager(this)
1083             .addSelectionListener(mol);
1084     if (debug)
1085     {
1086       System.err.println("Added a selection listener for "
1087               + ((af == null) ? "All frames" : "Just views for "
1088                       + af.getAlignViewport().getSequenceSetId()));
1089       System.err.println("There are now " + javascriptListeners.size()
1090               + " listeners in total.");
1091     }
1092   }
1093
1094   /*
1095    * (non-Javadoc)
1096    * 
1097    * @see jalview.bin.JalviewLiteJsApi#setStructureListener(java.lang.String,
1098    * java.lang.String)
1099    */
1100   public void setStructureListener(String listener, String modelSet)
1101   {
1102     if (listener != null)
1103     {
1104       listener = listener.trim();
1105       if (listener.length() == 0)
1106       {
1107         System.err
1108                 .println("jalview Javascript error: Ignoring empty function for selection listener.");
1109         return;
1110       }
1111     }
1112     MouseOverStructureListener mol = new MouseOverStructureListener(
1113             this, listener, separatorListToArray(modelSet));
1114     javascriptListeners.addElement(mol);
1115     StructureSelectionManager.getStructureSelectionManager(this)
1116             .addStructureViewerListener(mol);
1117     if (debug)
1118     {
1119       System.err.println("Added a javascript structure viewer listener '"
1120               + listener + "'");
1121       System.err.println("There are now " + javascriptListeners.size()
1122               + " listeners in total.");
1123     }
1124   }
1125
1126   /*
1127    * (non-Javadoc)
1128    * 
1129    * @see
1130    * jalview.bin.JalviewLiteJsApi#removeJavascriptListener(jalview.appletgui
1131    * .AlignFrame, java.lang.String)
1132    */
1133   public void removeJavascriptListener(AlignFrame af, String listener)
1134   {
1135     if (listener != null)
1136     {
1137       listener = listener.trim();
1138       if (listener.length() == 0)
1139       {
1140         listener = null;
1141       }
1142     }
1143     boolean rprt = false;
1144     for (int ms = 0, msSize = javascriptListeners.size(); ms < msSize;)
1145     {
1146       Object lstn = javascriptListeners.elementAt(ms);
1147       JsCallBack lstner = (JsCallBack) lstn;
1148       if ((af == null || lstner.getAlignFrame() == af)
1149               && (listener == null || lstner.getListenerFunction().equals(
1150                       listener)))
1151       {
1152         javascriptListeners.removeElement(lstner);
1153         msSize--;
1154         if (lstner instanceof SelectionListener)
1155         {
1156           StructureSelectionManager.getStructureSelectionManager(this)
1157                   .removeSelectionListener((SelectionListener) lstner);
1158         }
1159         else
1160         {
1161           StructureSelectionManager.getStructureSelectionManager(this)
1162                   .removeStructureViewerListener(lstner, null);
1163         }
1164         rprt = debug;
1165         if (debug)
1166         {
1167           System.err.println("Removed listener '" + listener + "'");
1168         }
1169       }
1170       else
1171       {
1172         ms++;
1173       }
1174     }
1175     if (rprt)
1176     {
1177       System.err.println("There are now " + javascriptListeners.size()
1178               + " listeners in total.");
1179     }
1180   }
1181
1182   public void stop()
1183   {
1184     System.err.println("Applet " + getName() + " stop().");
1185     tidyUp();
1186   }
1187
1188   public void destroy()
1189   {
1190     System.err.println("Applet " + getName() + " destroy().");
1191     tidyUp();
1192   }
1193
1194   private void tidyUp()
1195   {
1196     removeAll();
1197     if (currentAlignFrame != null && currentAlignFrame.viewport != null
1198             && currentAlignFrame.viewport.applet != null)
1199     {
1200       AlignViewport av = currentAlignFrame.viewport;
1201       currentAlignFrame.closeMenuItem_actionPerformed();
1202       av.applet = null;
1203       currentAlignFrame = null;
1204     }
1205     if (javascriptListeners != null)
1206     {
1207       while (javascriptListeners.size() > 0)
1208       {
1209         JSFunctionExec mol = javascriptListeners.elementAt(0);
1210         javascriptListeners.removeElement(mol);
1211         if (mol instanceof SelectionListener)
1212         {
1213           StructureSelectionManager.getStructureSelectionManager(this)
1214                   .removeSelectionListener((SelectionListener) mol);
1215         }
1216         else
1217         {
1218           StructureSelectionManager.getStructureSelectionManager(this)
1219                   .removeStructureViewerListener(mol, null);
1220         }
1221         mol.jvlite = null;
1222       }
1223     }
1224     if (jsFunctionExec != null)
1225     {
1226       jsFunctionExec.stopQueue();
1227       jsFunctionExec.jvlite = null;
1228     }
1229     initialAlignFrame = null;
1230     jsFunctionExec = null;
1231     javascriptListeners = null;
1232     StructureSelectionManager.release(this);
1233   }
1234
1235   private JSFunctionExec jsFunctionExec;
1236
1237   /*
1238    * (non-Javadoc)
1239    * 
1240    * @see jalview.bin.JalviewLiteJsApi#mouseOverStructure(java.lang.String,
1241    * java.lang.String, java.lang.String)
1242    */
1243   public void mouseOverStructure(final String pdbResNum,
1244           final String chain, final String pdbfile)
1245   {
1246     final StructureSelectionManagerProvider me = this;
1247     java.awt.EventQueue.invokeLater(new Runnable()
1248     {
1249       @Override
1250       public void run()
1251       {
1252         try
1253         {
1254           StructureSelectionManager.getStructureSelectionManager(me)
1255                   .mouseOverStructure(new Integer(pdbResNum).intValue(),
1256                           chain, pdbfile);
1257           if (debug)
1258           {
1259             System.err.println("mouseOver for '" + pdbResNum
1260                     + "' in chain '" + chain + "' in structure '" + pdbfile
1261                     + "'");
1262           }
1263         } catch (NumberFormatException e)
1264         {
1265           System.err.println("Ignoring invalid residue number string '"
1266                   + pdbResNum + "'");
1267         }
1268
1269       }
1270     });
1271   }
1272
1273   /*
1274    * (non-Javadoc)
1275    * 
1276    * @see
1277    * jalview.bin.JalviewLiteJsApi#scrollViewToIn(jalview.appletgui.AlignFrame,
1278    * java.lang.String, java.lang.String)
1279    */
1280   public void scrollViewToIn(final AlignFrame alf, final String topRow,
1281           final String leftHandColumn)
1282   {
1283     java.awt.EventQueue.invokeLater(new Runnable()
1284     {
1285       @Override
1286       public void run()
1287       {
1288         try
1289         {
1290           alf.scrollTo(new Integer(topRow).intValue(), new Integer(
1291                   leftHandColumn).intValue());
1292
1293         } catch (Exception ex)
1294         {
1295           System.err.println("Couldn't parse integer arguments (topRow='"
1296                   + topRow + "' and leftHandColumn='" + leftHandColumn
1297                   + "')");
1298           ex.printStackTrace();
1299         }
1300       }
1301     });
1302   }
1303
1304   /*
1305    * (non-Javadoc)
1306    * 
1307    * @see
1308    * jalview.javascript.JalviewLiteJsApi#scrollViewToRowIn(jalview.appletgui
1309    * .AlignFrame, java.lang.String)
1310    */
1311   @Override
1312   public void scrollViewToRowIn(final AlignFrame alf, final String topRow)
1313   {
1314
1315     java.awt.EventQueue.invokeLater(new Runnable()
1316     {
1317       @Override
1318       public void run()
1319       {
1320         try
1321         {
1322           alf.scrollToRow(new Integer(topRow).intValue());
1323
1324         } catch (Exception ex)
1325         {
1326           System.err.println("Couldn't parse integer arguments (topRow='"
1327                   + topRow + "')");
1328           ex.printStackTrace();
1329         }
1330
1331       }
1332     });
1333   }
1334
1335   /*
1336    * (non-Javadoc)
1337    * 
1338    * @see
1339    * jalview.javascript.JalviewLiteJsApi#scrollViewToColumnIn(jalview.appletgui
1340    * .AlignFrame, java.lang.String)
1341    */
1342   @Override
1343   public void scrollViewToColumnIn(final AlignFrame alf,
1344           final String leftHandColumn)
1345   {
1346     EventQueue.invokeLater(new Runnable()
1347     {
1348
1349       @Override
1350       public void run()
1351       {
1352         try
1353         {
1354           alf.scrollToColumn(new Integer(leftHandColumn).intValue());
1355
1356         } catch (Exception ex)
1357         {
1358           System.err
1359                   .println("Couldn't parse integer arguments (leftHandColumn='"
1360                           + leftHandColumn + "')");
1361           ex.printStackTrace();
1362         }
1363       }
1364     });
1365
1366   }
1367
1368   // //////////////////////////////////////////////
1369   // //////////////////////////////////////////////
1370
1371   public static int lastFrameX = 200;
1372
1373   public static int lastFrameY = 200;
1374
1375   boolean fileFound = true;
1376
1377   JButton launcher = new JButton(
1378           MessageManager.getString("label.start_jalview"));
1379
1380   /**
1381    * The currentAlignFrame is static, it will change if and when the user
1382    * selects a new window. Note that it will *never* point back to the embedded
1383    * AlignFrame if the applet is started as embedded on the page and then
1384    * afterwards a new view is created.
1385    */
1386   public AlignFrame currentAlignFrame = null;
1387
1388   /**
1389    * This is the first frame to be displayed, and does not change. API calls
1390    * will default to this instance if currentAlignFrame is null.
1391    */
1392   AlignFrame initialAlignFrame = null;
1393
1394   private boolean checkedForJmol = false; // ensure we don't check for jmol
1395
1396   // every time the app is re-inited
1397
1398   public boolean jmolAvailable = false;
1399
1400   private boolean alignPdbStructures = false;
1401
1402   /**
1403    * use an external structure viewer exclusively (no jmols or MCViews will be
1404    * opened by JalviewLite itself)
1405    */
1406   public boolean useXtrnalSviewer = false;
1407
1408         private boolean haveShownLoadMessage;
1409
1410   static String builddate = null, version = null, installation = null;
1411
1412   private static void initBuildDetails()
1413   {
1414     if (builddate == null)
1415     {
1416       builddate = "unknown";
1417       version = "test";
1418       installation = "Webstart";
1419       java.net.URL url = JalviewLite.class
1420               .getResource("/.build_properties");
1421       if (url != null)
1422       {
1423         try
1424         {
1425           BufferedReader reader = new BufferedReader(new InputStreamReader(
1426                   url.openStream()));
1427           String line;
1428           while ((line = reader.readLine()) != null)
1429           {
1430             if (line.indexOf("VERSION") > -1)
1431             {
1432               version = line.substring(line.indexOf("=") + 1);
1433             }
1434             if (line.indexOf("BUILD_DATE") > -1)
1435             {
1436               builddate = line.substring(line.indexOf("=") + 1);
1437             }
1438             if (line.indexOf("INSTALLATION") > -1)
1439             {
1440               installation = line.substring(line.indexOf("=") + 1);
1441             }
1442           }
1443         } catch (Exception ex)
1444         {
1445           ex.printStackTrace();
1446         }
1447       }
1448     }
1449   }
1450
1451   public static String getBuildDate()
1452   {
1453     initBuildDetails();
1454     return builddate;
1455   }
1456
1457   public static String getInstallation()
1458   {
1459     initBuildDetails();
1460     return installation;
1461   }
1462
1463   public static String getVersion()
1464   {
1465     initBuildDetails();
1466     return version;
1467   }
1468
1469   // public JSObject scriptObject = null;
1470
1471   /**
1472    * init method for Jalview Applet
1473    */
1474   public void init()
1475   {
1476         
1477         setParams(); // BH
1478         
1479     // remove any handlers that might be hanging around from an earlier instance
1480     try
1481     {
1482       if (debug)
1483       {
1484         System.err.println("Applet context is '"
1485                 + getAppletContext().getClass().toString() + "'");
1486       }
1487       JSObject scriptObject = JSObject.getWindow(this);
1488       if (debug && scriptObject != null)
1489       {
1490         System.err.println("Applet has Javascript callback support.");
1491       }
1492
1493     } catch (Exception ex)
1494     {
1495       System.err
1496               .println("Warning: No JalviewLite javascript callbacks available.");
1497       if (debug)
1498       {
1499         ex.printStackTrace();
1500       }
1501     }
1502     /**
1503      * turn on extra applet debugging
1504      */
1505     
1506     if (debug)
1507     {
1508
1509       System.err.println("JalviewLite Version " + getVersion());
1510       System.err.println("Build Date : " + getBuildDate());
1511       System.err.println("Installation : " + getInstallation());
1512
1513     }
1514     if (externalstructureviewer != null)
1515     {
1516       useXtrnalSviewer = externalstructureviewer.trim().toLowerCase()
1517               .equals(TRUE);
1518     }
1519     /**
1520      * get the separator parameter if present
1521      */
1522     if (sep != null)
1523     {
1524       if (sep.length() > 0)
1525       {
1526         separator = sep;
1527         if (debug)
1528         {
1529           System.err.println("Separator set to '" + separator + "'");
1530         }
1531       }
1532       else
1533       {
1534         throw new Error(MessageManager.getString("error.invalid_separator_parameter"));
1535       }
1536     }
1537     int r = 255;
1538     int g = 255;
1539     int b = 255;
1540
1541     if (rgb != null)
1542     {
1543       try
1544       {
1545         r = Integer.parseInt(rgb.substring(0, 2), 16);
1546         g = Integer.parseInt(rgb.substring(2, 4), 16);
1547         b = Integer.parseInt(rgb.substring(4, 6), 16);
1548       } catch (Exception ex)
1549       {
1550         r = 255;
1551         g = 255;
1552         b = 255;
1553       }
1554     }
1555     rgb = labelColour;
1556     if (rgb != null)
1557     {
1558       launcher.setLabel(rgb);
1559     }
1560
1561     setBackground(new Color(r, g, b));
1562
1563     if (startupFile == null)
1564     {
1565       // Maybe the sequences are added as parameters
1566       StringBuffer data = new StringBuffer("PASTE");
1567       int i = 1;
1568       while ((startupFile = getParameter("sequence" + i)) != null)
1569       {
1570         data.append(startupFile.toString() + "\n");
1571         i++;
1572       }
1573       if (data.length() > 5)
1574       {
1575         startupFile = data.toString();
1576       }
1577     }
1578     if (!enableSplitFrame)
1579     {
1580         file2 = null;
1581     }
1582
1583     if (embedded)
1584     {
1585       LoadingThread loader = new LoadingThread(startupFile, file2, this);
1586       /**
1587        * @j2sNative
1588        * 
1589        * loader.run();
1590        */
1591       {
1592         loader.start();
1593       }
1594     }
1595     else if (startupFile != null)
1596     {
1597       /*
1598        * Start the applet immediately or show a button to start it
1599        */
1600       if (!showButton)
1601       {
1602         LoadingThread loader = new LoadingThread(startupFile, file2, this);
1603         loader.start();
1604       }
1605       else
1606       {
1607         add(launcher);
1608         launcher.addActionListener(new java.awt.event.ActionListener()
1609         {
1610           public void actionPerformed(ActionEvent e)
1611           {
1612             LoadingThread loader = new LoadingThread(startupFile, file2,
1613                     JalviewLite.this);
1614             loader.start();
1615           }
1616         });
1617       }
1618     }
1619     else
1620     {
1621       // jalview initialisation with no alignment. loadAlignment() method can
1622       // still be called to open new alignments.
1623       startupFile = "NO FILE";
1624       fileFound = false;
1625       callInitCallback();
1626     }
1627   }
1628
1629   private void initLiveConnect()
1630   {
1631     // try really hard to get the liveConnect thing working
1632     boolean notFailed = false;
1633     int tries = 0;
1634     while (!notFailed && tries < 10)
1635     {
1636       if (tries > 0)
1637       {
1638         System.err.println("LiveConnect request thread going to sleep.");
1639       }
1640       try
1641       {
1642         Thread.sleep(700 * (1 + tries));
1643       } catch (InterruptedException q)
1644       {
1645       }
1646       ;
1647       if (tries++ > 0)
1648       {
1649         System.err.println("LiveConnect request thread woken up.");
1650       }
1651       try
1652       {
1653         JSObject scriptObject = JSObject.getWindow(this);
1654         if (scriptObject.eval("navigator") != null)
1655         {
1656           notFailed = true;
1657         }
1658       } catch (Exception jsex)
1659       {
1660         System.err.println("Attempt " + tries
1661                 + " to access LiveConnect javascript failed.");
1662       }
1663     }
1664   }
1665
1666   private void callInitCallback()
1667   {
1668     if (initjscallback == null)
1669     {
1670       return;
1671     }
1672     initjscallback = initjscallback.trim();
1673     if (initjscallback.length() > 0)
1674     {
1675       JSObject scriptObject = null;
1676       try
1677       {
1678         scriptObject = JSObject.getWindow(this);
1679       } catch (Exception ex)
1680       {
1681       }
1682       ;
1683       // try really hard to let the browser plugin know we want liveconnect
1684       initLiveConnect();
1685
1686       if (scriptObject != null)
1687       {
1688         try
1689         {
1690           // do onInit with the JS executor thread
1691           new JSFunctionExec(this).executeJavascriptFunction(true,
1692                   initjscallback, null, "Calling oninit callback '"
1693                           + initjscallback + "'.");
1694         } catch (Exception e)
1695         {
1696           System.err.println("Exception when executing _oninit callback '"
1697                   + initjscallback + "'.");
1698           e.printStackTrace();
1699         }
1700       }
1701       else
1702       {
1703         System.err.println("Not executing _oninit callback '"
1704                 + initjscallback + "' - no scripting allowed.");
1705       }
1706     }
1707   }
1708
1709   /**
1710    * Initialises and displays a new java.awt.Frame
1711    * 
1712    * @param frame
1713    *          java.awt.Frame to be displayed
1714    * @param title
1715    *          title of new frame
1716    * @param width
1717    *          width if new frame
1718    * @param height
1719    *          height of new frame
1720    */
1721   public static void addFrame(final JFrame frame, String title, int width,
1722           int height)
1723   {
1724     frame.setLocation(lastFrameX, lastFrameY);
1725     lastFrameX += 40;
1726     lastFrameY += 40;
1727     frame.setSize(width, height);
1728     frame.setTitle(title);
1729     frame.addWindowListener(new WindowAdapter()
1730     {
1731       public void windowClosing(WindowEvent e)
1732       {
1733         if (frame instanceof AlignFrame)
1734         {
1735           AlignViewport vp = ((AlignFrame) frame).viewport;
1736           ((AlignFrame) frame).closeMenuItem_actionPerformed();
1737           if (vp.applet.currentAlignFrame == frame)
1738           {
1739             vp.applet.currentAlignFrame = null;
1740           }
1741           vp.applet = null;
1742           vp = null;
1743
1744         }
1745         lastFrameX -= 40;
1746         lastFrameY -= 40;
1747         if (frame instanceof EmbmenuFrame)
1748         {
1749           ((EmbmenuFrame) frame).destroyMenus();
1750         }
1751         frame.setMenuBar(null);
1752         frame.dispose();
1753       }
1754
1755       public void windowActivated(WindowEvent e)
1756       {
1757         if (frame instanceof AlignFrame)
1758         {
1759           ((AlignFrame) frame).viewport.applet.currentAlignFrame = (AlignFrame) frame;
1760           if (debug)
1761           {
1762             System.err.println("Activated window " + frame);
1763           }
1764         }
1765         // be good.
1766         super.windowActivated(e);
1767       }
1768       /*
1769        * Probably not necessary to do this - see TODO above. (non-Javadoc)
1770        * 
1771        * @see
1772        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
1773        * )
1774        * 
1775        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
1776        * frame) { currentAlignFrame = null; if (debug) {
1777        * System.err.println("Deactivated window "+frame); } }
1778        * super.windowDeactivated(e); }
1779        */
1780     });
1781     frame.setVisible(true);
1782   }
1783
1784   /**
1785    * This paints the background surrounding the "Launch Jalview button" <br>
1786    * <br>
1787    * If file given in parameter not found, displays error message
1788    * 
1789    * @param g
1790    *          graphics context
1791    */
1792   public void paintComponent(Graphics g)
1793   {
1794     if (!fileFound)
1795     {
1796       g.setColor(new Color(200, 200, 200));
1797       g.setColor(Color.cyan);
1798       g.fillRect(0, 0, getSize().width, getSize().height);
1799       g.setColor(Color.red);
1800       awt2swing.Util.drawString(g, 
1801               MessageManager.getString("label.jalview_cannot_open_file"),
1802               5, 15);
1803       awt2swing.Util.drawString(g, "\"" + startupFile + "\"", 5, 30);
1804     }
1805     else if (embedded && !haveShownLoadMessage)
1806     {
1807       g.setColor(Color.black);
1808       g.setFont(new Font("Arial", Font.BOLD, 24));
1809       awt2swing.Util.drawString(g, MessageManager.getString("label.jalview_applet"), 50,
1810               getSize().height / 2 - 30);
1811       awt2swing.Util.drawString(g, MessageManager.getString("label.loading_data") + "...",
1812               50, getSize().height / 2);
1813       haveShownLoadMessage = true;
1814     }
1815   }
1816
1817   /**
1818    * get all components associated with the applet of the given type
1819    * 
1820    * @param class1
1821    * @return
1822    */
1823   public Vector getAppletWindow(Class class1)
1824   {
1825     Vector wnds = new Vector();
1826     Component[] cmp = getComponents();
1827     if (cmp != null)
1828     {
1829       for (int i = 0; i < cmp.length; i++)
1830       {
1831         if (class1.isAssignableFrom(cmp[i].getClass()))
1832         {
1833           wnds.addElement(cmp);
1834         }
1835       }
1836     }
1837     return wnds;
1838   }
1839
1840   class LoadJmolThread extends Thread
1841   {
1842     private boolean running = false;
1843
1844     public void run()
1845     {
1846       if (running || checkedForJmol)
1847       {
1848         return;
1849       }
1850       running = true;
1851       if (checkForJmol)
1852       {
1853         try
1854         {
1855           if (!System.getProperty("java.version").startsWith("1.1"))
1856           {
1857             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
1858             jmolAvailable = true;
1859           }
1860           if (!jmolAvailable)
1861           {
1862             System.out
1863                     .println("Jmol not available - Using MCview for structures");
1864           }
1865         } catch (java.lang.ClassNotFoundException ex)
1866         {
1867         }
1868       }
1869       else
1870       {
1871         jmolAvailable = false;
1872         if (debug)
1873         {
1874           System.err
1875                   .println("Skipping Jmol check. Will use MCView (probably)");
1876         }
1877       }
1878       checkedForJmol = true;
1879       running = false;
1880     }
1881
1882     public boolean notFinished()
1883     {
1884       return running || !checkedForJmol;
1885     }
1886   }
1887
1888   class LoadingThread extends Thread
1889   {
1890     /**
1891      * State variable: protocol for access to file source
1892      */
1893     String protocol;
1894
1895     String _file; // alignment file or URL spec
1896
1897     String _file2; // second alignment file or URL spec
1898
1899     JalviewLite applet;
1900
1901     private void dbgMsg(String msg)
1902     {
1903       if (JalviewLite.debug)
1904       {
1905         System.err.println(msg);
1906       }
1907     }
1908
1909     /**
1910      * update the protocol state variable for accessing the datasource located
1911      * by file.
1912      * 
1913      * @param file
1914      * @return possibly updated datasource string
1915      */
1916     public String setProtocolState(String file)
1917     {
1918       if (file.startsWith("PASTE"))
1919       {
1920         file = file.substring(5);
1921         protocol = AppletFormatAdapter.PASTE;
1922       }
1923       else if (inArchive(file))
1924       {
1925         protocol = AppletFormatAdapter.CLASSLOADER;
1926       }
1927       else
1928       {
1929         file = addProtocol(file);
1930         protocol = AppletFormatAdapter.URL;
1931       }
1932       dbgMsg("Protocol identified as '" + protocol + "'");
1933       return file;
1934     }
1935
1936     public LoadingThread(String file, String file2, JalviewLite _applet)
1937     {
1938       this._file = file;
1939       this._file2 = file2;
1940       applet = _applet;
1941     }
1942
1943                 public void run() {
1944                         /**
1945                          * 
1946                          * @j2sNative
1947                          * 
1948                          * 
1949                          * System.out.println("BYPASSING JMOL LOADING FOR NOW. THIS WILL BE DONE ANOTHER WAY")
1950                          * 
1951                          */
1952                         {
1953                                 LoadJmolThread jmolchecker = new LoadJmolThread();
1954                                 jmolchecker.start();
1955                                 while (jmolchecker.notFinished()) {
1956                                         // wait around until the Jmol check is complete.
1957                                         try {
1958                                                 Thread.sleep(2);
1959                                         } catch (Exception e) {
1960                                         }
1961                                 }
1962                         }
1963                         startLoading();
1964                         // applet.callInitCallback();
1965                 }
1966
1967     /**
1968      * Load the alignment and any related files as specified by applet
1969      * parameters
1970      */
1971     private void startLoading()
1972     {
1973       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
1974
1975       dbgMsg("Loading started.");
1976
1977       AlignFrame newAlignFrame = readAlignment(_file);
1978       AlignFrame newAlignFrame2 = readAlignment(_file2);
1979       if (newAlignFrame != null)
1980       {
1981         addToDisplay(newAlignFrame, newAlignFrame2);
1982         loadTree(newAlignFrame);
1983
1984         loadScoreFile(newAlignFrame);
1985
1986         loadFeatures(newAlignFrame);
1987
1988         loadAnnotations(newAlignFrame);
1989
1990         loadJnetFile(newAlignFrame);
1991
1992         loadPdbFiles(newAlignFrame);
1993       }
1994       else
1995       {
1996         fileFound = false;
1997         applet.remove(launcher);
1998         applet.repaint();
1999       }
2000       callInitCallback();
2001     }
2002
2003     /**
2004      * Add an AlignFrame to the display; or if two are provided, a SplitFrame.
2005      * 
2006      * @param af
2007      * @param af2
2008      */
2009     public void addToDisplay(AlignFrame af, AlignFrame af2)
2010     {
2011       if (af2 == null)
2012       {
2013         af.addToDisplay(embedded);
2014       }
2015       else
2016       {
2017         SplitFrame sf = new SplitFrame(af, af2);
2018         sf.addToDisplay(embedded, JalviewLite.this);
2019       }
2020     }
2021
2022     /**
2023      * Read the alignment file (from URL, text 'paste', or archive by
2024      * classloader).
2025      * 
2026      * @return
2027      */
2028     protected AlignFrame readAlignment(String fileParam)
2029     {
2030       if (fileParam == null)
2031       {
2032         return null;
2033       }
2034       String resolvedFile = setProtocolState(fileParam);
2035       String format = new IdentifyFile().Identify(resolvedFile, protocol);
2036       dbgMsg("File identified as '" + format + "'");
2037       AlignmentI al = null;
2038       try
2039       {
2040         al = new AppletFormatAdapter().readFile(resolvedFile, protocol, format);
2041         if ((al != null) && (al.getHeight() > 0))
2042         {
2043           dbgMsg("Successfully loaded file.");
2044           al.setDataset(null);
2045           AlignFrame newAlignFrame = new AlignFrame(al, applet,
2046                   resolvedFile, embedded, false);
2047           newAlignFrame.setTitle(resolvedFile);
2048           if (initialAlignFrame == null)
2049           {
2050             initialAlignFrame = newAlignFrame;
2051           }
2052           // update the focus.
2053           currentAlignFrame = newAlignFrame;
2054
2055           if (protocol == AppletFormatAdapter.PASTE)
2056           {
2057             newAlignFrame.setTitle(MessageManager.formatMessage(
2058                     "label.sequences_from", new Object[]
2059                     { applet.getDocumentBase().toString() }));
2060           }
2061
2062           newAlignFrame.setStatus(MessageManager.formatMessage(
2063                   "label.successfully_loaded_file", new Object[]
2064                   { resolvedFile }));
2065
2066           return newAlignFrame;
2067         }
2068       } catch (java.io.IOException ex)
2069       {
2070         dbgMsg("File load exception.");
2071         ex.printStackTrace();
2072         if (debug)
2073         {
2074           try
2075           {
2076             FileParse fp = new FileParse(resolvedFile, protocol);
2077             String ln = null;
2078             dbgMsg(">>>Dumping contents of '" + resolvedFile + "' " + "("
2079                     + protocol + ")");
2080             while ((ln = fp.nextLine()) != null)
2081             {
2082               dbgMsg(ln);
2083             }
2084             dbgMsg(">>>Dump finished.");
2085           } catch (Exception e)
2086           {
2087             System.err
2088                     .println("Exception when trying to dump the content of the file parameter.");
2089             e.printStackTrace();
2090           }
2091         }
2092       }
2093       return null;
2094     }
2095
2096     /**
2097      * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
2098      * else false.
2099      * 
2100      * @param alignFrame
2101      * @return
2102      */
2103     protected boolean loadPdbFiles(AlignFrame alignFrame)
2104     {
2105       boolean result = false;
2106       /*
2107        * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
2108        * related to JAL-434
2109        */
2110
2111       applet.setAlignPdbStructures(getDefaultParameter("alignpdbfiles",
2112               false));
2113       /*
2114        * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
2115        * PDB|1GAQ|1GAQ|C">
2116        * 
2117        * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
2118        * 
2119        * <param name="PDBfile3" value="1q0o Q45135_9MICO">
2120        */
2121
2122       int pdbFileCount = 0;
2123       // Accumulate pdbs here if they are heading for the same view (if
2124       // alignPdbStructures is true)
2125       Vector pdbs = new Vector();
2126       // create a lazy matcher if we're asked to
2127       SequenceIdMatcher matcher = (applet.getDefaultParameter(
2128               "relaxedidmatch", false)) ? new SequenceIdMatcher(
2129               alignFrame.getAlignViewport().getAlignment()
2130                       .getSequencesArray()) : null;
2131
2132       String param;
2133       do
2134       {
2135         if (pdbFileCount > 0)
2136         {               
2137           param = applet.getParameter("PDBFILE" + pdbFileCount);
2138         }
2139         else
2140         {
2141           param = pdbFile;
2142         }
2143
2144         if (param != null)
2145         {
2146           PDBEntry pdb = new PDBEntry();
2147
2148           String seqstring;
2149           SequenceI[] seqs = null;
2150           String[] chains = null;
2151
2152           StringTokenizer st = new StringTokenizer(param, " ");
2153
2154           if (st.countTokens() < 2)
2155           {
2156             if (sequence != null)
2157             {
2158               seqs = new SequenceI[]
2159               { matcher == null ? (Sequence) alignFrame.getAlignViewport()
2160                       .getAlignment().findName(sequence) : matcher
2161                       .findIdMatch(sequence) };
2162             }
2163
2164           }
2165           else
2166           {
2167             param = st.nextToken();
2168             Vector tmp = new Vector();
2169             Vector tmp2 = new Vector();
2170
2171             while (st.hasMoreTokens())
2172             {
2173               seqstring = st.nextToken();
2174               StringTokenizer st2 = new StringTokenizer(seqstring, "=");
2175               if (st2.countTokens() > 1)
2176               {
2177                 // This is the chain
2178                 tmp2.addElement(st2.nextToken());
2179                 seqstring = st2.nextToken();
2180               }
2181               tmp.addElement(matcher == null ? (Sequence) alignFrame
2182                       .getAlignViewport().getAlignment()
2183                       .findName(seqstring) : matcher.findIdMatch(seqstring));
2184             }
2185
2186             seqs = new SequenceI[tmp.size()];
2187             tmp.copyInto(seqs);
2188             if (tmp2.size() == tmp.size())
2189             {
2190               chains = new String[tmp2.size()];
2191               tmp2.copyInto(chains);
2192             }
2193           }
2194           param = setProtocolState(param);
2195
2196           if (// !jmolAvailable
2197           // &&
2198           protocol == AppletFormatAdapter.CLASSLOADER && !useXtrnalSviewer)
2199           {
2200             // Re: JAL-357 : the bug isn't a problem if we are using an
2201             // external viewer!
2202             // TODO: verify this Re:
2203             // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
2204             // This exception preserves the current behaviour where, even if
2205             // the local pdb file was identified in the class loader
2206             protocol = AppletFormatAdapter.URL; // this is probably NOT
2207             // CORRECT!
2208             param = addProtocol(param); //
2209           }
2210
2211           pdb.setFile(param);
2212
2213           if (seqs != null)
2214           {
2215             for (int i = 0; i < seqs.length; i++)
2216             {
2217               if (seqs[i] != null)
2218               {
2219                 ((Sequence) seqs[i]).addPDBId(pdb);
2220                 StructureSelectionManager.getStructureSelectionManager(
2221                         applet).registerPDBEntry(pdb);
2222               }
2223               else
2224               {
2225                 if (JalviewLite.debug)
2226                 {
2227                   // this may not really be a problem but we give a warning
2228                   // anyway
2229                   System.err
2230                           .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
2231                                   + i + ")");
2232                 }
2233               }
2234             }
2235
2236             if (!alignPdbStructures)
2237             {
2238               alignFrame.newStructureView(applet, pdb, seqs, chains,
2239                       protocol);
2240             }
2241             else
2242             {
2243               pdbs.addElement(new Object[]
2244               { pdb, seqs, chains, new String(protocol) });
2245             }
2246           }
2247         }
2248
2249         pdbFileCount++;
2250       } while (param != null || pdbFileCount < 10);
2251       if (pdbs.size() > 0)
2252       {
2253         SequenceI[][] seqs = new SequenceI[pdbs.size()][];
2254         PDBEntry[] pdb = new PDBEntry[pdbs.size()];
2255         String[][] chains = new String[pdbs.size()][];
2256         String[] protocols = new String[pdbs.size()];
2257         for (int pdbsi = 0, pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
2258         {
2259           Object[] o = (Object[]) pdbs.elementAt(pdbsi);
2260           pdb[pdbsi] = (PDBEntry) o[0];
2261           seqs[pdbsi] = (SequenceI[]) o[1];
2262           chains[pdbsi] = (String[]) o[2];
2263           protocols[pdbsi] = (String) o[3];
2264         }
2265         alignFrame.alignedStructureView(applet, pdb, seqs, chains,
2266                 protocols);
2267         result = true;
2268       }
2269       return result;
2270     }
2271
2272     /**
2273      * Load in a Jnetfile if specified by parameter. Returns true if loaded,
2274      * else false.
2275      * 
2276      * @param alignFrame
2277      * @return
2278      */
2279     protected boolean loadJnetFile(AlignFrame alignFrame)
2280     {
2281       boolean result = false;
2282       String param = jnetFile; 
2283       if (param != null)
2284       {
2285         try
2286         {
2287           param = setProtocolState(param);
2288           AlignFile predictions = GenericFileAdapter.getFile("JPredFile", param, protocol);
2289           JnetAnnotationMaker.add_annotation(predictions,
2290                   alignFrame.viewport.getAlignment(), 0, false);
2291           // false == do not add sequence profile from concise output
2292           SequenceI repseq = alignFrame.viewport.getAlignment()
2293                   .getSequenceAt(0);
2294           alignFrame.viewport.getAlignment().setSeqrep(repseq);
2295           ColumnSelection cs = new ColumnSelection();
2296           cs.hideInsertionsFor(repseq);
2297           alignFrame.viewport.setColumnSelection(cs);
2298           alignFrame.alignPanel.fontChanged();
2299           alignFrame.alignPanel.setScrollValues(0, 0);
2300           result = true;
2301         } catch (Exception ex)
2302         {
2303           ex.printStackTrace();
2304         }
2305       }
2306       return result;
2307     }
2308
2309     /**
2310      * Load annotations if specified by parameter. Returns true if loaded, else
2311      * false.
2312      * 
2313      * @param alignFrame
2314      * @return
2315      */
2316     protected boolean loadAnnotations(AlignFrame alignFrame)
2317     {
2318       boolean result = false;
2319       String param = annotations;
2320       if (param != null)
2321       {
2322         param = setProtocolState(param);
2323
2324         if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
2325                 param, protocol))
2326         {
2327           alignFrame.alignPanel.fontChanged();
2328           alignFrame.alignPanel.setScrollValues(0, 0);
2329           result = true;
2330         }
2331         else
2332         {
2333           System.err
2334                   .println("Annotations were not added from annotation file '"
2335                           + param + "'");
2336         }
2337       }
2338       return result;
2339     }
2340
2341     /**
2342      * Load features file and view settings as specified by parameters. Returns
2343      * true if features were loaded, else false.
2344      * 
2345      * @param alignFrame
2346      * @return
2347      */
2348     protected boolean loadFeatures(AlignFrame alignFrame)
2349     {
2350       boolean result = false;
2351       // ///////////////////////////
2352       // modify display of features
2353       // we do this before any features have been loaded, ensuring any hidden
2354       // groups are hidden when features first displayed
2355       //
2356       // hide specific groups
2357       //
2358       String param = hideFeatureGroups;
2359       if (param != null)
2360       {
2361         alignFrame.setFeatureGroupState(separatorListToArray(param), false);
2362         // applet.setFeatureGroupStateOn(newAlignFrame, param, false);
2363       }
2364       // show specific groups
2365       
2366       param = showFeatureGroups;
2367       if (param != null)
2368       {
2369         alignFrame.setFeatureGroupState(separatorListToArray(param), true);
2370         // applet.setFeatureGroupStateOn(newAlignFrame, param, true);
2371       }
2372       
2373       // and now load features
2374       param = features; 
2375       if (param != null)
2376       {
2377         param = setProtocolState(param);
2378
2379         result = alignFrame.parseFeaturesFile(param, protocol);
2380       }
2381
2382       param = showFeatureSettings; 
2383       if (param != null && param.equalsIgnoreCase(TRUE))
2384       {
2385         alignFrame.viewport.setShowSequenceFeatures(true);
2386         new FeatureSettings(alignFrame.alignPanel);
2387       }
2388       return result;
2389     }
2390
2391     /**
2392      * Load a score file if specified by parameter. Returns true if file was
2393      * loaded, else false.
2394      * 
2395      * @param alignFrame
2396      */
2397     protected boolean loadScoreFile(AlignFrame alignFrame)
2398     {
2399       boolean result = false;
2400       if (scoreFile != null && !"".equals(scoreFile))
2401       {
2402         try
2403         {
2404           if (debug)
2405           {
2406             System.err
2407                     .println("Attempting to load T-COFFEE score file from the scoreFile parameter");
2408           }
2409           result = alignFrame.loadScoreFile(scoreFile);
2410           if (!result)
2411           {
2412             System.err
2413                     .println("Failed to parse T-COFFEE parameter as a valid score file ('"
2414                             + scoreFile + "')");
2415           }
2416         } catch (Exception e)
2417         {
2418           System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
2419                   scoreFile, e.getMessage());
2420         }
2421       }
2422       return result;
2423     }
2424
2425     /**
2426      * Load a tree for the alignment if specified by parameter. Returns true if
2427      * a tree was loaded, else false.
2428      * 
2429      * @param alignFrame
2430      * @return
2431      */
2432     protected boolean loadTree(AlignFrame alignFrame)
2433     {
2434       boolean result = false;
2435       if (treeFile == null)
2436       {
2437         treeFile = applet.getParameter("treeFile");
2438       }
2439
2440       if (treeFile != null)
2441       {
2442         try
2443         {
2444           treeFile = setProtocolState(treeFile);
2445           NewickFile fin = new NewickFile(treeFile, protocol);
2446           fin.parse();
2447
2448           if (fin.getTree() != null)
2449           {
2450             alignFrame.loadTree(fin, treeFile);
2451             result = true;
2452             dbgMsg("Successfully imported tree.");
2453           }
2454           else
2455           {
2456             dbgMsg("Tree parameter did not resolve to a valid tree.");
2457           }
2458         } catch (Exception ex)
2459         {
2460           ex.printStackTrace();
2461         }
2462       }
2463       return result;
2464     }
2465
2466     /**
2467      * Discovers whether the given file is in the Applet Archive
2468      * 
2469      * @param file
2470      *          String
2471      * @return boolean
2472      */
2473     boolean inArchive(String file)
2474     {
2475       // This might throw a security exception in certain browsers
2476       // Netscape Communicator for instance.
2477       try
2478       {
2479         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
2480         if (debug)
2481         {
2482           System.err.println("Resource '" + file + "' was "
2483                   + (rtn ? "" : "not") + " located by classloader.");
2484         }
2485         return rtn;
2486       } catch (Exception ex)
2487       {
2488         System.out.println("Exception checking resources: " + file + " "
2489                 + ex);
2490         return false;
2491       }
2492     }
2493
2494     /**
2495      * If the file is not already in URL format, tries to locate it by resolving
2496      * as a URL.
2497      * 
2498      * @param file
2499      * @return
2500      */
2501     String addProtocol(final String file)
2502     {
2503       if (file.indexOf("://") == -1)
2504       {
2505         /*
2506          * Try relative to document base
2507          */
2508         String url = applet.resolveUrlForLocalOrAbsolute(file,
2509                 getDocumentBase());
2510         if (urlExists(url))
2511         {
2512           if (debug)
2513           {
2514             System.err.println("Prepended document base for resource: '"
2515                     + file + "'");
2516           }
2517           return url;
2518         }
2519
2520         /*
2521          * Try relative to codebase
2522          */
2523         url = applet.resolveUrlForLocalOrAbsolute(file, getCodeBase());
2524         if (urlExists(url))
2525         {
2526           if (debug)
2527           {
2528             System.err.println("Prepended codebase for resource: '" + file
2529                     + "'");
2530           }
2531           return url;
2532         }
2533       }
2534
2535       /*
2536        * Not resolved, leave unchanged
2537        */
2538       return file;
2539     }
2540
2541     /**
2542      * Returns true if an input stream can be opened on the specified URL, else
2543      * false.
2544      * 
2545      * @param url
2546      * @return
2547      */
2548     private boolean urlExists(String url)
2549     {
2550       InputStream is = null;
2551       try
2552       {
2553         is = new URL(url).openStream();
2554         if (is != null)
2555         {
2556           return true;
2557         }
2558       } catch (Exception x)
2559       {
2560         // ignore
2561       } finally
2562       {
2563         if (is != null)
2564         {
2565           try
2566           {
2567             is.close();
2568           } catch (IOException e)
2569           {
2570             // ignore
2571           }
2572         }
2573       }
2574       return false;
2575     }
2576   }
2577
2578   /**
2579    * @return the default alignFrame acted on by the public applet methods. May
2580    *         return null with an error message on System.err indicating the
2581    *         fact.
2582    */
2583   public AlignFrame getDefaultTargetFrame()
2584   {
2585     if (currentAlignFrame != null)
2586     {
2587       return currentAlignFrame;
2588     }
2589     if (initialAlignFrame != null)
2590     {
2591       return initialAlignFrame;
2592     }
2593     System.err
2594             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
2595     return null;
2596   }
2597
2598   /**
2599    * separator used for separatorList
2600    */
2601   protected String separator = "" + ((char) 0x00AC); // the default used to be
2602                                                      // '|' but many sequence
2603                                                      // IDS include pipes.
2604
2605   /**
2606    * set to enable the URL based javascript execution mechanism
2607    */
2608   public boolean jsfallbackEnabled = false;
2609
2610   /**
2611    * parse the string into a list
2612    * 
2613    * @param list
2614    * @return elements separated by separator
2615    */
2616   public String[] separatorListToArray(String list)
2617   {
2618     return separatorListToArray(list, separator);
2619   }
2620
2621   /**
2622    * parse the string into a list
2623    * 
2624    * @param list
2625    * @param separator
2626    * @return elements separated by separator
2627    */
2628   public String[] separatorListToArray(String list, String separator)
2629   {
2630     // note separator local variable intentionally masks object field
2631     int seplen = separator.length();
2632     if (list == null || list.equals("") || list.equals(separator))
2633     {
2634       return null;
2635     }
2636     java.util.Vector jv = new Vector();
2637     int cp = 0, pos;
2638     while ((pos = list.indexOf(separator, cp)) > cp)
2639     {
2640       jv.addElement(list.substring(cp, pos));
2641       cp = pos + seplen;
2642     }
2643     if (cp < list.length())
2644     {
2645       String c = list.substring(cp);
2646       if (!c.equals(separator))
2647       {
2648         jv.addElement(c);
2649       }
2650     }
2651     if (jv.size() > 0)
2652     {
2653       String[] v = new String[jv.size()];
2654       for (int i = 0; i < v.length; i++)
2655       {
2656         v[i] = (String) jv.elementAt(i);
2657       }
2658       jv.removeAllElements();
2659       if (debug)
2660       {
2661         System.err.println("Array from '" + separator
2662                 + "' separated List:\n" + v.length);
2663         for (int i = 0; i < v.length; i++)
2664         {
2665           System.err.println("item " + i + " '" + v[i] + "'");
2666         }
2667       }
2668       return v;
2669     }
2670     if (debug)
2671     {
2672       System.err.println("Empty Array from '" + separator
2673               + "' separated List");
2674     }
2675     return null;
2676   }
2677
2678   /**
2679    * concatenate the list with separator
2680    * 
2681    * @param list
2682    * @return concatenated string
2683    */
2684   public String arrayToSeparatorList(String[] list)
2685   {
2686     return arrayToSeparatorList(list, separator);
2687   }
2688
2689   /**
2690    * concatenate the list with separator
2691    * 
2692    * @param list
2693    * @param separator
2694    * @return concatenated string
2695    */
2696   public String arrayToSeparatorList(String[] list, String separator)
2697   {
2698     StringBuffer v = new StringBuffer();
2699     if (list != null && list.length > 0)
2700     {
2701       for (int i = 0, iSize = list.length; i < iSize; i++)
2702       {
2703         if (list[i] != null)
2704         {
2705           if (i > 0)
2706           {
2707             v.append(separator);
2708           }
2709           v.append(list[i]);
2710         }
2711       }
2712       if (debug)
2713       {
2714         System.err.println("Returning '" + separator
2715                 + "' separated List:\n");
2716         System.err.println(v);
2717       }
2718       return v.toString();
2719     }
2720     if (debug)
2721     {
2722       System.err.println("Returning empty '" + separator
2723               + "' separated List\n");
2724     }
2725     return "" + separator;
2726   }
2727
2728   /*
2729    * (non-Javadoc)
2730    * 
2731    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroups()
2732    */
2733   public String getFeatureGroups()
2734   {
2735     String lst = arrayToSeparatorList(getDefaultTargetFrame()
2736             .getFeatureGroups());
2737     return lst;
2738   }
2739
2740   /*
2741    * (non-Javadoc)
2742    * 
2743    * @see
2744    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOn(jalview.appletgui.AlignFrame
2745    * )
2746    */
2747   public String getFeatureGroupsOn(AlignFrame alf)
2748   {
2749     String lst = arrayToSeparatorList(alf.getFeatureGroups());
2750     return lst;
2751   }
2752
2753   /*
2754    * (non-Javadoc)
2755    * 
2756    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfState(boolean)
2757    */
2758   public String getFeatureGroupsOfState(boolean visible)
2759   {
2760     return arrayToSeparatorList(getDefaultTargetFrame()
2761             .getFeatureGroupsOfState(visible));
2762   }
2763
2764   /*
2765    * (non-Javadoc)
2766    * 
2767    * @see
2768    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfStateOn(jalview.appletgui
2769    * .AlignFrame, boolean)
2770    */
2771   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
2772   {
2773     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
2774   }
2775
2776   /*
2777    * (non-Javadoc)
2778    * 
2779    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupStateOn(jalview.appletgui.
2780    * AlignFrame, java.lang.String, boolean)
2781    */
2782   public void setFeatureGroupStateOn(final AlignFrame alf,
2783           final String groups, boolean state)
2784   {
2785     final boolean st = state;// !(state==null || state.equals("") ||
2786     // state.toLowerCase().equals("false"));
2787     java.awt.EventQueue.invokeLater(new Runnable()
2788     {
2789       @Override
2790       public void run()
2791       {
2792         alf.setFeatureGroupState(separatorListToArray(groups), st);
2793       }
2794     });
2795   }
2796
2797   /*
2798    * (non-Javadoc)
2799    * 
2800    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupState(java.lang.String,
2801    * boolean)
2802    */
2803   public void setFeatureGroupState(String groups, boolean state)
2804   {
2805     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
2806   }
2807
2808   /*
2809    * (non-Javadoc)
2810    * 
2811    * @see jalview.bin.JalviewLiteJsApi#getSeparator()
2812    */
2813   public String getSeparator()
2814   {
2815     return separator;
2816   }
2817
2818   /*
2819    * (non-Javadoc)
2820    * 
2821    * @see jalview.bin.JalviewLiteJsApi#setSeparator(java.lang.String)
2822    */
2823   public void setSeparator(String separator)
2824   {
2825     if (separator == null || separator.length() < 1)
2826     {
2827       // reset to default
2828       separator = "" + ((char) 0x00AC);
2829     }
2830     this.separator = separator;
2831     if (debug)
2832     {
2833       System.err.println("Default Separator now: '" + separator + "'");
2834     }
2835   }
2836
2837   /*
2838    * (non-Javadoc)
2839    * 
2840    * @see jalview.bin.JalviewLiteJsApi#addPdbFile(jalview.appletgui.AlignFrame,
2841    * java.lang.String, java.lang.String, java.lang.String)
2842    */
2843   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
2844           String pdbEntryString, String pdbFile)
2845   {
2846     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
2847   }
2848
2849   protected void setAlignPdbStructures(boolean alignPdbStructures)
2850   {
2851     this.alignPdbStructures = alignPdbStructures;
2852   }
2853
2854   public boolean isAlignPdbStructures()
2855   {
2856     return alignPdbStructures;
2857   }
2858
2859   public void start()
2860   {
2861     // callInitCallback();
2862   }
2863
2864   private Hashtable<String, long[]> jshashes = new Hashtable<String, long[]>();
2865
2866   private Hashtable<String, Hashtable<String, String[]>> jsmessages = new Hashtable<String, Hashtable<String, String[]>>();
2867
2868   public void setJsMessageSet(String messageclass, String viewId,
2869           String[] colcommands)
2870   {
2871     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2872     if (msgset == null)
2873     {
2874       msgset = new Hashtable<String, String[]>();
2875       jsmessages.put(messageclass, msgset);
2876     }
2877     msgset.put(viewId, colcommands);
2878     long[] l = new long[colcommands.length];
2879     for (int i = 0; i < colcommands.length; i++)
2880     {
2881       l[i] = colcommands[i].hashCode();
2882     }
2883     jshashes.put(messageclass + "|" + viewId, l);
2884   }
2885
2886   /*
2887    * (non-Javadoc)
2888    * 
2889    * @see jalview.bin.JalviewLiteJsApi#getJsMessage(java.lang.String,
2890    * java.lang.String)
2891    */
2892   public String getJsMessage(String messageclass, String viewId)
2893   {
2894     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2895     if (msgset != null)
2896     {
2897       String[] msgs = msgset.get(viewId);
2898       if (msgs != null)
2899       {
2900         for (int i = 0; i < msgs.length; i++)
2901         {
2902           if (msgs[i] != null)
2903           {
2904             String m = msgs[i];
2905             msgs[i] = null;
2906             return m;
2907           }
2908         }
2909       }
2910     }
2911     return "";
2912   }
2913
2914   public boolean isJsMessageSetChanged(String string, String string2,
2915           String[] colcommands)
2916   {
2917     long[] l = jshashes.get(string + "|" + string2);
2918     if (l == null && colcommands != null)
2919     {
2920       return true;
2921     }
2922     for (int i = 0; i < colcommands.length; i++)
2923     {
2924       if (l[i] != colcommands[i].hashCode())
2925       {
2926         return true;
2927       }
2928     }
2929     return false;
2930   }
2931
2932   private Vector jsExecQueue = new Vector();
2933
2934   public Vector getJsExecQueue()
2935   {
2936     return jsExecQueue;
2937   }
2938
2939   public void setExecutor(JSFunctionExec jsFunctionExec2)
2940   {
2941     jsFunctionExec = jsFunctionExec2;
2942   }
2943
2944   /**
2945    * return the given colour value parameter or the given default if parameter
2946    * not given
2947    * 
2948    * @param colparam
2949    * @param defcolour
2950    * @return
2951    */
2952   public Color getDefaultColourParameter(String colparam, Color defcolour)
2953   {
2954     String colprop = getParameter(colparam);
2955     if (colprop == null || colprop.trim().length() == 0)
2956     {
2957       return defcolour;
2958     }
2959     Color col = ColourSchemeProperty.getAWTColorFromName(colprop);
2960     if (col == null)
2961     {
2962       try
2963       {
2964         col = new UserColourScheme(colprop).findColour('A');
2965       } catch (Exception ex)
2966       {
2967         System.err.println("Couldn't parse '" + colprop
2968                 + "' as a colour for " + colparam);
2969         col = null;
2970       }
2971     }
2972     return (col == null) ? defcolour : col;
2973
2974   }
2975
2976   public void openJalviewHelpUrl()
2977   {
2978     if (helpUrl == null || helpUrl.trim().length() < 5)
2979     {
2980       helpUrl = "http://www.jalview.org/help.html";
2981     }
2982     showURL(helpUrl, "HELP");
2983   }
2984
2985   /**
2986    * form a complete URL given a path to a resource and a reference location on
2987    * the same server
2988    * 
2989    * @param url
2990    *          - an absolute path on the same server as localref or a document
2991    *          located relative to localref
2992    * @param localref
2993    *          - a URL on the same server as url
2994    * @return a complete URL for the resource located by url
2995    */
2996   private String resolveUrlForLocalOrAbsolute(String url, URL localref)
2997   {
2998     String codebase = localref.toString();
2999     // BH removing file name and query
3000     int pt = codebase.indexOf("?");
3001     if (pt < 0)
3002         pt = codebase.length();
3003     codebase = codebase.substring(0, pt);
3004     codebase = codebase.substring(0, codebase.lastIndexOf("/") + 1);
3005     // codebase is now http://...xxx/
3006     if (url.indexOf("/") == 0 && !localref.getProtocol().equals("file"))
3007     { //  http://  https://   we do NOT allow going to the root file system directory!
3008         pt = codebase.indexOf("/", 8); 
3009       return codebase.substring(0, pt) + url;
3010     }
3011     return codebase + url;
3012   }
3013
3014   /**
3015    * open a URL in the browser - resolving it according to relative refs and
3016    * coping with javascript: protocol if necessary.
3017    * 
3018    * @param url
3019    * @param target
3020    */
3021   public void showURL(String url, String target)
3022   {
3023     try
3024     {
3025       if (url.indexOf(":") == -1)
3026       {
3027         // TODO: verify (Bas Vroling bug) prepend codebase or server URL to
3028         // form valid URL
3029         // Should really use docbase, not codebase.
3030         URL prepend;
3031         url = resolveUrlForLocalOrAbsolute(
3032                 url,
3033                 prepend = getDefaultParameter("resolvetocodebase", false) ? getDocumentBase()
3034                         : getCodeBase());
3035         if (debug)
3036         {
3037           System.err
3038                   .println("Show url (prepended "
3039                           + prepend
3040                           + " - toggle resolvetocodebase if code/docbase resolution is wrong): "
3041                           + url);
3042         }
3043       }
3044       else
3045       {
3046         if (debug)
3047         {
3048           System.err.println("Show url: " + url);
3049         }
3050       }
3051       if (url.indexOf("javascript:") == 0)
3052       {
3053         // no target for the javascript context
3054         getAppletContext().showDocument(new java.net.URL(url));
3055       }
3056       else
3057       {
3058         getAppletContext().showDocument(new java.net.URL(url), target);
3059       }
3060     } catch (Exception ex)
3061     {
3062       ex.printStackTrace();
3063     }
3064   }
3065
3066   /**
3067    * bind structures in a viewer to any matching sequences in an alignFrame (use
3068    * sequenceIds to limit scope of search to specific sequences)
3069    * 
3070    * @param alFrame
3071    * @param viewer
3072    * @param sequenceIds
3073    * @return TODO: consider making an exception structure for indicating when
3074    *         binding fails public SequenceStructureBinding
3075    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
3076    *         sequenceIds) {
3077    * 
3078    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
3079    *         alFrame.addStructureViewInstance(viewer,
3080    *         separatorListToArray(sequenceIds)); } else { return
3081    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
3082    */
3083 }