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