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