9526ae9ea6ee4253a7e4c70cc4567822fa3a7ebc
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b1)
3  * Copyright (C) 2015 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.HttpUtils;
52 import jalview.util.MessageManager;
53
54 import java.applet.Applet;
55 import java.awt.Button;
56 import java.awt.Color;
57 import java.awt.Component;
58 import java.awt.EventQueue;
59 import java.awt.Font;
60 import java.awt.Frame;
61 import java.awt.Graphics;
62 import java.awt.event.ActionEvent;
63 import java.awt.event.WindowAdapter;
64 import java.awt.event.WindowEvent;
65 import java.io.BufferedReader;
66 import java.io.InputStreamReader;
67 import java.net.URL;
68 import java.util.ArrayList;
69 import java.util.Hashtable;
70 import java.util.List;
71 import java.util.StringTokenizer;
72 import java.util.Vector;
73
74 import netscape.javascript.JSObject;
75
76 /**
77  * Jalview Applet. Runs in Java 1.18 runtime
78  * 
79  * @author $author$
80  * @version $Revision: 1.92 $
81  */
82 public class JalviewLite extends Applet implements
83         StructureSelectionManagerProvider, JalviewLiteJsApi
84 {
85
86   private static final String TRUE = "true";
87
88   private static final String FALSE = "false";
89
90   public StructureSelectionManager getStructureSelectionManager()
91   {
92     return StructureSelectionManager.getStructureSelectionManager(this);
93   }
94
95   // /////////////////////////////////////////
96   // The following public methods may be called
97   // externally, eg via javascript in HTML page
98   /*
99    * (non-Javadoc)
100    * 
101    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
102    */
103   public String getSelectedSequences()
104   {
105     return getSelectedSequencesFrom(getDefaultTargetFrame());
106   }
107
108   /*
109    * (non-Javadoc)
110    * 
111    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
112    */
113   public String getSelectedSequences(String sep)
114   {
115     return getSelectedSequencesFrom(getDefaultTargetFrame(), sep);
116   }
117
118   /*
119    * (non-Javadoc)
120    * 
121    * @see
122    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
123    * .AlignFrame)
124    */
125   public String getSelectedSequencesFrom(AlignFrame alf)
126   {
127     return getSelectedSequencesFrom(alf, separator); // ""+0x00AC);
128   }
129
130   /*
131    * (non-Javadoc)
132    * 
133    * @see
134    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
135    * .AlignFrame, java.lang.String)
136    */
137   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
138   {
139     StringBuffer result = new StringBuffer("");
140     if (sep == null || sep.length() == 0)
141     {
142       sep = separator; // "+0x00AC;
143     }
144     if (alf.viewport.getSelectionGroup() != null)
145     {
146       SequenceI[] seqs = alf.viewport.getSelectionGroup()
147               .getSequencesInOrder(alf.viewport.getAlignment());
148
149       for (int i = 0; i < seqs.length; i++)
150       {
151         result.append(seqs[i].getName());
152         result.append(sep);
153       }
154     }
155
156     return result.toString();
157   }
158
159   /*
160    * (non-Javadoc)
161    * 
162    * @see jalview.bin.JalviewLiteJsApi#highlight(java.lang.String,
163    * java.lang.String, java.lang.String)
164    */
165   public void highlight(String sequenceId, String position,
166           String alignedPosition)
167   {
168     highlightIn(getDefaultTargetFrame(), sequenceId, position,
169             alignedPosition);
170   }
171
172   /*
173    * (non-Javadoc)
174    * 
175    * @see jalview.bin.JalviewLiteJsApi#highlightIn(jalview.appletgui.AlignFrame,
176    * java.lang.String, java.lang.String, java.lang.String)
177    */
178   public void highlightIn(final AlignFrame alf, final String sequenceId,
179           final String position, final String alignedPosition)
180   {
181     // TODO: could try to highlight in all alignments if alf==null
182     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
183             alf.viewport.getAlignment().getSequencesArray());
184     final SequenceI sq = matcher.findIdMatch(sequenceId);
185     if (sq != null)
186     {
187       int apos = -1;
188       try
189       {
190         apos = new Integer(position).intValue();
191         apos--;
192       } catch (NumberFormatException ex)
193       {
194         return;
195       }
196       final StructureSelectionManagerProvider me = this;
197       final int pos = apos;
198       // use vamsas listener to broadcast to all listeners in scope
199       if (alignedPosition != null
200               && (alignedPosition.trim().length() == 0 || alignedPosition
201                       .toLowerCase().indexOf("false") > -1))
202       {
203         java.awt.EventQueue.invokeLater(new Runnable()
204         {
205           @Override
206           public void run()
207           {
208             StructureSelectionManager.getStructureSelectionManager(me)
209                     .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
210           }
211         });
212       }
213       else
214       {
215         java.awt.EventQueue.invokeLater(new Runnable()
216         {
217           @Override
218           public void run()
219           {
220             StructureSelectionManager.getStructureSelectionManager(me)
221                     .mouseOverVamsasSequence(sq, pos, null);
222           }
223         });
224       }
225     }
226   }
227
228   /*
229    * (non-Javadoc)
230    * 
231    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
232    * java.lang.String)
233    */
234   public void select(String sequenceIds, String columns)
235   {
236     selectIn(getDefaultTargetFrame(), sequenceIds, columns, separator);
237   }
238
239   /*
240    * (non-Javadoc)
241    * 
242    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
243    * java.lang.String, java.lang.String)
244    */
245   public void select(String sequenceIds, String columns, String sep)
246   {
247     selectIn(getDefaultTargetFrame(), sequenceIds, columns, sep);
248   }
249
250   /*
251    * (non-Javadoc)
252    * 
253    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
254    * java.lang.String, java.lang.String)
255    */
256   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
257   {
258     selectIn(alf, sequenceIds, columns, separator);
259   }
260
261   /*
262    * (non-Javadoc)
263    * 
264    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
265    * java.lang.String, java.lang.String, java.lang.String)
266    */
267   public void selectIn(final AlignFrame alf, String sequenceIds,
268           String columns, String sep)
269   {
270     if (sep == null || sep.length() == 0)
271     {
272       sep = separator;
273     }
274     else
275     {
276       if (debug)
277       {
278         System.err.println("Selecting region using separator string '"
279                 + separator + "'");
280       }
281     }
282     // deparse fields
283     String[] ids = separatorListToArray(sequenceIds, sep);
284     String[] cols = separatorListToArray(columns, sep);
285     final SequenceGroup sel = new SequenceGroup();
286     final ColumnSelection csel = new ColumnSelection();
287     AlignmentI al = alf.viewport.getAlignment();
288     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
289             alf.viewport.getAlignment().getSequencesArray());
290     int start = 0, end = al.getWidth(), alw = al.getWidth();
291     boolean seqsfound = true;
292     if (ids != null && ids.length > 0)
293     {
294       seqsfound = false;
295       for (int i = 0; i < ids.length; i++)
296       {
297         if (ids[i].trim().length() == 0)
298         {
299           continue;
300         }
301         SequenceI sq = matcher.findIdMatch(ids[i]);
302         if (sq != null)
303         {
304           seqsfound = true;
305           sel.addSequence(sq, false);
306         }
307       }
308     }
309     boolean inseqpos = false;
310     if (cols != null && cols.length > 0)
311     {
312       boolean seset = false;
313       for (int i = 0; i < cols.length; i++)
314       {
315         String cl = cols[i].trim();
316         if (cl.length() == 0)
317         {
318           continue;
319         }
320         int p;
321         if ((p = cl.indexOf("-")) > -1)
322         {
323           int from = -1, to = -1;
324           try
325           {
326             from = new Integer(cl.substring(0, p)).intValue();
327             from--;
328           } catch (NumberFormatException ex)
329           {
330             System.err
331                     .println("ERROR: Couldn't parse first integer in range element column selection string '"
332                             + cl + "' - format is 'from-to'");
333             return;
334           }
335           try
336           {
337             to = new Integer(cl.substring(p + 1)).intValue();
338             to--;
339           } catch (NumberFormatException ex)
340           {
341             System.err
342                     .println("ERROR: Couldn't parse second integer in range element column selection string '"
343                             + cl + "' - format is 'from-to'");
344             return;
345           }
346           if (from >= 0 && to >= 0)
347           {
348             // valid range
349             if (from < to)
350             {
351               int t = to;
352               to = from;
353               to = t;
354             }
355             if (!seset)
356             {
357               start = from;
358               end = to;
359               seset = true;
360             }
361             else
362             {
363               // comment to prevent range extension
364               if (start > from)
365               {
366                 start = from;
367               }
368               if (end < to)
369               {
370                 end = to;
371               }
372             }
373             for (int r = from; r <= to; r++)
374             {
375               if (r >= 0 && r < alw)
376               {
377                 csel.addElement(r);
378               }
379             }
380             if (debug)
381             {
382               System.err.println("Range '" + cl + "' deparsed as [" + from
383                       + "," + to + "]");
384             }
385           }
386           else
387           {
388             System.err.println("ERROR: Invalid Range '" + cl
389                     + "' deparsed as [" + from + "," + to + "]");
390           }
391         }
392         else
393         {
394           int r = -1;
395           try
396           {
397             r = new Integer(cl).intValue();
398             r--;
399           } catch (NumberFormatException ex)
400           {
401             if (cl.toLowerCase().equals("sequence"))
402             {
403               // we are in the dataset sequence's coordinate frame.
404               inseqpos = true;
405             }
406             else
407             {
408               System.err
409                       .println("ERROR: Couldn't parse integer from point selection element of column selection string '"
410                               + cl + "'");
411               return;
412             }
413           }
414           if (r >= 0 && r <= alw)
415           {
416             if (!seset)
417             {
418               start = r;
419               end = r;
420               seset = true;
421             }
422             else
423             {
424               // comment to prevent range extension
425               if (start > r)
426               {
427                 start = r;
428               }
429               if (end < r)
430               {
431                 end = r;
432               }
433             }
434             csel.addElement(r);
435             if (debug)
436             {
437               System.err.println("Point selection '" + cl
438                       + "' deparsed as [" + r + "]");
439             }
440           }
441           else
442           {
443             System.err.println("ERROR: Invalid Point selection '" + cl
444                     + "' deparsed as [" + r + "]");
445           }
446         }
447       }
448     }
449     if (seqsfound)
450     {
451       // we only propagate the selection when it was the null selection, or the
452       // given sequences were found in the alignment.
453       if (inseqpos && sel.getSize() > 0)
454       {
455         // assume first sequence provides reference frame ?
456         SequenceI rs = sel.getSequenceAt(0);
457         start = rs.findIndex(start);
458         end = rs.findIndex(end);
459         if (csel != null)
460         {
461           List<Integer> cs = csel.getSelected();
462           csel.clear();
463           for (Integer selectedCol : cs)
464           {
465             csel.addElement(rs.findIndex(selectedCol));
466           }
467         }
468       }
469       sel.setStartRes(start);
470       sel.setEndRes(end);
471       EventQueue.invokeLater(new Runnable()
472       {
473         @Override
474         public void run()
475         {
476           alf.select(sel, csel);
477         }
478       });
479     }
480   }
481
482   /*
483    * (non-Javadoc)
484    * 
485    * @see
486    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesAsAlignment(java.lang.
487    * String, java.lang.String)
488    */
489   public String getSelectedSequencesAsAlignment(String format, String suffix)
490   {
491     return getSelectedSequencesAsAlignmentFrom(getDefaultTargetFrame(),
492             format, suffix);
493   }
494
495   /*
496    * (non-Javadoc)
497    * 
498    * @see
499    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesAsAlignmentFrom(jalview
500    * .appletgui.AlignFrame, java.lang.String, java.lang.String)
501    */
502   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
503           String format, String suffix)
504   {
505     try
506     {
507       boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
508       if (alf.viewport.getSelectionGroup() != null)
509       {
510         // JBPNote: getSelectionAsNewSequence behaviour has changed - this
511         // method now returns a full copy of sequence data
512         // TODO consider using getSequenceSelection instead here
513         String reply = new AppletFormatAdapter().formatSequences(format,
514                 new Alignment(alf.viewport.getSelectionAsNewSequence()),
515                 seqlimits);
516         return reply;
517       }
518     } catch (Exception ex)
519     {
520       ex.printStackTrace();
521       return "Error retrieving alignment in " + format + " format. ";
522     }
523     return "";
524   }
525
526   /*
527    * (non-Javadoc)
528    * 
529    * @see jalview.bin.JalviewLiteJsApi#getAlignmentOrder()
530    */
531   public String getAlignmentOrder()
532   {
533     return getAlignmentOrderFrom(getDefaultTargetFrame());
534   }
535
536   /*
537    * (non-Javadoc)
538    * 
539    * @see
540    * jalview.bin.JalviewLiteJsApi#getAlignmentOrderFrom(jalview.appletgui.AlignFrame
541    * )
542    */
543   public String getAlignmentOrderFrom(AlignFrame alf)
544   {
545     return getAlignmentOrderFrom(alf, separator);
546   }
547
548   /*
549    * (non-Javadoc)
550    * 
551    * @see
552    * jalview.bin.JalviewLiteJsApi#getAlignmentOrderFrom(jalview.appletgui.AlignFrame
553    * , java.lang.String)
554    */
555   public String getAlignmentOrderFrom(AlignFrame alf, String sep)
556   {
557     AlignmentI alorder = alf.getAlignViewport().getAlignment();
558     String[] order = new String[alorder.getHeight()];
559     for (int i = 0; i < order.length; i++)
560     {
561       order[i] = alorder.getSequenceAt(i).getName();
562     }
563     return arrayToSeparatorList(order);
564   }
565
566   /*
567    * (non-Javadoc)
568    * 
569    * @see jalview.bin.JalviewLiteJsApi#orderBy(java.lang.String,
570    * java.lang.String)
571    */
572   public String orderBy(String order, String undoName)
573   {
574     return orderBy(order, undoName, separator);
575   }
576
577   /*
578    * (non-Javadoc)
579    * 
580    * @see jalview.bin.JalviewLiteJsApi#orderBy(java.lang.String,
581    * java.lang.String, java.lang.String)
582    */
583   public String orderBy(String order, String undoName, String sep)
584   {
585     return orderAlignmentBy(getDefaultTargetFrame(), order, undoName, sep);
586   }
587
588   /*
589    * (non-Javadoc)
590    * 
591    * @see
592    * jalview.bin.JalviewLiteJsApi#orderAlignmentBy(jalview.appletgui.AlignFrame,
593    * java.lang.String, java.lang.String, java.lang.String)
594    */
595   public String orderAlignmentBy(AlignFrame alf, String order,
596           String undoName, String sep)
597   {
598     String[] ids = separatorListToArray(order, sep);
599     SequenceI[] sqs = null;
600     if (ids != null && ids.length > 0)
601     {
602       jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
603               alf.viewport.getAlignment().getSequencesArray());
604       int s = 0;
605       sqs = new SequenceI[ids.length];
606       for (int i = 0; i < ids.length; i++)
607       {
608         if (ids[i].trim().length() == 0)
609         {
610           continue;
611         }
612         SequenceI sq = matcher.findIdMatch(ids[i]);
613         if (sq != null)
614         {
615           sqs[s++] = sq;
616         }
617       }
618       if (s > 0)
619       {
620         SequenceI[] sqq = new SequenceI[s];
621         System.arraycopy(sqs, 0, sqq, 0, s);
622         sqs = sqq;
623       }
624       else
625       {
626         sqs = null;
627       }
628     }
629     if (sqs == null)
630     {
631       return "";
632     }
633     ;
634     final AlignmentOrder aorder = new AlignmentOrder(sqs);
635
636     if (undoName != null && undoName.trim().length() == 0)
637     {
638       undoName = null;
639     }
640     final String _undoName = undoName;
641     // TODO: deal with synchronization here: cannot raise any events until after
642     // this has returned.
643     return alf.sortBy(aorder, _undoName) ? TRUE : "";
644   }
645
646   /*
647    * (non-Javadoc)
648    * 
649    * @see jalview.bin.JalviewLiteJsApi#getAlignment(java.lang.String)
650    */
651   public String getAlignment(String format)
652   {
653     return getAlignmentFrom(getDefaultTargetFrame(), format, TRUE);
654   }
655
656   /*
657    * (non-Javadoc)
658    * 
659    * @see
660    * jalview.bin.JalviewLiteJsApi#getAlignmentFrom(jalview.appletgui.AlignFrame,
661    * java.lang.String)
662    */
663   public String getAlignmentFrom(AlignFrame alf, String format)
664   {
665     return getAlignmentFrom(alf, format, TRUE);
666   }
667
668   /*
669    * (non-Javadoc)
670    * 
671    * @see jalview.bin.JalviewLiteJsApi#getAlignment(java.lang.String,
672    * java.lang.String)
673    */
674   public String getAlignment(String format, String suffix)
675   {
676     return getAlignmentFrom(getDefaultTargetFrame(), format, suffix);
677   }
678
679   /*
680    * (non-Javadoc)
681    * 
682    * @see
683    * jalview.bin.JalviewLiteJsApi#getAlignmentFrom(jalview.appletgui.AlignFrame,
684    * java.lang.String, java.lang.String)
685    */
686   public String getAlignmentFrom(AlignFrame alf, String format,
687           String suffix)
688   {
689     try
690     {
691       boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
692
693       String reply = new AppletFormatAdapter().formatSequences(format,
694               alf.viewport.getAlignment(), seqlimits);
695       return reply;
696     } catch (Exception ex)
697     {
698       ex.printStackTrace();
699       return "Error retrieving alignment in " + format + " format. ";
700     }
701   }
702
703   /*
704    * (non-Javadoc)
705    * 
706    * @see jalview.bin.JalviewLiteJsApi#loadAnnotation(java.lang.String)
707    */
708   public void loadAnnotation(String annotation)
709   {
710     loadAnnotationFrom(getDefaultTargetFrame(), annotation);
711   }
712
713   /*
714    * (non-Javadoc)
715    * 
716    * @see
717    * jalview.bin.JalviewLiteJsApi#loadAnnotationFrom(jalview.appletgui.AlignFrame
718    * , java.lang.String)
719    */
720   public void loadAnnotationFrom(AlignFrame alf, String annotation)
721   {
722     if (new AnnotationFile().annotateAlignmentView(alf.getAlignViewport(),
723             annotation, AppletFormatAdapter.PASTE))
724     {
725       alf.alignPanel.fontChanged();
726       alf.alignPanel.setScrollValues(0, 0);
727     }
728     else
729     {
730       alf.parseFeaturesFile(annotation, AppletFormatAdapter.PASTE);
731     }
732   }
733
734   /*
735    * (non-Javadoc)
736    * 
737    * @see jalview.bin.JalviewLiteJsApi#loadAnnotation(java.lang.String)
738    */
739   public void loadFeatures(String features, boolean autoenabledisplay)
740   {
741     loadFeaturesFrom(getDefaultTargetFrame(), features, autoenabledisplay);
742   }
743
744   /*
745    * (non-Javadoc)
746    * 
747    * @see
748    * jalview.bin.JalviewLiteJsApi#loadAnnotationFrom(jalview.appletgui.AlignFrame
749    * , java.lang.String)
750    */
751   public boolean loadFeaturesFrom(AlignFrame alf, String features,
752           boolean autoenabledisplay)
753   {
754     return alf.parseFeaturesFile(features, AppletFormatAdapter.PASTE,
755             autoenabledisplay);
756   }
757
758   /*
759    * (non-Javadoc)
760    * 
761    * @see jalview.bin.JalviewLiteJsApi#getFeatures(java.lang.String)
762    */
763   public String getFeatures(String format)
764   {
765     return getFeaturesFrom(getDefaultTargetFrame(), format);
766   }
767
768   /*
769    * (non-Javadoc)
770    * 
771    * @see
772    * jalview.bin.JalviewLiteJsApi#getFeaturesFrom(jalview.appletgui.AlignFrame,
773    * java.lang.String)
774    */
775   public String getFeaturesFrom(AlignFrame alf, String format)
776   {
777     return alf.outputFeatures(false, format);
778   }
779
780   /*
781    * (non-Javadoc)
782    * 
783    * @see jalview.bin.JalviewLiteJsApi#getAnnotation()
784    */
785   public String getAnnotation()
786   {
787     return getAnnotationFrom(getDefaultTargetFrame());
788   }
789
790   /*
791    * (non-Javadoc)
792    * 
793    * @see
794    * jalview.bin.JalviewLiteJsApi#getAnnotationFrom(jalview.appletgui.AlignFrame
795    * )
796    */
797   public String getAnnotationFrom(AlignFrame alf)
798   {
799     return alf.outputAnnotations(false);
800   }
801
802   /*
803    * (non-Javadoc)
804    * 
805    * @see jalview.bin.JalviewLiteJsApi#newView()
806    */
807   public AlignFrame newView()
808   {
809     return newViewFrom(getDefaultTargetFrame());
810   }
811
812   /*
813    * (non-Javadoc)
814    * 
815    * @see jalview.bin.JalviewLiteJsApi#newView(java.lang.String)
816    */
817   public AlignFrame newView(String name)
818   {
819     return newViewFrom(getDefaultTargetFrame(), name);
820   }
821
822   /*
823    * (non-Javadoc)
824    * 
825    * @see jalview.bin.JalviewLiteJsApi#newViewFrom(jalview.appletgui.AlignFrame)
826    */
827   public AlignFrame newViewFrom(AlignFrame alf)
828   {
829     return alf.newView(null);
830   }
831
832   /*
833    * (non-Javadoc)
834    * 
835    * @see jalview.bin.JalviewLiteJsApi#newViewFrom(jalview.appletgui.AlignFrame,
836    * java.lang.String)
837    */
838   public AlignFrame newViewFrom(AlignFrame alf, String name)
839   {
840     return alf.newView(name);
841   }
842
843   /*
844    * (non-Javadoc)
845    * 
846    * @see jalview.bin.JalviewLiteJsApi#loadAlignment(java.lang.String,
847    * java.lang.String)
848    */
849   public AlignFrame loadAlignment(String text, String title)
850   {
851     AlignmentI al = null;
852
853     String format = new IdentifyFile().Identify(text,
854             AppletFormatAdapter.PASTE);
855     try
856     {
857       al = new AppletFormatAdapter().readFile(text,
858               AppletFormatAdapter.PASTE, format);
859       if (al.getHeight() > 0)
860       {
861         return new AlignFrame(al, this, title, false);
862       }
863     } catch (java.io.IOException ex)
864     {
865       ex.printStackTrace();
866     }
867     return null;
868   }
869
870   /*
871    * (non-Javadoc)
872    * 
873    * @see jalview.bin.JalviewLiteJsApi#setMouseoverListener(java.lang.String)
874    */
875   public void setMouseoverListener(String listener)
876   {
877     setMouseoverListener(currentAlignFrame, listener);
878   }
879
880   private Vector<jalview.javascript.JSFunctionExec> javascriptListeners = new Vector<jalview.javascript.JSFunctionExec>();
881
882   /*
883    * (non-Javadoc)
884    * 
885    * @see
886    * jalview.bin.JalviewLiteJsApi#setMouseoverListener(jalview.appletgui.AlignFrame
887    * , java.lang.String)
888    */
889   public void setMouseoverListener(AlignFrame af, String listener)
890   {
891     if (listener != null)
892     {
893       listener = listener.trim();
894       if (listener.length() == 0)
895       {
896         System.err
897                 .println("jalview Javascript error: Ignoring empty function for mouseover listener.");
898         return;
899       }
900     }
901     jalview.javascript.MouseOverListener mol = new jalview.javascript.MouseOverListener(
902             this, af, listener);
903     javascriptListeners.addElement(mol);
904     StructureSelectionManager.getStructureSelectionManager(this)
905             .addStructureViewerListener(mol);
906     if (debug)
907     {
908       System.err.println("Added a mouseover listener for "
909               + ((af == null) ? "All frames" : "Just views for "
910                       + af.getAlignViewport().getSequenceSetId()));
911       System.err.println("There are now " + javascriptListeners.size()
912               + " listeners in total.");
913     }
914   }
915
916   /*
917    * (non-Javadoc)
918    * 
919    * @see jalview.bin.JalviewLiteJsApi#setSelectionListener(java.lang.String)
920    */
921   public void setSelectionListener(String listener)
922   {
923     setSelectionListener(null, listener);
924   }
925
926   /*
927    * (non-Javadoc)
928    * 
929    * @see
930    * jalview.bin.JalviewLiteJsApi#setSelectionListener(jalview.appletgui.AlignFrame
931    * , java.lang.String)
932    */
933   public void setSelectionListener(AlignFrame af, String listener)
934   {
935     if (listener != null)
936     {
937       listener = listener.trim();
938       if (listener.length() == 0)
939       {
940         System.err
941                 .println("jalview Javascript error: Ignoring empty function for selection listener.");
942         return;
943       }
944     }
945     jalview.javascript.JsSelectionSender mol = new jalview.javascript.JsSelectionSender(
946             this, af, listener);
947     javascriptListeners.addElement(mol);
948     StructureSelectionManager.getStructureSelectionManager(this)
949             .addSelectionListener(mol);
950     if (debug)
951     {
952       System.err.println("Added a selection listener for "
953               + ((af == null) ? "All frames" : "Just views for "
954                       + af.getAlignViewport().getSequenceSetId()));
955       System.err.println("There are now " + javascriptListeners.size()
956               + " listeners in total.");
957     }
958   }
959
960   /**
961    * Callable from javascript to register a javascript function to pass events
962    * to a structure viewer.
963    *
964    * @param listener
965    *          the name of a javascript function
966    * @param modelSet
967    *          a token separated list of PDB file names listened for
968    * @see jalview.bin.JalviewLiteJsApi#setStructureListener(java.lang.String,
969    *      java.lang.String)
970    */
971   public void setStructureListener(String listener, String modelSet)
972   {
973     if (listener != null)
974     {
975       listener = listener.trim();
976       if (listener.length() == 0)
977       {
978         System.err
979                 .println("jalview Javascript error: Ignoring empty function for selection listener.");
980         return;
981       }
982     }
983     MouseOverStructureListener mol = new MouseOverStructureListener(this,
984             listener, separatorListToArray(modelSet));
985     javascriptListeners.addElement(mol);
986     StructureSelectionManager.getStructureSelectionManager(this)
987             .addStructureViewerListener(mol);
988     if (debug)
989     {
990       System.err.println("Added a javascript structure viewer listener '"
991               + listener + "'");
992       System.err.println("There are now " + javascriptListeners.size()
993               + " listeners in total.");
994     }
995   }
996
997   /*
998    * (non-Javadoc)
999    * 
1000    * @see
1001    * jalview.bin.JalviewLiteJsApi#removeJavascriptListener(jalview.appletgui
1002    * .AlignFrame, java.lang.String)
1003    */
1004   public void removeJavascriptListener(AlignFrame af, String listener)
1005   {
1006     if (listener != null)
1007     {
1008       listener = listener.trim();
1009       if (listener.length() == 0)
1010       {
1011         listener = null;
1012       }
1013     }
1014     boolean rprt = false;
1015     for (int ms = 0, msSize = javascriptListeners.size(); ms < msSize;)
1016     {
1017       Object lstn = javascriptListeners.elementAt(ms);
1018       JsCallBack lstner = (JsCallBack) lstn;
1019       if ((af == null || lstner.getAlignFrame() == af)
1020               && (listener == null || lstner.getListenerFunction().equals(
1021                       listener)))
1022       {
1023         javascriptListeners.removeElement(lstner);
1024         msSize--;
1025         if (lstner instanceof SelectionListener)
1026         {
1027           StructureSelectionManager.getStructureSelectionManager(this)
1028                   .removeSelectionListener((SelectionListener) lstner);
1029         }
1030         else
1031         {
1032           StructureSelectionManager.getStructureSelectionManager(this)
1033                   .removeStructureViewerListener(lstner, null);
1034         }
1035         rprt = debug;
1036         if (debug)
1037         {
1038           System.err.println("Removed listener '" + listener + "'");
1039         }
1040       }
1041       else
1042       {
1043         ms++;
1044       }
1045     }
1046     if (rprt)
1047     {
1048       System.err.println("There are now " + javascriptListeners.size()
1049               + " listeners in total.");
1050     }
1051   }
1052
1053   public void stop()
1054   {
1055     System.err.println("Applet " + getName() + " stop().");
1056     tidyUp();
1057   }
1058
1059   public void destroy()
1060   {
1061     System.err.println("Applet " + getName() + " destroy().");
1062     tidyUp();
1063   }
1064
1065   private void tidyUp()
1066   {
1067     removeAll();
1068     if (currentAlignFrame != null && currentAlignFrame.viewport != null
1069             && currentAlignFrame.viewport.applet != null)
1070     {
1071       AlignViewport av = currentAlignFrame.viewport;
1072       currentAlignFrame.closeMenuItem_actionPerformed();
1073       av.applet = null;
1074       currentAlignFrame = null;
1075     }
1076     if (javascriptListeners != null)
1077     {
1078       while (javascriptListeners.size() > 0)
1079       {
1080         jalview.javascript.JSFunctionExec mol = javascriptListeners
1081                 .elementAt(0);
1082         javascriptListeners.removeElement(mol);
1083         if (mol instanceof SelectionListener)
1084         {
1085           StructureSelectionManager.getStructureSelectionManager(this)
1086                   .removeSelectionListener((SelectionListener) mol);
1087         }
1088         else
1089         {
1090           StructureSelectionManager.getStructureSelectionManager(this)
1091                   .removeStructureViewerListener(mol, null);
1092         }
1093         mol.jvlite = null;
1094       }
1095     }
1096     if (jsFunctionExec != null)
1097     {
1098       jsFunctionExec.stopQueue();
1099       jsFunctionExec.jvlite = null;
1100     }
1101     initialAlignFrame = null;
1102     jsFunctionExec = null;
1103     javascriptListeners = null;
1104     StructureSelectionManager.release(this);
1105   }
1106
1107   private jalview.javascript.JSFunctionExec jsFunctionExec;
1108
1109   /*
1110    * (non-Javadoc)
1111    * 
1112    * @see jalview.bin.JalviewLiteJsApi#mouseOverStructure(java.lang.String,
1113    * java.lang.String, java.lang.String)
1114    */
1115   public void mouseOverStructure(final String pdbResNum,
1116           final String chain, final String pdbfile)
1117   {
1118     final StructureSelectionManagerProvider me = this;
1119     java.awt.EventQueue.invokeLater(new Runnable()
1120     {
1121       @Override
1122       public void run()
1123       {
1124         try
1125         {
1126           StructureSelectionManager.getStructureSelectionManager(me)
1127                   .mouseOverStructure(new Integer(pdbResNum).intValue(),
1128                           chain, pdbfile);
1129           if (debug)
1130           {
1131             System.err.println("mouseOver for '" + pdbResNum
1132                     + "' in chain '" + chain + "' in structure '" + pdbfile
1133                     + "'");
1134           }
1135         } catch (NumberFormatException e)
1136         {
1137           System.err.println("Ignoring invalid residue number string '"
1138                   + pdbResNum + "'");
1139         }
1140
1141       }
1142     });
1143   }
1144
1145   /*
1146    * (non-Javadoc)
1147    * 
1148    * @see
1149    * jalview.bin.JalviewLiteJsApi#scrollViewToIn(jalview.appletgui.AlignFrame,
1150    * java.lang.String, java.lang.String)
1151    */
1152   public void scrollViewToIn(final AlignFrame alf, final String topRow,
1153           final String leftHandColumn)
1154   {
1155     java.awt.EventQueue.invokeLater(new Runnable()
1156     {
1157       @Override
1158       public void run()
1159       {
1160         try
1161         {
1162           alf.scrollTo(new Integer(topRow).intValue(), new Integer(
1163                   leftHandColumn).intValue());
1164
1165         } catch (Exception ex)
1166         {
1167           System.err.println("Couldn't parse integer arguments (topRow='"
1168                   + topRow + "' and leftHandColumn='" + leftHandColumn
1169                   + "')");
1170           ex.printStackTrace();
1171         }
1172       }
1173     });
1174   }
1175
1176   /*
1177    * (non-Javadoc)
1178    * 
1179    * @see
1180    * jalview.javascript.JalviewLiteJsApi#scrollViewToRowIn(jalview.appletgui
1181    * .AlignFrame, java.lang.String)
1182    */
1183   @Override
1184   public void scrollViewToRowIn(final AlignFrame alf, final String topRow)
1185   {
1186
1187     java.awt.EventQueue.invokeLater(new Runnable()
1188     {
1189       @Override
1190       public void run()
1191       {
1192         try
1193         {
1194           alf.scrollToRow(new Integer(topRow).intValue());
1195
1196         } catch (Exception ex)
1197         {
1198           System.err.println("Couldn't parse integer arguments (topRow='"
1199                   + topRow + "')");
1200           ex.printStackTrace();
1201         }
1202
1203       }
1204     });
1205   }
1206
1207   /*
1208    * (non-Javadoc)
1209    * 
1210    * @see
1211    * jalview.javascript.JalviewLiteJsApi#scrollViewToColumnIn(jalview.appletgui
1212    * .AlignFrame, java.lang.String)
1213    */
1214   @Override
1215   public void scrollViewToColumnIn(final AlignFrame alf,
1216           final String leftHandColumn)
1217   {
1218     java.awt.EventQueue.invokeLater(new Runnable()
1219     {
1220
1221       @Override
1222       public void run()
1223       {
1224         try
1225         {
1226           alf.scrollToColumn(new Integer(leftHandColumn).intValue());
1227
1228         } catch (Exception ex)
1229         {
1230           System.err
1231                   .println("Couldn't parse integer arguments (leftHandColumn='"
1232                           + leftHandColumn + "')");
1233           ex.printStackTrace();
1234         }
1235       }
1236     });
1237
1238   }
1239
1240   // //////////////////////////////////////////////
1241   // //////////////////////////////////////////////
1242
1243   public static int lastFrameX = 200;
1244
1245   public static int lastFrameY = 200;
1246
1247   boolean fileFound = true;
1248
1249   String file = "No file";
1250
1251   String file2 = null;
1252
1253   Button launcher = new Button(
1254           MessageManager.getString("label.start_jalview"));
1255
1256   /**
1257    * The currentAlignFrame is static, it will change if and when the user
1258    * selects a new window. Note that it will *never* point back to the embedded
1259    * AlignFrame if the applet is started as embedded on the page and then
1260    * afterwards a new view is created.
1261    */
1262   public AlignFrame currentAlignFrame = null;
1263
1264   /**
1265    * This is the first frame to be displayed, and does not change. API calls
1266    * will default to this instance if currentAlignFrame is null.
1267    */
1268   AlignFrame initialAlignFrame = null;
1269
1270   boolean embedded = false;
1271
1272   private boolean checkForJmol = true;
1273
1274   private boolean checkedForJmol = false; // ensure we don't check for jmol
1275
1276   // every time the app is re-inited
1277
1278   public boolean jmolAvailable = false;
1279
1280   private boolean alignPdbStructures = false;
1281
1282   /**
1283    * use an external structure viewer exclusively (no jmols or MCViews will be
1284    * opened by JalviewLite itself)
1285    */
1286   public boolean useXtrnalSviewer = false;
1287
1288   public static boolean debug = false;
1289
1290   static String builddate = null, version = null, installation = null;
1291
1292   private static void initBuildDetails()
1293   {
1294     if (builddate == null)
1295     {
1296       builddate = "unknown";
1297       version = "test";
1298       installation = "applet";
1299       java.net.URL url = JalviewLite.class
1300               .getResource("/.build_properties");
1301       if (url != null)
1302       {
1303         try
1304         {
1305           BufferedReader reader = new BufferedReader(new InputStreamReader(
1306                   url.openStream()));
1307           String line;
1308           while ((line = reader.readLine()) != null)
1309           {
1310             if (line.indexOf("VERSION") > -1)
1311             {
1312               version = line.substring(line.indexOf("=") + 1);
1313             }
1314             if (line.indexOf("BUILD_DATE") > -1)
1315             {
1316               builddate = line.substring(line.indexOf("=") + 1);
1317             }
1318             if (line.indexOf("INSTALLATION") > -1)
1319             {
1320               installation = line.substring(line.indexOf("=") + 1);
1321             }
1322           }
1323         } catch (Exception ex)
1324         {
1325           ex.printStackTrace();
1326         }
1327       }
1328     }
1329   }
1330
1331   public static String getBuildDate()
1332   {
1333     initBuildDetails();
1334     return builddate;
1335   }
1336
1337   public static String getInstallation()
1338   {
1339     initBuildDetails();
1340     return installation;
1341   }
1342
1343   public static String getVersion()
1344   {
1345     initBuildDetails();
1346     return version;
1347   }
1348
1349   // public JSObject scriptObject = null;
1350
1351   /**
1352    * init method for Jalview Applet
1353    */
1354   @Override
1355   public void init()
1356   {
1357     debug = TRUE.equalsIgnoreCase(getParameter("debug"));
1358     try
1359     {
1360       if (debug)
1361       {
1362         System.err.println("Applet context is '"
1363                 + getAppletContext().getClass().toString() + "'");
1364       }
1365       JSObject scriptObject = JSObject.getWindow(this);
1366       if (debug && scriptObject != null)
1367       {
1368         System.err.println("Applet has Javascript callback support.");
1369       }
1370
1371     } catch (Exception ex)
1372     {
1373       System.err
1374               .println("Warning: No JalviewLite javascript callbacks available.");
1375       if (debug)
1376       {
1377         ex.printStackTrace();
1378       }
1379     }
1380
1381     if (debug)
1382     {
1383       System.err.println("JalviewLite Version " + getVersion());
1384       System.err.println("Build Date : " + getBuildDate());
1385       System.err.println("Installation : " + getInstallation());
1386     }
1387     String externalsviewer = getParameter("externalstructureviewer");
1388     if (externalsviewer != null)
1389     {
1390       useXtrnalSviewer = externalsviewer.trim().toLowerCase().equals(TRUE);
1391     }
1392     /**
1393      * if true disable the check for jmol
1394      */
1395     String chkforJmol = getParameter("nojmol");
1396     if (chkforJmol != null)
1397     {
1398       checkForJmol = !chkforJmol.equals(TRUE);
1399     }
1400     /**
1401      * get the separator parameter if present
1402      */
1403     String sep = getParameter("separator");
1404     if (sep != null)
1405     {
1406       if (sep.length() > 0)
1407       {
1408         separator = sep;
1409         if (debug)
1410         {
1411           System.err.println("Separator set to '" + separator + "'");
1412         }
1413       }
1414       else
1415       {
1416         throw new Error(
1417                 MessageManager
1418                         .getString("error.invalid_separator_parameter"));
1419       }
1420     }
1421     int r = 255;
1422     int g = 255;
1423     int b = 255;
1424     String param = getParameter("RGB");
1425
1426     if (param != null)
1427     {
1428       try
1429       {
1430         r = Integer.parseInt(param.substring(0, 2), 16);
1431         g = Integer.parseInt(param.substring(2, 4), 16);
1432         b = Integer.parseInt(param.substring(4, 6), 16);
1433       } catch (Exception ex)
1434       {
1435         r = 255;
1436         g = 255;
1437         b = 255;
1438       }
1439     }
1440     param = getParameter("label");
1441     if (param != null)
1442     {
1443       launcher.setLabel(param);
1444     }
1445
1446     setBackground(new Color(r, g, b));
1447
1448     file = getParameter("file");
1449
1450     if (file == null)
1451     {
1452       // Maybe the sequences are added as parameters
1453       StringBuffer data = new StringBuffer("PASTE");
1454       int i = 1;
1455       while ((file = getParameter("sequence" + i)) != null)
1456       {
1457         data.append(file.toString() + "\n");
1458         i++;
1459       }
1460       if (data.length() > 5)
1461       {
1462         file = data.toString();
1463       }
1464     }
1465     if (getDefaultParameter("enableSplitFrame", true))
1466     {
1467       file2 = getParameter("file2");
1468     }
1469
1470     embedded = TRUE.equalsIgnoreCase(getParameter("embedded"));
1471     if (embedded)
1472     {
1473       LoadingThread loader = new LoadingThread(file, file2, this);
1474       loader.start();
1475     }
1476     else if (file != null)
1477     {
1478       /*
1479        * Start the applet immediately or show a button to start it
1480        */
1481       if (FALSE.equalsIgnoreCase(getParameter("showbutton")))
1482       {
1483         LoadingThread loader = new LoadingThread(file, file2, this);
1484         loader.start();
1485       }
1486       else
1487       {
1488         add(launcher);
1489         launcher.addActionListener(new java.awt.event.ActionListener()
1490         {
1491           public void actionPerformed(ActionEvent e)
1492           {
1493             LoadingThread loader = new LoadingThread(file, file2,
1494                     JalviewLite.this);
1495             loader.start();
1496           }
1497         });
1498       }
1499     }
1500     else
1501     {
1502       // jalview initialisation with no alignment. loadAlignment() method can
1503       // still be called to open new alignments.
1504       file = "NO FILE";
1505       fileFound = false;
1506       callInitCallback();
1507     }
1508   }
1509
1510   private void initLiveConnect()
1511   {
1512     // try really hard to get the liveConnect thing working
1513     boolean notFailed = false;
1514     int tries = 0;
1515     while (!notFailed && tries < 10)
1516     {
1517       if (tries > 0)
1518       {
1519         System.err.println("LiveConnect request thread going to sleep.");
1520       }
1521       try
1522       {
1523         Thread.sleep(700 * (1 + tries));
1524       } catch (InterruptedException q)
1525       {
1526       }
1527       ;
1528       if (tries++ > 0)
1529       {
1530         System.err.println("LiveConnect request thread woken up.");
1531       }
1532       try
1533       {
1534         JSObject scriptObject = JSObject.getWindow(this);
1535         if (scriptObject.eval("navigator") != null)
1536         {
1537           notFailed = true;
1538         }
1539       } catch (Exception jsex)
1540       {
1541         System.err.println("Attempt " + tries
1542                 + " to access LiveConnect javascript failed.");
1543       }
1544     }
1545   }
1546
1547   private void callInitCallback()
1548   {
1549     String initjscallback = getParameter("oninit");
1550     if (initjscallback == null)
1551     {
1552       return;
1553     }
1554     initjscallback = initjscallback.trim();
1555     if (initjscallback.length() > 0)
1556     {
1557       JSObject scriptObject = null;
1558       try
1559       {
1560         scriptObject = JSObject.getWindow(this);
1561       } catch (Exception ex)
1562       {
1563       }
1564       ;
1565       // try really hard to let the browser plugin know we want liveconnect
1566       initLiveConnect();
1567
1568       if (scriptObject != null)
1569       {
1570         try
1571         {
1572           // do onInit with the JS executor thread
1573           new JSFunctionExec(this).executeJavascriptFunction(true,
1574                   initjscallback, null, "Calling oninit callback '"
1575                           + initjscallback + "'.");
1576         } catch (Exception e)
1577         {
1578           System.err.println("Exception when executing _oninit callback '"
1579                   + initjscallback + "'.");
1580           e.printStackTrace();
1581         }
1582       }
1583       else
1584       {
1585         System.err.println("Not executing _oninit callback '"
1586                 + initjscallback + "' - no scripting allowed.");
1587       }
1588     }
1589   }
1590
1591   /**
1592    * Initialises and displays a new java.awt.Frame
1593    * 
1594    * @param frame
1595    *          java.awt.Frame to be displayed
1596    * @param title
1597    *          title of new frame
1598    * @param width
1599    *          width if new frame
1600    * @param height
1601    *          height of new frame
1602    */
1603   public static void addFrame(final Frame frame, String title, int width,
1604           int height)
1605   {
1606     frame.setLocation(lastFrameX, lastFrameY);
1607     lastFrameX += 40;
1608     lastFrameY += 40;
1609     frame.setSize(width, height);
1610     frame.setTitle(title);
1611     frame.addWindowListener(new WindowAdapter()
1612     {
1613       public void windowClosing(WindowEvent e)
1614       {
1615         if (frame instanceof AlignFrame)
1616         {
1617           AlignViewport vp = ((AlignFrame) frame).viewport;
1618           ((AlignFrame) frame).closeMenuItem_actionPerformed();
1619           if (vp.applet.currentAlignFrame == frame)
1620           {
1621             vp.applet.currentAlignFrame = null;
1622           }
1623           vp.applet = null;
1624           vp = null;
1625
1626         }
1627         lastFrameX -= 40;
1628         lastFrameY -= 40;
1629         if (frame instanceof EmbmenuFrame)
1630         {
1631           ((EmbmenuFrame) frame).destroyMenus();
1632         }
1633         frame.setMenuBar(null);
1634         frame.dispose();
1635       }
1636
1637       public void windowActivated(WindowEvent e)
1638       {
1639         if (frame instanceof AlignFrame)
1640         {
1641           ((AlignFrame) frame).viewport.applet.currentAlignFrame = (AlignFrame) frame;
1642           if (debug)
1643           {
1644             System.err.println("Activated window " + frame);
1645           }
1646         }
1647         // be good.
1648         super.windowActivated(e);
1649       }
1650       /*
1651        * Probably not necessary to do this - see TODO above. (non-Javadoc)
1652        * 
1653        * @see
1654        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
1655        * )
1656        * 
1657        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
1658        * frame) { currentAlignFrame = null; if (debug) {
1659        * System.err.println("Deactivated window "+frame); } }
1660        * super.windowDeactivated(e); }
1661        */
1662     });
1663     frame.setVisible(true);
1664   }
1665
1666   /**
1667    * This paints the background surrounding the "Launch Jalview button" <br>
1668    * <br>
1669    * If file given in parameter not found, displays error message
1670    * 
1671    * @param g
1672    *          graphics context
1673    */
1674   public void paint(Graphics g)
1675   {
1676     if (!fileFound)
1677     {
1678       g.setColor(new Color(200, 200, 200));
1679       g.setColor(Color.cyan);
1680       g.fillRect(0, 0, getSize().width, getSize().height);
1681       g.setColor(Color.red);
1682       g.drawString(
1683               MessageManager.getString("label.jalview_cannot_open_file"),
1684               5, 15);
1685       g.drawString("\"" + file + "\"", 5, 30);
1686     }
1687     else if (embedded)
1688     {
1689       g.setColor(Color.black);
1690       g.setFont(new Font("Arial", Font.BOLD, 24));
1691       g.drawString(MessageManager.getString("label.jalview_applet"), 50,
1692               getSize().height / 2 - 30);
1693       g.drawString(MessageManager.getString("label.loading_data") + "...",
1694               50, getSize().height / 2);
1695     }
1696   }
1697
1698   /**
1699    * get all components associated with the applet of the given type
1700    * 
1701    * @param class1
1702    * @return
1703    */
1704   public Vector getAppletWindow(Class class1)
1705   {
1706     Vector wnds = new Vector();
1707     Component[] cmp = getComponents();
1708     if (cmp != null)
1709     {
1710       for (int i = 0; i < cmp.length; i++)
1711       {
1712         if (class1.isAssignableFrom(cmp[i].getClass()))
1713         {
1714           wnds.addElement(cmp);
1715         }
1716       }
1717     }
1718     return wnds;
1719   }
1720
1721   class LoadJmolThread extends Thread
1722   {
1723     private boolean running = false;
1724
1725     public void run()
1726     {
1727       if (running || checkedForJmol)
1728       {
1729         return;
1730       }
1731       running = true;
1732       if (checkForJmol)
1733       {
1734         try
1735         {
1736           if (!System.getProperty("java.version").startsWith("1.1"))
1737           {
1738             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
1739             jmolAvailable = true;
1740           }
1741           if (!jmolAvailable)
1742           {
1743             System.out
1744                     .println("Jmol not available - Using MCview for structures");
1745           }
1746         } catch (java.lang.ClassNotFoundException ex)
1747         {
1748         }
1749       }
1750       else
1751       {
1752         jmolAvailable = false;
1753         if (debug)
1754         {
1755           System.err
1756                   .println("Skipping Jmol check. Will use MCView (probably)");
1757         }
1758       }
1759       checkedForJmol = true;
1760       running = false;
1761     }
1762
1763     public boolean notFinished()
1764     {
1765       return running || !checkedForJmol;
1766     }
1767   }
1768
1769   class LoadingThread extends Thread
1770   {
1771     /**
1772      * State variable: protocol for access to file source
1773      */
1774     String protocol;
1775
1776     String _file; // alignment file or URL spec
1777
1778     String _file2; // second alignment file or URL spec
1779
1780     JalviewLite applet;
1781
1782     private void dbgMsg(String msg)
1783     {
1784       if (JalviewLite.debug)
1785       {
1786         System.err.println(msg);
1787       }
1788     }
1789
1790     /**
1791      * update the protocol state variable for accessing the datasource located
1792      * by file.
1793      * 
1794      * @param path
1795      * @return possibly updated datasource string
1796      */
1797     public String resolveFileProtocol(String path)
1798     {
1799       /*
1800        * is it paste data?
1801        */
1802       if (path.startsWith("PASTE"))
1803       {
1804         protocol = AppletFormatAdapter.PASTE;
1805         return path.substring(5);
1806       }
1807
1808       /*
1809        * is it a URL?
1810        */
1811       if (path.indexOf("://") != -1)
1812       {
1813         protocol = AppletFormatAdapter.URL;
1814         return path;
1815       }
1816
1817       /*
1818        * try relative to document root
1819        */
1820       URL documentBase = getDocumentBase();
1821       String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
1822       if (HttpUtils.isValidUrl(withDocBase))
1823       {
1824         if (debug)
1825         {
1826           System.err.println("Prepended document base '" + documentBase
1827                   + "' to make: '" + withDocBase + "'");
1828         }
1829         protocol = AppletFormatAdapter.URL;
1830         return withDocBase;
1831       }
1832
1833       /*
1834        * try relative to codebase (if different to document base)
1835        */
1836       URL codeBase = getCodeBase();
1837       String withCodeBase = applet.resolveUrlForLocalOrAbsolute(path,
1838               codeBase);
1839       if (!withCodeBase.equals(withDocBase)
1840               && HttpUtils.isValidUrl(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 + " " + ex);
2408         return false;
2409       }
2410     }
2411   }
2412
2413   /**
2414    * @return the default alignFrame acted on by the public applet methods. May
2415    *         return null with an error message on System.err indicating the
2416    *         fact.
2417    */
2418   public AlignFrame getDefaultTargetFrame()
2419   {
2420     if (currentAlignFrame != null)
2421     {
2422       return currentAlignFrame;
2423     }
2424     if (initialAlignFrame != null)
2425     {
2426       return initialAlignFrame;
2427     }
2428     System.err
2429             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
2430     return null;
2431   }
2432
2433   /**
2434    * separator used for separatorList
2435    */
2436   protected String separator = "" + ((char) 0x00AC); // the default used to be
2437                                                      // '|' but many sequence
2438                                                      // IDS include pipes.
2439
2440   /**
2441    * set to enable the URL based javascript execution mechanism
2442    */
2443   public boolean jsfallbackEnabled = false;
2444
2445   /**
2446    * parse the string into a list
2447    * 
2448    * @param list
2449    * @return elements separated by separator
2450    */
2451   public String[] separatorListToArray(String list)
2452   {
2453     return separatorListToArray(list, separator);
2454   }
2455
2456   /**
2457    * parse the string into a list
2458    * 
2459    * @param list
2460    * @param separator
2461    * @return elements separated by separator
2462    */
2463   public static String[] separatorListToArray(String list, String separator)
2464   {
2465     // TODO use StringUtils version (slightly different...)
2466     int seplen = separator.length();
2467     if (list == null || list.equals("") || list.equals(separator))
2468     {
2469       return null;
2470     }
2471     java.util.Vector jv = new Vector();
2472     int cp = 0, pos;
2473     while ((pos = list.indexOf(separator, cp)) > cp)
2474     {
2475       jv.addElement(list.substring(cp, pos));
2476       cp = pos + seplen;
2477     }
2478     if (cp < list.length())
2479     {
2480       String c = list.substring(cp);
2481       if (!c.equals(separator))
2482       {
2483         jv.addElement(c);
2484       }
2485     }
2486     if (jv.size() > 0)
2487     {
2488       String[] v = new String[jv.size()];
2489       for (int i = 0; i < v.length; i++)
2490       {
2491         v[i] = (String) jv.elementAt(i);
2492       }
2493       jv.removeAllElements();
2494       if (debug)
2495       {
2496         System.err.println("Array from '" + separator
2497                 + "' separated List:\n" + v.length);
2498         for (int i = 0; i < v.length; i++)
2499         {
2500           System.err.println("item " + i + " '" + v[i] + "'");
2501         }
2502       }
2503       return v;
2504     }
2505     if (debug)
2506     {
2507       System.err.println("Empty Array from '" + separator
2508               + "' separated List");
2509     }
2510     return null;
2511   }
2512
2513   /**
2514    * concatenate the list with separator
2515    * 
2516    * @param list
2517    * @return concatenated string
2518    */
2519   public String arrayToSeparatorList(String[] list)
2520   {
2521     return arrayToSeparatorList(list, separator);
2522   }
2523
2524   /**
2525    * concatenate the list with separator
2526    * 
2527    * @param list
2528    * @param separator
2529    * @return concatenated string
2530    */
2531   public static String arrayToSeparatorList(String[] list, String separator)
2532   {
2533     // TODO use StringUtils version
2534     StringBuffer v = new StringBuffer();
2535     if (list != null && list.length > 0)
2536     {
2537       for (int i = 0, iSize = list.length; i < iSize; i++)
2538       {
2539         if (list[i] != null)
2540         {
2541           if (i > 0)
2542           {
2543             v.append(separator);
2544           }
2545           v.append(list[i]);
2546         }
2547       }
2548       if (debug)
2549       {
2550         System.err.println("Returning '" + separator
2551                 + "' separated List:\n");
2552         System.err.println(v);
2553       }
2554       return v.toString();
2555     }
2556     if (debug)
2557     {
2558       System.err.println("Returning empty '" + separator
2559               + "' separated List\n");
2560     }
2561     return "" + separator;
2562   }
2563
2564   /*
2565    * (non-Javadoc)
2566    * 
2567    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroups()
2568    */
2569   public String getFeatureGroups()
2570   {
2571     String lst = arrayToSeparatorList(getDefaultTargetFrame()
2572             .getFeatureGroups());
2573     return lst;
2574   }
2575
2576   /*
2577    * (non-Javadoc)
2578    * 
2579    * @see
2580    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOn(jalview.appletgui.AlignFrame
2581    * )
2582    */
2583   public String getFeatureGroupsOn(AlignFrame alf)
2584   {
2585     String lst = arrayToSeparatorList(alf.getFeatureGroups());
2586     return lst;
2587   }
2588
2589   /*
2590    * (non-Javadoc)
2591    * 
2592    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfState(boolean)
2593    */
2594   public String getFeatureGroupsOfState(boolean visible)
2595   {
2596     return arrayToSeparatorList(getDefaultTargetFrame()
2597             .getFeatureGroupsOfState(visible));
2598   }
2599
2600   /*
2601    * (non-Javadoc)
2602    * 
2603    * @see
2604    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfStateOn(jalview.appletgui
2605    * .AlignFrame, boolean)
2606    */
2607   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
2608   {
2609     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
2610   }
2611
2612   /*
2613    * (non-Javadoc)
2614    * 
2615    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupStateOn(jalview.appletgui.
2616    * AlignFrame, java.lang.String, boolean)
2617    */
2618   public void setFeatureGroupStateOn(final AlignFrame alf,
2619           final String groups, boolean state)
2620   {
2621     final boolean st = state;// !(state==null || state.equals("") ||
2622     // state.toLowerCase().equals("false"));
2623     java.awt.EventQueue.invokeLater(new Runnable()
2624     {
2625       @Override
2626       public void run()
2627       {
2628         alf.setFeatureGroupState(separatorListToArray(groups), st);
2629       }
2630     });
2631   }
2632
2633   /*
2634    * (non-Javadoc)
2635    * 
2636    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupState(java.lang.String,
2637    * boolean)
2638    */
2639   public void setFeatureGroupState(String groups, boolean state)
2640   {
2641     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
2642   }
2643
2644   /*
2645    * (non-Javadoc)
2646    * 
2647    * @see jalview.bin.JalviewLiteJsApi#getSeparator()
2648    */
2649   public String getSeparator()
2650   {
2651     return separator;
2652   }
2653
2654   /*
2655    * (non-Javadoc)
2656    * 
2657    * @see jalview.bin.JalviewLiteJsApi#setSeparator(java.lang.String)
2658    */
2659   public void setSeparator(String separator)
2660   {
2661     if (separator == null || separator.length() < 1)
2662     {
2663       // reset to default
2664       separator = "" + ((char) 0x00AC);
2665     }
2666     this.separator = separator;
2667     if (debug)
2668     {
2669       System.err.println("Default Separator now: '" + separator + "'");
2670     }
2671   }
2672
2673   /**
2674    * get boolean value of applet parameter 'name' and return default if
2675    * parameter is not set
2676    * 
2677    * @param name
2678    *          name of paremeter
2679    * @param def
2680    *          the value to return otherwise
2681    * @return true or false
2682    */
2683   public boolean getDefaultParameter(String name, boolean def)
2684   {
2685     String stn;
2686     if ((stn = getParameter(name)) == null)
2687     {
2688       return def;
2689     }
2690     if (TRUE.equalsIgnoreCase(stn))
2691     {
2692       return true;
2693     }
2694     return false;
2695   }
2696
2697   /*
2698    * (non-Javadoc)
2699    * 
2700    * @see jalview.bin.JalviewLiteJsApi#addPdbFile(jalview.appletgui.AlignFrame,
2701    * java.lang.String, java.lang.String, java.lang.String)
2702    */
2703   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
2704           String pdbEntryString, String pdbFile)
2705   {
2706     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
2707   }
2708
2709   protected void setAlignPdbStructures(boolean alignPdbStructures)
2710   {
2711     this.alignPdbStructures = alignPdbStructures;
2712   }
2713
2714   public boolean isAlignPdbStructures()
2715   {
2716     return alignPdbStructures;
2717   }
2718
2719   public void start()
2720   {
2721     // callInitCallback();
2722   }
2723
2724   private Hashtable<String, long[]> jshashes = new Hashtable<String, long[]>();
2725
2726   private Hashtable<String, Hashtable<String, String[]>> jsmessages = new Hashtable<String, Hashtable<String, String[]>>();
2727
2728   public void setJsMessageSet(String messageclass, String viewId,
2729           String[] colcommands)
2730   {
2731     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2732     if (msgset == null)
2733     {
2734       msgset = new Hashtable<String, String[]>();
2735       jsmessages.put(messageclass, msgset);
2736     }
2737     msgset.put(viewId, colcommands);
2738     long[] l = new long[colcommands.length];
2739     for (int i = 0; i < colcommands.length; i++)
2740     {
2741       l[i] = colcommands[i].hashCode();
2742     }
2743     jshashes.put(messageclass + "|" + viewId, l);
2744   }
2745
2746   /*
2747    * (non-Javadoc)
2748    * 
2749    * @see jalview.bin.JalviewLiteJsApi#getJsMessage(java.lang.String,
2750    * java.lang.String)
2751    */
2752   public String getJsMessage(String messageclass, String viewId)
2753   {
2754     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2755     if (msgset != null)
2756     {
2757       String[] msgs = msgset.get(viewId);
2758       if (msgs != null)
2759       {
2760         for (int i = 0; i < msgs.length; i++)
2761         {
2762           if (msgs[i] != null)
2763           {
2764             String m = msgs[i];
2765             msgs[i] = null;
2766             return m;
2767           }
2768         }
2769       }
2770     }
2771     return "";
2772   }
2773
2774   public boolean isJsMessageSetChanged(String string, String string2,
2775           String[] colcommands)
2776   {
2777     long[] l = jshashes.get(string + "|" + string2);
2778     if (l == null && colcommands != null)
2779     {
2780       return true;
2781     }
2782     for (int i = 0; i < colcommands.length; i++)
2783     {
2784       if (l[i] != colcommands[i].hashCode())
2785       {
2786         return true;
2787       }
2788     }
2789     return false;
2790   }
2791
2792   private Vector jsExecQueue = new Vector();
2793
2794   public Vector getJsExecQueue()
2795   {
2796     return jsExecQueue;
2797   }
2798
2799   public void setExecutor(JSFunctionExec jsFunctionExec2)
2800   {
2801     jsFunctionExec = jsFunctionExec2;
2802   }
2803
2804   /**
2805    * return the given colour value parameter or the given default if parameter
2806    * not given
2807    * 
2808    * @param colparam
2809    * @param defcolour
2810    * @return
2811    */
2812   public Color getDefaultColourParameter(String colparam, Color defcolour)
2813   {
2814     String colprop = getParameter(colparam);
2815     if (colprop == null || colprop.trim().length() == 0)
2816     {
2817       return defcolour;
2818     }
2819     Color col = jalview.schemes.ColourSchemeProperty
2820             .getAWTColorFromName(colprop);
2821     if (col == null)
2822     {
2823       try
2824       {
2825         col = new jalview.schemes.UserColourScheme(colprop).findColour('A');
2826       } catch (Exception ex)
2827       {
2828         System.err.println("Couldn't parse '" + colprop
2829                 + "' as a colour for " + colparam);
2830         col = null;
2831       }
2832     }
2833     return (col == null) ? defcolour : col;
2834
2835   }
2836
2837   public void openJalviewHelpUrl()
2838   {
2839     String helpUrl = getParameter("jalviewhelpurl");
2840     if (helpUrl == null || helpUrl.trim().length() < 5)
2841     {
2842       helpUrl = "http://www.jalview.org/help.html";
2843     }
2844     showURL(helpUrl, "HELP");
2845   }
2846
2847   /**
2848    * form a complete URL given a path to a resource and a reference location on
2849    * the same server
2850    * 
2851    * @param targetPath
2852    *          - an absolute path on the same server as localref or a document
2853    *          located relative to localref
2854    * @param localref
2855    *          - a URL on the same server as url
2856    * @return a complete URL for the resource located by url
2857    */
2858   private String resolveUrlForLocalOrAbsolute(String targetPath,
2859           URL localref)
2860   {
2861     String resolvedPath = "";
2862     if (targetPath.startsWith("/"))
2863     {
2864       String codebase = localref.toString();
2865       String localfile = localref.getFile();
2866       resolvedPath = codebase.substring(0,
2867               codebase.length() - localfile.length())
2868               + targetPath;
2869       return resolvedPath;
2870     }
2871
2872     /*
2873      * get URL path and strip off any trailing file e.g.
2874      * www.jalview.org/examples/index.html#applets?a=b is trimmed to
2875      * www.jalview.org/examples/
2876      */
2877     String urlPath = localref.toString();
2878     String directoryPath = urlPath;
2879     int lastSeparator = directoryPath.lastIndexOf("/");
2880     if (lastSeparator > 0)
2881     {
2882       directoryPath = directoryPath.substring(0, lastSeparator + 1);
2883     }
2884
2885     if (targetPath.startsWith("/"))
2886     {
2887       /*
2888        * construct absolute URL to a file on the server - this is not allowed?
2889        */
2890       // String localfile = localref.getFile();
2891       // resolvedPath = urlPath.substring(0,
2892       // urlPath.length() - localfile.length())
2893       // + targetPath;
2894       resolvedPath = directoryPath + targetPath.substring(1);
2895     }
2896     else
2897     {
2898       resolvedPath = directoryPath + targetPath;
2899     }
2900     if (debug)
2901     {
2902       System.err.println("resolveUrlForLocalOrAbsolute returning "
2903               + resolvedPath);
2904     }
2905     return resolvedPath;
2906   }
2907
2908   /**
2909    * open a URL in the browser - resolving it according to relative refs and
2910    * coping with javascript: protocol if necessary.
2911    * 
2912    * @param url
2913    * @param target
2914    */
2915   public void showURL(String url, String target)
2916   {
2917     try
2918     {
2919       if (url.indexOf(":") == -1)
2920       {
2921         // TODO: verify (Bas Vroling bug) prepend codebase or server URL to
2922         // form valid URL
2923         // Should really use docbase, not codebase.
2924         URL prepend;
2925         url = resolveUrlForLocalOrAbsolute(
2926                 url,
2927                 prepend = getDefaultParameter("resolvetocodebase", false) ? getCodeBase()
2928                         : getDocumentBase());
2929         if (debug)
2930         {
2931           System.err
2932                   .println("Show url (prepended "
2933                           + prepend
2934                           + " - toggle resolvetocodebase if code/docbase resolution is wrong): "
2935                           + url);
2936         }
2937       }
2938       else
2939       {
2940         if (debug)
2941         {
2942           System.err.println("Show url: " + url);
2943         }
2944       }
2945       if (url.indexOf("javascript:") == 0)
2946       {
2947         // no target for the javascript context
2948         getAppletContext().showDocument(new java.net.URL(url));
2949       }
2950       else
2951       {
2952         getAppletContext().showDocument(new java.net.URL(url), target);
2953       }
2954     } catch (Exception ex)
2955     {
2956       ex.printStackTrace();
2957     }
2958   }
2959
2960   /**
2961    * bind structures in a viewer to any matching sequences in an alignFrame (use
2962    * sequenceIds to limit scope of search to specific sequences)
2963    * 
2964    * @param alFrame
2965    * @param viewer
2966    * @param sequenceIds
2967    * @return TODO: consider making an exception structure for indicating when
2968    *         binding fails public SequenceStructureBinding
2969    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
2970    *         sequenceIds) {
2971    * 
2972    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
2973    *         alFrame.addStructureViewInstance(viewer,
2974    *         separatorListToArray(sequenceIds)); } else { return
2975    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
2976    */
2977 }