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