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