JAL-1859 code tidy, remove 'absolute URL' option
[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        * is it paste data?
1802        */
1803       if (path.startsWith("PASTE"))
1804       {
1805         protocol = AppletFormatAdapter.PASTE;
1806         return path.substring(5);
1807       }
1808
1809       /*
1810        * is it a URL?
1811        */
1812       if (path.indexOf("://") != -1)
1813       {
1814         protocol = AppletFormatAdapter.URL;
1815         return path;
1816       }
1817
1818       /*
1819        * try relative to document root
1820        */
1821       URL documentBase = getDocumentBase();
1822       String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
1823       if (urlExists(withDocBase))
1824       {
1825         if (debug)
1826         {
1827           System.err.println("Prepended document base '" + documentBase
1828                   + "' to make: '" + withDocBase + "'");
1829         }
1830         protocol = AppletFormatAdapter.URL;
1831         return withDocBase;
1832       }
1833
1834       /*
1835        * try relative to codebase (if different to document base)
1836        */
1837       URL codeBase = getCodeBase();
1838       String withCodeBase = applet.resolveUrlForLocalOrAbsolute(path,
1839               codeBase);
1840       if (!withCodeBase.equals(withDocBase) && urlExists(withCodeBase))
1841       {
1842         protocol = AppletFormatAdapter.URL;
1843         if (debug)
1844         {
1845           System.err.println("Prepended codebase '" + codeBase
1846                   + "' to make: '" + withCodeBase + "'");
1847         }
1848         return withCodeBase;
1849       }
1850
1851       /*
1852        * try locating by classloader; try this last so files in the directory
1853        * are resolved using document base
1854        */
1855       if (inArchive(path))
1856       {
1857         protocol = AppletFormatAdapter.CLASSLOADER;
1858       }
1859       return path;
1860     }
1861
1862     public LoadingThread(String file, String file2, JalviewLite _applet)
1863     {
1864       this._file = file;
1865       this._file2 = file2;
1866       applet = _applet;
1867     }
1868
1869     public void run()
1870     {
1871       LoadJmolThread jmolchecker = new LoadJmolThread();
1872       jmolchecker.start();
1873       while (jmolchecker.notFinished())
1874       {
1875         // wait around until the Jmol check is complete.
1876         try
1877         {
1878           Thread.sleep(2);
1879         } catch (Exception e)
1880         {
1881         }
1882       }
1883       startLoading();
1884       // applet.callInitCallback();
1885     }
1886
1887     /**
1888      * Load the alignment and any related files as specified by applet
1889      * parameters
1890      */
1891     private void startLoading()
1892     {
1893       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
1894
1895       dbgMsg("Loading started.");
1896
1897       AlignFrame newAlignFrame = readAlignment(_file);
1898       AlignFrame newAlignFrame2 = readAlignment(_file2);
1899       if (newAlignFrame != null)
1900       {
1901         addToDisplay(newAlignFrame, newAlignFrame2);
1902         loadTree(newAlignFrame);
1903
1904         loadScoreFile(newAlignFrame);
1905
1906         loadFeatures(newAlignFrame);
1907
1908         loadAnnotations(newAlignFrame);
1909
1910         loadJnetFile(newAlignFrame);
1911
1912         loadPdbFiles(newAlignFrame);
1913       }
1914       else
1915       {
1916         fileFound = false;
1917         applet.remove(launcher);
1918         applet.repaint();
1919       }
1920       callInitCallback();
1921     }
1922
1923     /**
1924      * Add an AlignFrame to the display; or if two are provided, a SplitFrame.
1925      * 
1926      * @param af
1927      * @param af2
1928      */
1929     public void addToDisplay(AlignFrame af, AlignFrame af2)
1930     {
1931       if (af2 != null)
1932       {
1933         AlignmentI al1 = af.viewport.getAlignment();
1934         AlignmentI al2 = af2.viewport.getAlignment();
1935         AlignmentI cdna = al1.isNucleotide() ? al1 : al2;
1936         AlignmentI prot = al1.isNucleotide() ? al2 : al1;
1937         if (AlignmentUtils.mapProteinAlignmentToCdna(prot, cdna))
1938         {
1939           al2.alignAs(al1);
1940           SplitFrame sf = new SplitFrame(af, af2);
1941           sf.addToDisplay(embedded, JalviewLite.this);
1942           return;
1943         }
1944         else
1945         {
1946           String msg = "Could not map any sequence in " + af2.getTitle()
1947                   + " as "
1948                   + (al1.isNucleotide() ? "protein product" : "cDNA")
1949                   + " for " + af.getTitle();
1950           System.err.println(msg);
1951         }
1952       }
1953
1954       af.addToDisplay(embedded);
1955     }
1956
1957     /**
1958      * Read the alignment file (from URL, text 'paste', or archive by
1959      * classloader).
1960      * 
1961      * @return
1962      */
1963     protected AlignFrame readAlignment(String fileParam)
1964     {
1965       if (fileParam == null)
1966       {
1967         return null;
1968       }
1969       String resolvedFile = resolveFileProtocol(fileParam);
1970       String format = new IdentifyFile().Identify(resolvedFile, protocol);
1971       dbgMsg("File identified as '" + format + "'");
1972       AlignmentI al = null;
1973       try
1974       {
1975         al = new AppletFormatAdapter().readFile(resolvedFile, protocol,
1976                 format);
1977         if ((al != null) && (al.getHeight() > 0))
1978         {
1979           dbgMsg("Successfully loaded file.");
1980           al.setDataset(null);
1981           AlignFrame newAlignFrame = new AlignFrame(al, applet,
1982                   resolvedFile, embedded, false);
1983           newAlignFrame.setTitle(resolvedFile);
1984           if (initialAlignFrame == null)
1985           {
1986             initialAlignFrame = newAlignFrame;
1987           }
1988           // update the focus.
1989           currentAlignFrame = newAlignFrame;
1990
1991           if (protocol == AppletFormatAdapter.PASTE)
1992           {
1993             newAlignFrame.setTitle(MessageManager.formatMessage(
1994                     "label.sequences_from", new Object[] { applet
1995                             .getDocumentBase().toString() }));
1996           }
1997
1998           newAlignFrame.statusBar.setText(MessageManager.formatMessage(
1999                   "label.successfully_loaded_file",
2000                   new Object[] { resolvedFile }));
2001
2002           return newAlignFrame;
2003         }
2004       } catch (java.io.IOException ex)
2005       {
2006         dbgMsg("File load exception.");
2007         ex.printStackTrace();
2008         if (debug)
2009         {
2010           try
2011           {
2012             FileParse fp = new FileParse(resolvedFile, protocol);
2013             String ln = null;
2014             dbgMsg(">>>Dumping contents of '" + resolvedFile + "' " + "("
2015                     + protocol + ")");
2016             while ((ln = fp.nextLine()) != null)
2017             {
2018               dbgMsg(ln);
2019             }
2020             dbgMsg(">>>Dump finished.");
2021           } catch (Exception e)
2022           {
2023             System.err
2024                     .println("Exception when trying to dump the content of the file parameter.");
2025             e.printStackTrace();
2026           }
2027         }
2028       }
2029       return null;
2030     }
2031
2032     /**
2033      * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
2034      * else false.
2035      * 
2036      * @param alignFrame
2037      * @return
2038      */
2039     protected boolean loadPdbFiles(AlignFrame alignFrame)
2040     {
2041       boolean result = false;
2042       /*
2043        * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
2044        * related to JAL-434
2045        */
2046
2047       applet.setAlignPdbStructures(getDefaultParameter("alignpdbfiles",
2048               false));
2049       /*
2050        * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
2051        * PDB|1GAQ|1GAQ|C">
2052        * 
2053        * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
2054        * 
2055        * <param name="PDBfile3" value="1q0o Q45135_9MICO">
2056        */
2057
2058       int pdbFileCount = 0;
2059       // Accumulate pdbs here if they are heading for the same view (if
2060       // alignPdbStructures is true)
2061       Vector pdbs = new Vector();
2062       // create a lazy matcher if we're asked to
2063       jalview.analysis.SequenceIdMatcher matcher = (applet
2064               .getDefaultParameter("relaxedidmatch", false)) ? new jalview.analysis.SequenceIdMatcher(
2065               alignFrame.getAlignViewport().getAlignment()
2066                       .getSequencesArray()) : null;
2067
2068       String param;
2069       do
2070       {
2071         if (pdbFileCount > 0)
2072         {
2073           param = applet.getParameter("PDBFILE" + pdbFileCount);
2074         }
2075         else
2076         {
2077           param = applet.getParameter("PDBFILE");
2078         }
2079
2080         if (param != null)
2081         {
2082           PDBEntry pdb = new PDBEntry();
2083
2084           String seqstring;
2085           SequenceI[] seqs = null;
2086           String[] chains = null;
2087
2088           StringTokenizer st = new StringTokenizer(param, " ");
2089
2090           if (st.countTokens() < 2)
2091           {
2092             String sequence = applet.getParameter("PDBSEQ");
2093             if (sequence != null)
2094             {
2095               seqs = new SequenceI[] { matcher == null ? (Sequence) alignFrame
2096                       .getAlignViewport().getAlignment().findName(sequence)
2097                       : matcher.findIdMatch(sequence) };
2098             }
2099
2100           }
2101           else
2102           {
2103             param = st.nextToken();
2104             List<SequenceI> tmp = new ArrayList<SequenceI>();
2105             List<String> tmp2 = new ArrayList<String>();
2106
2107             while (st.hasMoreTokens())
2108             {
2109               seqstring = st.nextToken();
2110               StringTokenizer st2 = new StringTokenizer(seqstring, "=");
2111               if (st2.countTokens() > 1)
2112               {
2113                 // This is the chain
2114                 tmp2.add(st2.nextToken());
2115                 seqstring = st2.nextToken();
2116               }
2117               tmp.add(matcher == null ? (Sequence) alignFrame
2118                       .getAlignViewport().getAlignment()
2119                       .findName(seqstring) : matcher.findIdMatch(seqstring));
2120             }
2121
2122             seqs = tmp.toArray(new SequenceI[tmp.size()]);
2123             if (tmp2.size() == tmp.size())
2124             {
2125               chains = tmp2.toArray(new String[tmp2.size()]);
2126             }
2127           }
2128           param = resolveFileProtocol(param);
2129           // TODO check JAL-357 for files in a jar (CLASSLOADER)
2130           pdb.setFile(param);
2131
2132           if (seqs != null)
2133           {
2134             for (int i = 0; i < seqs.length; i++)
2135             {
2136               if (seqs[i] != null)
2137               {
2138                 ((Sequence) seqs[i]).addPDBId(pdb);
2139                 StructureSelectionManager.getStructureSelectionManager(
2140                         applet).registerPDBEntry(pdb);
2141               }
2142               else
2143               {
2144                 if (JalviewLite.debug)
2145                 {
2146                   // this may not really be a problem but we give a warning
2147                   // anyway
2148                   System.err
2149                           .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
2150                                   + i + ")");
2151                 }
2152               }
2153             }
2154
2155             if (!alignPdbStructures)
2156             {
2157               alignFrame.newStructureView(applet, pdb, seqs, chains,
2158                       protocol);
2159             }
2160             else
2161             {
2162               pdbs.addElement(new Object[] { pdb, seqs, chains,
2163                   new String(protocol) });
2164             }
2165           }
2166         }
2167
2168         pdbFileCount++;
2169       } while (param != null || pdbFileCount < 10);
2170       if (pdbs.size() > 0)
2171       {
2172         SequenceI[][] seqs = new SequenceI[pdbs.size()][];
2173         PDBEntry[] pdb = new PDBEntry[pdbs.size()];
2174         String[][] chains = new String[pdbs.size()][];
2175         String[] protocols = new String[pdbs.size()];
2176         for (int pdbsi = 0, pdbsiSize = pdbs.size(); pdbsi < pdbsiSize; pdbsi++)
2177         {
2178           Object[] o = (Object[]) pdbs.elementAt(pdbsi);
2179           pdb[pdbsi] = (PDBEntry) o[0];
2180           seqs[pdbsi] = (SequenceI[]) o[1];
2181           chains[pdbsi] = (String[]) o[2];
2182           protocols[pdbsi] = (String) o[3];
2183         }
2184         alignFrame.alignedStructureView(applet, pdb, seqs, chains,
2185                 protocols);
2186         result = true;
2187       }
2188       return result;
2189     }
2190
2191     /**
2192      * Load in a Jnetfile if specified by parameter. Returns true if loaded,
2193      * else false.
2194      * 
2195      * @param alignFrame
2196      * @return
2197      */
2198     protected boolean loadJnetFile(AlignFrame alignFrame)
2199     {
2200       boolean result = false;
2201       String param = applet.getParameter("jnetfile");
2202       if (param != null)
2203       {
2204         try
2205         {
2206           param = resolveFileProtocol(param);
2207           JPredFile predictions = new JPredFile(param, protocol);
2208           JnetAnnotationMaker.add_annotation(predictions,
2209                   alignFrame.viewport.getAlignment(), 0, false);
2210           // false == do not add sequence profile from concise output
2211           SequenceI repseq = alignFrame.viewport.getAlignment()
2212                   .getSequenceAt(0);
2213           alignFrame.viewport.getAlignment().setSeqrep(repseq);
2214           ColumnSelection cs = new ColumnSelection();
2215           cs.hideInsertionsFor(repseq);
2216           alignFrame.viewport.setColumnSelection(cs);
2217           alignFrame.alignPanel.fontChanged();
2218           alignFrame.alignPanel.setScrollValues(0, 0);
2219           result = true;
2220         } catch (Exception ex)
2221         {
2222           ex.printStackTrace();
2223         }
2224       }
2225       return result;
2226     }
2227
2228     /**
2229      * Load annotations if specified by parameter. Returns true if loaded, else
2230      * false.
2231      * 
2232      * @param alignFrame
2233      * @return
2234      */
2235     protected boolean loadAnnotations(AlignFrame alignFrame)
2236     {
2237       boolean result = false;
2238       String param = applet.getParameter("annotations");
2239       if (param != null)
2240       {
2241         param = resolveFileProtocol(param);
2242
2243         if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
2244                 param, protocol))
2245         {
2246           alignFrame.alignPanel.fontChanged();
2247           alignFrame.alignPanel.setScrollValues(0, 0);
2248           result = true;
2249         }
2250         else
2251         {
2252           System.err
2253                   .println("Annotations were not added from annotation file '"
2254                           + param + "'");
2255         }
2256       }
2257       return result;
2258     }
2259
2260     /**
2261      * Load features file and view settings as specified by parameters. Returns
2262      * true if features were loaded, else false.
2263      * 
2264      * @param alignFrame
2265      * @return
2266      */
2267     protected boolean loadFeatures(AlignFrame alignFrame)
2268     {
2269       boolean result = false;
2270       // ///////////////////////////
2271       // modify display of features
2272       // we do this before any features have been loaded, ensuring any hidden
2273       // groups are hidden when features first displayed
2274       //
2275       // hide specific groups
2276       //
2277       String param = applet.getParameter("hidefeaturegroups");
2278       if (param != null)
2279       {
2280         alignFrame.setFeatureGroupState(separatorListToArray(param), false);
2281         // applet.setFeatureGroupStateOn(newAlignFrame, param, false);
2282       }
2283       // show specific groups
2284       param = applet.getParameter("showfeaturegroups");
2285       if (param != null)
2286       {
2287         alignFrame.setFeatureGroupState(separatorListToArray(param), true);
2288         // applet.setFeatureGroupStateOn(newAlignFrame, param, true);
2289       }
2290       // and now load features
2291       param = applet.getParameter("features");
2292       if (param != null)
2293       {
2294         param = resolveFileProtocol(param);
2295
2296         result = alignFrame.parseFeaturesFile(param, protocol);
2297       }
2298
2299       param = applet.getParameter("showFeatureSettings");
2300       if (param != null && param.equalsIgnoreCase(TRUE))
2301       {
2302         alignFrame.viewport.setShowSequenceFeatures(true);
2303         new FeatureSettings(alignFrame.alignPanel);
2304       }
2305       return result;
2306     }
2307
2308     /**
2309      * Load a score file if specified by parameter. Returns true if file was
2310      * loaded, else false.
2311      * 
2312      * @param alignFrame
2313      */
2314     protected boolean loadScoreFile(AlignFrame alignFrame)
2315     {
2316       boolean result = false;
2317       String sScoreFile = applet.getParameter("scoreFile");
2318       if (sScoreFile != null && !"".equals(sScoreFile))
2319       {
2320         try
2321         {
2322           if (debug)
2323           {
2324             System.err
2325                     .println("Attempting to load T-COFFEE score file from the scoreFile parameter");
2326           }
2327           result = alignFrame.loadScoreFile(sScoreFile);
2328           if (!result)
2329           {
2330             System.err
2331                     .println("Failed to parse T-COFFEE parameter as a valid score file ('"
2332                             + sScoreFile + "')");
2333           }
2334         } catch (Exception e)
2335         {
2336           System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
2337                   sScoreFile, e.getMessage());
2338         }
2339       }
2340       return result;
2341     }
2342
2343     /**
2344      * Load a tree for the alignment if specified by parameter. Returns true if
2345      * a tree was loaded, else false.
2346      * 
2347      * @param alignFrame
2348      * @return
2349      */
2350     protected boolean loadTree(AlignFrame alignFrame)
2351     {
2352       boolean result = false;
2353       String treeFile = applet.getParameter("tree");
2354       if (treeFile == null)
2355       {
2356         treeFile = applet.getParameter("treeFile");
2357       }
2358
2359       if (treeFile != null)
2360       {
2361         try
2362         {
2363           treeFile = resolveFileProtocol(treeFile);
2364           NewickFile fin = new NewickFile(treeFile, protocol);
2365           fin.parse();
2366
2367           if (fin.getTree() != null)
2368           {
2369             alignFrame.loadTree(fin, treeFile);
2370             result = true;
2371             dbgMsg("Successfully imported tree.");
2372           }
2373           else
2374           {
2375             dbgMsg("Tree parameter did not resolve to a valid tree.");
2376           }
2377         } catch (Exception ex)
2378         {
2379           ex.printStackTrace();
2380         }
2381       }
2382       return result;
2383     }
2384
2385     /**
2386      * Discovers whether the given file is in the Applet Archive
2387      * 
2388      * @param f
2389      *          String
2390      * @return boolean
2391      */
2392     boolean inArchive(String f)
2393     {
2394       // This might throw a security exception in certain browsers
2395       // Netscape Communicator for instance.
2396       try
2397       {
2398         boolean rtn = (getClass().getResourceAsStream("/" + f) != null);
2399         if (debug)
2400         {
2401           System.err.println("Resource '" + f + "' was "
2402                   + (rtn ? "" : "not ") + "located by classloader.");
2403         }
2404         return rtn;
2405       } catch (Exception ex)
2406       {
2407         System.out.println("Exception checking resources: " + f + " "
2408                 + ex);
2409         return false;
2410       }
2411     }
2412
2413     /**
2414      * If the file is not already in URL format, tries to locate it by resolving
2415      * as a URL.
2416      * 
2417      * @param f
2418      * @return
2419      */
2420     String addProtocol(final String f)
2421     {
2422       if (f.indexOf("://") != -1)
2423       {
2424         // already has URL format
2425         return f;
2426       }
2427
2428       /*
2429        * Try relative to document base
2430        */
2431       URL documentBase = getDocumentBase();
2432       System.err.println("Trying documentbase: " + documentBase);
2433       String url = applet.resolveUrlForLocalOrAbsolute(f, documentBase);
2434       if (urlExists(url))
2435       {
2436         if (true/* debug */)
2437         {
2438           System.err.println("Prepended document base '" + documentBase
2439                   + "' to make: '" + url + "'");
2440         }
2441         return url;
2442       }
2443
2444       /*
2445        * Try relative to codebase
2446        */
2447       URL codeBase = getCodeBase();
2448       System.err.println("Trying codebase: " + codeBase);
2449       url = applet.resolveUrlForLocalOrAbsolute(f, codeBase);
2450       if (urlExists(url))
2451       {
2452         if (true/* debug */)
2453         {
2454           System.err.println("Prepended codebase '" + codeBase
2455                   + "' to make: '" + url + "'");
2456         }
2457         return url;
2458       }
2459
2460       return f;
2461     }
2462
2463     /**
2464      * Returns true if an input stream can be opened on the specified URL, else
2465      * false.
2466      * 
2467      * @param url
2468      * @return
2469      */
2470     private boolean urlExists(String url)
2471     {
2472       InputStream is = null;
2473       try
2474       {
2475         is = new URL(url).openStream();
2476         if (is != null)
2477         {
2478           return true;
2479         }
2480       } catch (Exception x)
2481       {
2482         // ignore
2483       } finally
2484       {
2485         if (is != null)
2486         {
2487           try
2488           {
2489             is.close();
2490           } catch (IOException e)
2491           {
2492             // ignore
2493           }
2494         }
2495       }
2496       return false;
2497     }
2498   }
2499
2500   /**
2501    * @return the default alignFrame acted on by the public applet methods. May
2502    *         return null with an error message on System.err indicating the
2503    *         fact.
2504    */
2505   public AlignFrame getDefaultTargetFrame()
2506   {
2507     if (currentAlignFrame != null)
2508     {
2509       return currentAlignFrame;
2510     }
2511     if (initialAlignFrame != null)
2512     {
2513       return initialAlignFrame;
2514     }
2515     System.err
2516             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
2517     return null;
2518   }
2519
2520   /**
2521    * separator used for separatorList
2522    */
2523   protected String separator = "" + ((char) 0x00AC); // the default used to be
2524                                                      // '|' but many sequence
2525                                                      // IDS include pipes.
2526
2527   /**
2528    * set to enable the URL based javascript execution mechanism
2529    */
2530   public boolean jsfallbackEnabled = false;
2531
2532   /**
2533    * parse the string into a list
2534    * 
2535    * @param list
2536    * @return elements separated by separator
2537    */
2538   public String[] separatorListToArray(String list)
2539   {
2540     return separatorListToArray(list, separator);
2541   }
2542
2543   /**
2544    * parse the string into a list
2545    * 
2546    * @param list
2547    * @param separator
2548    * @return elements separated by separator
2549    */
2550   public static String[] separatorListToArray(String list, String separator)
2551   {
2552     // TODO use StringUtils version (slightly different...)
2553     int seplen = separator.length();
2554     if (list == null || list.equals("") || list.equals(separator))
2555     {
2556       return null;
2557     }
2558     java.util.Vector jv = new Vector();
2559     int cp = 0, pos;
2560     while ((pos = list.indexOf(separator, cp)) > cp)
2561     {
2562       jv.addElement(list.substring(cp, pos));
2563       cp = pos + seplen;
2564     }
2565     if (cp < list.length())
2566     {
2567       String c = list.substring(cp);
2568       if (!c.equals(separator))
2569       {
2570         jv.addElement(c);
2571       }
2572     }
2573     if (jv.size() > 0)
2574     {
2575       String[] v = new String[jv.size()];
2576       for (int i = 0; i < v.length; i++)
2577       {
2578         v[i] = (String) jv.elementAt(i);
2579       }
2580       jv.removeAllElements();
2581       if (debug)
2582       {
2583         System.err.println("Array from '" + separator
2584                 + "' separated List:\n" + v.length);
2585         for (int i = 0; i < v.length; i++)
2586         {
2587           System.err.println("item " + i + " '" + v[i] + "'");
2588         }
2589       }
2590       return v;
2591     }
2592     if (debug)
2593     {
2594       System.err.println("Empty Array from '" + separator
2595               + "' separated List");
2596     }
2597     return null;
2598   }
2599
2600   /**
2601    * concatenate the list with separator
2602    * 
2603    * @param list
2604    * @return concatenated string
2605    */
2606   public String arrayToSeparatorList(String[] list)
2607   {
2608     return arrayToSeparatorList(list, separator);
2609   }
2610
2611   /**
2612    * concatenate the list with separator
2613    * 
2614    * @param list
2615    * @param separator
2616    * @return concatenated string
2617    */
2618   public static String arrayToSeparatorList(String[] list, String separator)
2619   {
2620     // TODO use StringUtils version
2621     StringBuffer v = new StringBuffer();
2622     if (list != null && list.length > 0)
2623     {
2624       for (int i = 0, iSize = list.length; i < iSize; i++)
2625       {
2626         if (list[i] != null)
2627         {
2628           if (i > 0)
2629           {
2630             v.append(separator);
2631           }
2632           v.append(list[i]);
2633         }
2634       }
2635       if (debug)
2636       {
2637         System.err.println("Returning '" + separator
2638                 + "' separated List:\n");
2639         System.err.println(v);
2640       }
2641       return v.toString();
2642     }
2643     if (debug)
2644     {
2645       System.err.println("Returning empty '" + separator
2646               + "' separated List\n");
2647     }
2648     return "" + separator;
2649   }
2650
2651   /*
2652    * (non-Javadoc)
2653    * 
2654    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroups()
2655    */
2656   public String getFeatureGroups()
2657   {
2658     String lst = arrayToSeparatorList(getDefaultTargetFrame()
2659             .getFeatureGroups());
2660     return lst;
2661   }
2662
2663   /*
2664    * (non-Javadoc)
2665    * 
2666    * @see
2667    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOn(jalview.appletgui.AlignFrame
2668    * )
2669    */
2670   public String getFeatureGroupsOn(AlignFrame alf)
2671   {
2672     String lst = arrayToSeparatorList(alf.getFeatureGroups());
2673     return lst;
2674   }
2675
2676   /*
2677    * (non-Javadoc)
2678    * 
2679    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfState(boolean)
2680    */
2681   public String getFeatureGroupsOfState(boolean visible)
2682   {
2683     return arrayToSeparatorList(getDefaultTargetFrame()
2684             .getFeatureGroupsOfState(visible));
2685   }
2686
2687   /*
2688    * (non-Javadoc)
2689    * 
2690    * @see
2691    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfStateOn(jalview.appletgui
2692    * .AlignFrame, boolean)
2693    */
2694   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
2695   {
2696     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
2697   }
2698
2699   /*
2700    * (non-Javadoc)
2701    * 
2702    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupStateOn(jalview.appletgui.
2703    * AlignFrame, java.lang.String, boolean)
2704    */
2705   public void setFeatureGroupStateOn(final AlignFrame alf,
2706           final String groups, boolean state)
2707   {
2708     final boolean st = state;// !(state==null || state.equals("") ||
2709     // state.toLowerCase().equals("false"));
2710     java.awt.EventQueue.invokeLater(new Runnable()
2711     {
2712       @Override
2713       public void run()
2714       {
2715         alf.setFeatureGroupState(separatorListToArray(groups), st);
2716       }
2717     });
2718   }
2719
2720   /*
2721    * (non-Javadoc)
2722    * 
2723    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupState(java.lang.String,
2724    * boolean)
2725    */
2726   public void setFeatureGroupState(String groups, boolean state)
2727   {
2728     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
2729   }
2730
2731   /*
2732    * (non-Javadoc)
2733    * 
2734    * @see jalview.bin.JalviewLiteJsApi#getSeparator()
2735    */
2736   public String getSeparator()
2737   {
2738     return separator;
2739   }
2740
2741   /*
2742    * (non-Javadoc)
2743    * 
2744    * @see jalview.bin.JalviewLiteJsApi#setSeparator(java.lang.String)
2745    */
2746   public void setSeparator(String separator)
2747   {
2748     if (separator == null || separator.length() < 1)
2749     {
2750       // reset to default
2751       separator = "" + ((char) 0x00AC);
2752     }
2753     this.separator = separator;
2754     if (debug)
2755     {
2756       System.err.println("Default Separator now: '" + separator + "'");
2757     }
2758   }
2759
2760   /**
2761    * get boolean value of applet parameter 'name' and return default if
2762    * parameter is not set
2763    * 
2764    * @param name
2765    *          name of paremeter
2766    * @param def
2767    *          the value to return otherwise
2768    * @return true or false
2769    */
2770   public boolean getDefaultParameter(String name, boolean def)
2771   {
2772     String stn;
2773     if ((stn = getParameter(name)) == null)
2774     {
2775       return def;
2776     }
2777     if (TRUE.equalsIgnoreCase(stn))
2778     {
2779       return true;
2780     }
2781     return false;
2782   }
2783
2784   /*
2785    * (non-Javadoc)
2786    * 
2787    * @see jalview.bin.JalviewLiteJsApi#addPdbFile(jalview.appletgui.AlignFrame,
2788    * java.lang.String, java.lang.String, java.lang.String)
2789    */
2790   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
2791           String pdbEntryString, String pdbFile)
2792   {
2793     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
2794   }
2795
2796   protected void setAlignPdbStructures(boolean alignPdbStructures)
2797   {
2798     this.alignPdbStructures = alignPdbStructures;
2799   }
2800
2801   public boolean isAlignPdbStructures()
2802   {
2803     return alignPdbStructures;
2804   }
2805
2806   public void start()
2807   {
2808     // callInitCallback();
2809   }
2810
2811   private Hashtable<String, long[]> jshashes = new Hashtable<String, long[]>();
2812
2813   private Hashtable<String, Hashtable<String, String[]>> jsmessages = new Hashtable<String, Hashtable<String, String[]>>();
2814
2815   public void setJsMessageSet(String messageclass, String viewId,
2816           String[] colcommands)
2817   {
2818     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2819     if (msgset == null)
2820     {
2821       msgset = new Hashtable<String, String[]>();
2822       jsmessages.put(messageclass, msgset);
2823     }
2824     msgset.put(viewId, colcommands);
2825     long[] l = new long[colcommands.length];
2826     for (int i = 0; i < colcommands.length; i++)
2827     {
2828       l[i] = colcommands[i].hashCode();
2829     }
2830     jshashes.put(messageclass + "|" + viewId, l);
2831   }
2832
2833   /*
2834    * (non-Javadoc)
2835    * 
2836    * @see jalview.bin.JalviewLiteJsApi#getJsMessage(java.lang.String,
2837    * java.lang.String)
2838    */
2839   public String getJsMessage(String messageclass, String viewId)
2840   {
2841     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2842     if (msgset != null)
2843     {
2844       String[] msgs = msgset.get(viewId);
2845       if (msgs != null)
2846       {
2847         for (int i = 0; i < msgs.length; i++)
2848         {
2849           if (msgs[i] != null)
2850           {
2851             String m = msgs[i];
2852             msgs[i] = null;
2853             return m;
2854           }
2855         }
2856       }
2857     }
2858     return "";
2859   }
2860
2861   public boolean isJsMessageSetChanged(String string, String string2,
2862           String[] colcommands)
2863   {
2864     long[] l = jshashes.get(string + "|" + string2);
2865     if (l == null && colcommands != null)
2866     {
2867       return true;
2868     }
2869     for (int i = 0; i < colcommands.length; i++)
2870     {
2871       if (l[i] != colcommands[i].hashCode())
2872       {
2873         return true;
2874       }
2875     }
2876     return false;
2877   }
2878
2879   private Vector jsExecQueue = new Vector();
2880
2881   public Vector getJsExecQueue()
2882   {
2883     return jsExecQueue;
2884   }
2885
2886   public void setExecutor(JSFunctionExec jsFunctionExec2)
2887   {
2888     jsFunctionExec = jsFunctionExec2;
2889   }
2890
2891   /**
2892    * return the given colour value parameter or the given default if parameter
2893    * not given
2894    * 
2895    * @param colparam
2896    * @param defcolour
2897    * @return
2898    */
2899   public Color getDefaultColourParameter(String colparam, Color defcolour)
2900   {
2901     String colprop = getParameter(colparam);
2902     if (colprop == null || colprop.trim().length() == 0)
2903     {
2904       return defcolour;
2905     }
2906     Color col = jalview.schemes.ColourSchemeProperty
2907             .getAWTColorFromName(colprop);
2908     if (col == null)
2909     {
2910       try
2911       {
2912         col = new jalview.schemes.UserColourScheme(colprop).findColour('A');
2913       } catch (Exception ex)
2914       {
2915         System.err.println("Couldn't parse '" + colprop
2916                 + "' as a colour for " + colparam);
2917         col = null;
2918       }
2919     }
2920     return (col == null) ? defcolour : col;
2921
2922   }
2923
2924   public void openJalviewHelpUrl()
2925   {
2926     String helpUrl = getParameter("jalviewhelpurl");
2927     if (helpUrl == null || helpUrl.trim().length() < 5)
2928     {
2929       helpUrl = "http://www.jalview.org/help.html";
2930     }
2931     showURL(helpUrl, "HELP");
2932   }
2933
2934   /**
2935    * form a complete URL given a path to a resource and a reference location on
2936    * the same server
2937    * 
2938    * @param targetPath
2939    *          - an absolute path on the same server as localref or a document
2940    *          located relative to localref
2941    * @param localref
2942    *          - a URL on the same server as url
2943    * @return a complete URL for the resource located by url
2944    */
2945   private String resolveUrlForLocalOrAbsolute(String targetPath,
2946           URL localref)
2947   {
2948     String resolvedPath = "";
2949
2950     /*
2951      * get URL path and strip off any trailing file e.g.
2952      * www.jalview.org/examples/index.html#applets?a=b is trimmed to
2953      * www.jalview.org/examples/
2954      */
2955     String urlPath = localref.toString();
2956     String directoryPath = urlPath;
2957     int lastSeparator = directoryPath.lastIndexOf("/");
2958     if (lastSeparator > 0)
2959     {
2960       directoryPath = directoryPath.substring(0, lastSeparator + 1);
2961     }
2962
2963     if (targetPath.startsWith("/"))
2964     {
2965       /*
2966        * construct absolute URL to a file on the server - this is not allowed?
2967        */
2968       // String localfile = localref.getFile();
2969       // resolvedPath = urlPath.substring(0,
2970       // urlPath.length() - localfile.length())
2971       // + targetPath;
2972       resolvedPath = directoryPath + targetPath.substring(1);
2973     }
2974     else
2975     {
2976       resolvedPath = directoryPath + targetPath;
2977     }
2978     if (debug)
2979     {
2980       System.err.println("resolveUrlForLocalOrAbsolute returning "
2981               + resolvedPath);
2982     }
2983     return resolvedPath;
2984   }
2985
2986   /**
2987    * open a URL in the browser - resolving it according to relative refs and
2988    * coping with javascript: protocol if necessary.
2989    * 
2990    * @param url
2991    * @param target
2992    */
2993   public void showURL(String url, String target)
2994   {
2995     try
2996     {
2997       if (url.indexOf(":") == -1)
2998       {
2999         // TODO: verify (Bas Vroling bug) prepend codebase or server URL to
3000         // form valid URL
3001         // Should really use docbase, not codebase.
3002         URL prepend;
3003         url = resolveUrlForLocalOrAbsolute(
3004                 url,
3005                 prepend = getDefaultParameter("resolvetocodebase", false) ? getCodeBase()
3006                         : getDocumentBase());
3007         if (debug)
3008         {
3009           System.err
3010                   .println("Show url (prepended "
3011                           + prepend
3012                           + " - toggle resolvetocodebase if code/docbase resolution is wrong): "
3013                           + url);
3014         }
3015       }
3016       else
3017       {
3018         if (debug)
3019         {
3020           System.err.println("Show url: " + url);
3021         }
3022       }
3023       if (url.indexOf("javascript:") == 0)
3024       {
3025         // no target for the javascript context
3026         getAppletContext().showDocument(new java.net.URL(url));
3027       }
3028       else
3029       {
3030         getAppletContext().showDocument(new java.net.URL(url), target);
3031       }
3032     } catch (Exception ex)
3033     {
3034       ex.printStackTrace();
3035     }
3036   }
3037
3038   /**
3039    * bind structures in a viewer to any matching sequences in an alignFrame (use
3040    * sequenceIds to limit scope of search to specific sequences)
3041    * 
3042    * @param alFrame
3043    * @param viewer
3044    * @param sequenceIds
3045    * @return TODO: consider making an exception structure for indicating when
3046    *         binding fails public SequenceStructureBinding
3047    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
3048    *         sequenceIds) {
3049    * 
3050    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
3051    *         alFrame.addStructureViewInstance(viewer,
3052    *         separatorListToArray(sequenceIds)); } else { return
3053    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
3054    */
3055 }