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