871ca54bbfb978175979e54f06eb22adb078617d
[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 java.util.Locale;
24
25 import jalview.analysis.AlignmentUtils;
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.Sequence;
38 import jalview.datamodel.SequenceGroup;
39 import jalview.datamodel.SequenceI;
40 import jalview.io.AnnotationFile;
41 import jalview.io.AppletFormatAdapter;
42 import jalview.io.DataSourceType;
43 import jalview.io.FileFormatI;
44 import jalview.io.FileFormats;
45 import jalview.io.FileParse;
46 import jalview.io.IdentifyFile;
47 import jalview.io.JPredFile;
48 import jalview.io.JnetAnnotationMaker;
49 import jalview.io.NewickFile;
50 import jalview.javascript.JSFunctionExec;
51 import jalview.javascript.JalviewLiteJsApi;
52 import jalview.javascript.JsCallBack;
53 import jalview.javascript.MouseOverStructureListener;
54 import jalview.structure.SelectionListener;
55 import jalview.structure.StructureSelectionManager;
56 import jalview.util.ColorUtils;
57 import jalview.util.HttpUtils;
58 import jalview.util.MessageManager;
59
60 import java.applet.Applet;
61 import java.awt.Button;
62 import java.awt.Color;
63 import java.awt.Component;
64 import java.awt.EventQueue;
65 import java.awt.Font;
66 import java.awt.Frame;
67 import java.awt.Graphics;
68 import java.awt.event.ActionEvent;
69 import java.awt.event.WindowAdapter;
70 import java.awt.event.WindowEvent;
71 import java.io.BufferedReader;
72 import java.io.IOException;
73 import java.io.InputStreamReader;
74 import java.net.URL;
75 import java.util.ArrayList;
76 import java.util.Hashtable;
77 import java.util.List;
78 import java.util.StringTokenizer;
79 import java.util.Vector;
80
81 import netscape.javascript.JSObject;
82
83 /**
84  * Jalview Applet. Runs in Java 1.18 runtime
85  * 
86  * @author $author$
87  * @version $Revision: 1.92 $
88  */
89 public class JalviewLite extends Applet
90         implements StructureSelectionManagerProvider, JalviewLiteJsApi
91 {
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 = Integer.valueOf(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(Locale.ROOT).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 = separatorListToArray(sequenceIds, sep);
300     String[] cols = 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 = Integer.valueOf(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 = Integer.valueOf(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 = Integer.valueOf(cl).intValue();
414             r--;
415           } catch (NumberFormatException ex)
416           {
417             if (cl.toLowerCase(Locale.ROOT).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 = 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(Integer.valueOf(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(Integer.valueOf(topRow).intValue(),
1218                   Integer.valueOf(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(Integer.valueOf(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(Integer.valueOf(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 static boolean debug = false;
1344
1345   static String builddate = null, version = null, installation = null;
1346
1347   private static void initBuildDetails()
1348   {
1349     if (builddate == null)
1350     {
1351       builddate = "unknown";
1352       version = "test";
1353       installation = "applet";
1354       java.net.URL url = JalviewLite.class
1355               .getResource("/.build_properties");
1356       if (url != null)
1357       {
1358         try
1359         {
1360           BufferedReader reader = new BufferedReader(
1361                   new InputStreamReader(url.openStream()));
1362           String line;
1363           while ((line = reader.readLine()) != null)
1364           {
1365             if (line.indexOf("VERSION") > -1)
1366             {
1367               version = line.substring(line.indexOf("=") + 1);
1368             }
1369             if (line.indexOf("BUILD_DATE") > -1)
1370             {
1371               builddate = line.substring(line.indexOf("=") + 1);
1372             }
1373             if (line.indexOf("INSTALLATION") > -1)
1374             {
1375               installation = line.substring(line.indexOf("=") + 1);
1376             }
1377           }
1378         } catch (Exception ex)
1379         {
1380           ex.printStackTrace();
1381         }
1382       }
1383     }
1384   }
1385
1386   public static String getBuildDate()
1387   {
1388     initBuildDetails();
1389     return builddate;
1390   }
1391
1392   public static String getInstallation()
1393   {
1394     initBuildDetails();
1395     return installation;
1396   }
1397
1398   public static String getVersion()
1399   {
1400     initBuildDetails();
1401     return version;
1402   }
1403
1404   // public JSObject scriptObject = null;
1405
1406   /**
1407    * init method for Jalview Applet
1408    */
1409   @Override
1410   public void init()
1411   {
1412     debug = TRUE.equalsIgnoreCase(getParameter("debug"));
1413     try
1414     {
1415       if (debug)
1416       {
1417         System.err.println("Applet context is '"
1418                 + getAppletContext().getClass().toString() + "'");
1419       }
1420       JSObject scriptObject = JSObject.getWindow(this);
1421       if (debug && scriptObject != null)
1422       {
1423         System.err.println("Applet has Javascript callback support.");
1424       }
1425
1426     } catch (Exception ex)
1427     {
1428       System.err.println(
1429               "Warning: No JalviewLite javascript callbacks available.");
1430       if (debug)
1431       {
1432         ex.printStackTrace();
1433       }
1434     }
1435
1436     if (debug)
1437     {
1438       System.err.println("JalviewLite Version " + getVersion());
1439       System.err.println("Build Date : " + getBuildDate());
1440       System.err.println("Installation : " + getInstallation());
1441     }
1442     String externalsviewer = getParameter("externalstructureviewer");
1443     if (externalsviewer != null)
1444     {
1445       useXtrnalSviewer = externalsviewer.trim().toLowerCase(Locale.ROOT).equals(TRUE);
1446     }
1447     /**
1448      * if true disable the check for jmol
1449      */
1450     String chkforJmol = getParameter("nojmol");
1451     if (chkforJmol != null)
1452     {
1453       checkForJmol = !chkforJmol.equals(TRUE);
1454     }
1455     /**
1456      * get the separator parameter if present
1457      */
1458     String sep = getParameter("separator");
1459     if (sep != null)
1460     {
1461       if (sep.length() > 0)
1462       {
1463         separator = sep;
1464         if (debug)
1465         {
1466           System.err.println("Separator set to '" + separator + "'");
1467         }
1468       }
1469       else
1470       {
1471         throw new Error(MessageManager
1472                 .getString("error.invalid_separator_parameter"));
1473       }
1474     }
1475     int r = 255;
1476     int g = 255;
1477     int b = 255;
1478     String param = getParameter("RGB");
1479
1480     if (param != null)
1481     {
1482       try
1483       {
1484         r = Integer.parseInt(param.substring(0, 2), 16);
1485         g = Integer.parseInt(param.substring(2, 4), 16);
1486         b = Integer.parseInt(param.substring(4, 6), 16);
1487       } catch (Exception ex)
1488       {
1489         r = 255;
1490         g = 255;
1491         b = 255;
1492       }
1493     }
1494     param = getParameter("label");
1495     if (param != null)
1496     {
1497       launcher.setLabel(param);
1498     }
1499
1500     setBackground(new Color(r, g, b));
1501
1502     file = getParameter("file");
1503
1504     if (file == null)
1505     {
1506       // Maybe the sequences are added as parameters
1507       StringBuffer data = new StringBuffer("PASTE");
1508       int i = 1;
1509       while ((file = getParameter("sequence" + i)) != null)
1510       {
1511         data.append(file.toString() + "\n");
1512         i++;
1513       }
1514       if (data.length() > 5)
1515       {
1516         file = data.toString();
1517       }
1518     }
1519     if (getDefaultParameter("enableSplitFrame", true))
1520     {
1521       file2 = getParameter("file2");
1522     }
1523
1524     embedded = TRUE.equalsIgnoreCase(getParameter("embedded"));
1525     if (embedded)
1526     {
1527       LoadingThread loader = new LoadingThread(file, file2, this);
1528       loader.start();
1529     }
1530     else if (file != null)
1531     {
1532       /*
1533        * Start the applet immediately or show a button to start it
1534        */
1535       if (FALSE.equalsIgnoreCase(getParameter("showbutton")))
1536       {
1537         LoadingThread loader = new LoadingThread(file, file2, this);
1538         loader.start();
1539       }
1540       else
1541       {
1542         add(launcher);
1543         launcher.addActionListener(new java.awt.event.ActionListener()
1544         {
1545           @Override
1546           public void actionPerformed(ActionEvent e)
1547           {
1548             LoadingThread loader = new LoadingThread(file, file2,
1549                     JalviewLite.this);
1550             loader.start();
1551           }
1552         });
1553       }
1554     }
1555     else
1556     {
1557       // jalview initialisation with no alignment. loadAlignment() method can
1558       // still be called to open new alignments.
1559       file = "NO FILE";
1560       fileFound = false;
1561       callInitCallback();
1562     }
1563   }
1564
1565   private void initLiveConnect()
1566   {
1567     // try really hard to get the liveConnect thing working
1568     boolean notFailed = false;
1569     int tries = 0;
1570     while (!notFailed && tries < 10)
1571     {
1572       if (tries > 0)
1573       {
1574         System.err.println("LiveConnect request thread going to sleep.");
1575       }
1576       try
1577       {
1578         Thread.sleep(700 * (1 + tries));
1579       } catch (InterruptedException q)
1580       {
1581       }
1582       ;
1583       if (tries++ > 0)
1584       {
1585         System.err.println("LiveConnect request thread woken up.");
1586       }
1587       try
1588       {
1589         JSObject scriptObject = JSObject.getWindow(this);
1590         if (scriptObject.eval("navigator") != null)
1591         {
1592           notFailed = true;
1593         }
1594       } catch (Exception jsex)
1595       {
1596         System.err.println("Attempt " + tries
1597                 + " to access LiveConnect javascript failed.");
1598       }
1599     }
1600   }
1601
1602   private void callInitCallback()
1603   {
1604     String initjscallback = getParameter("oninit");
1605     if (initjscallback == null)
1606     {
1607       return;
1608     }
1609     initjscallback = initjscallback.trim();
1610     if (initjscallback.length() > 0)
1611     {
1612       JSObject scriptObject = null;
1613       try
1614       {
1615         scriptObject = JSObject.getWindow(this);
1616       } catch (Exception ex)
1617       {
1618       }
1619       ;
1620       // try really hard to let the browser plugin know we want liveconnect
1621       initLiveConnect();
1622
1623       if (scriptObject != null)
1624       {
1625         try
1626         {
1627           // do onInit with the JS executor thread
1628           new JSFunctionExec(this).executeJavascriptFunction(true,
1629                   initjscallback, null,
1630                   "Calling oninit callback '" + initjscallback + "'.");
1631         } catch (Exception e)
1632         {
1633           System.err.println("Exception when executing _oninit callback '"
1634                   + initjscallback + "'.");
1635           e.printStackTrace();
1636         }
1637       }
1638       else
1639       {
1640         System.err.println("Not executing _oninit callback '"
1641                 + initjscallback + "' - no scripting allowed.");
1642       }
1643     }
1644   }
1645
1646   /**
1647    * Initialises and displays a new java.awt.Frame
1648    * 
1649    * @param frame
1650    *          java.awt.Frame to be displayed
1651    * @param title
1652    *          title of new frame
1653    * @param width
1654    *          width if new frame
1655    * @param height
1656    *          height of new frame
1657    */
1658   public static void addFrame(final Frame frame, String title, int width,
1659           int height)
1660   {
1661     frame.setLocation(lastFrameX, lastFrameY);
1662     lastFrameX += 40;
1663     lastFrameY += 40;
1664     frame.setSize(width, height);
1665     frame.setTitle(title);
1666     frame.addWindowListener(new WindowAdapter()
1667     {
1668       @Override
1669       public void windowClosing(WindowEvent e)
1670       {
1671         if (frame instanceof AlignFrame)
1672         {
1673           AlignViewport vp = ((AlignFrame) frame).viewport;
1674           ((AlignFrame) frame).closeMenuItem_actionPerformed();
1675           if (vp.applet.currentAlignFrame == frame)
1676           {
1677             vp.applet.currentAlignFrame = null;
1678           }
1679           vp.applet = null;
1680           vp = null;
1681
1682         }
1683         lastFrameX -= 40;
1684         lastFrameY -= 40;
1685         if (frame instanceof EmbmenuFrame)
1686         {
1687           ((EmbmenuFrame) frame).destroyMenus();
1688         }
1689         frame.setMenuBar(null);
1690         frame.dispose();
1691       }
1692
1693       @Override
1694       public void windowActivated(WindowEvent e)
1695       {
1696         if (frame instanceof AlignFrame)
1697         {
1698           ((AlignFrame) frame).viewport.applet.currentAlignFrame = (AlignFrame) frame;
1699           if (debug)
1700           {
1701             System.err.println("Activated window " + frame);
1702           }
1703         }
1704         // be good.
1705         super.windowActivated(e);
1706       }
1707       /*
1708        * Probably not necessary to do this - see TODO above. (non-Javadoc)
1709        * 
1710        * @see
1711        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
1712        * )
1713        * 
1714        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
1715        * frame) { currentAlignFrame = null; if (debug) {
1716        * System.err.println("Deactivated window "+frame); } }
1717        * super.windowDeactivated(e); }
1718        */
1719     });
1720     frame.setVisible(true);
1721   }
1722
1723   /**
1724    * This paints the background surrounding the "Launch Jalview button" <br>
1725    * <br>
1726    * If file given in parameter not found, displays error message
1727    * 
1728    * @param g
1729    *          graphics context
1730    */
1731   @Override
1732   public void paint(Graphics g)
1733   {
1734     if (!fileFound)
1735     {
1736       g.setColor(new Color(200, 200, 200));
1737       g.setColor(Color.cyan);
1738       g.fillRect(0, 0, getSize().width, getSize().height);
1739       g.setColor(Color.red);
1740       g.drawString(
1741               MessageManager.getString("label.jalview_cannot_open_file"), 5,
1742               15);
1743       g.drawString("\"" + file + "\"", 5, 30);
1744     }
1745     else if (embedded)
1746     {
1747       g.setColor(Color.black);
1748       g.setFont(new Font("Arial", Font.BOLD, 24));
1749       g.drawString(MessageManager.getString("label.jalview_applet"), 50,
1750               getSize().height / 2 - 30);
1751       g.drawString(MessageManager.getString("label.loading_data") + "...",
1752               50, getSize().height / 2);
1753     }
1754   }
1755
1756   /**
1757    * get all components associated with the applet of the given type
1758    * 
1759    * @param class1
1760    * @return
1761    */
1762   public Vector getAppletWindow(Class class1)
1763   {
1764     Vector wnds = new Vector();
1765     Component[] cmp = getComponents();
1766     if (cmp != null)
1767     {
1768       for (int i = 0; i < cmp.length; i++)
1769       {
1770         if (class1.isAssignableFrom(cmp[i].getClass()))
1771         {
1772           wnds.addElement(cmp);
1773         }
1774       }
1775     }
1776     return wnds;
1777   }
1778
1779   class LoadJmolThread extends Thread
1780   {
1781     private boolean running = false;
1782
1783     @Override
1784     public void run()
1785     {
1786       if (running || checkedForJmol)
1787       {
1788         return;
1789       }
1790       running = true;
1791       if (checkForJmol)
1792       {
1793         try
1794         {
1795           if (!System.getProperty("java.version").startsWith("1.1"))
1796           {
1797             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
1798             jmolAvailable = true;
1799           }
1800           if (!jmolAvailable)
1801           {
1802             System.out.println(
1803                     "Jmol not available - Using mc_view for structures");
1804           }
1805         } catch (java.lang.ClassNotFoundException ex)
1806         {
1807         }
1808       }
1809       else
1810       {
1811         jmolAvailable = false;
1812         if (debug)
1813         {
1814           System.err.println(
1815                   "Skipping Jmol check. Will use mc_view (probably)");
1816         }
1817       }
1818       checkedForJmol = true;
1819       running = false;
1820     }
1821
1822     public boolean notFinished()
1823     {
1824       return running || !checkedForJmol;
1825     }
1826   }
1827
1828   class LoadingThread extends Thread
1829   {
1830     /**
1831      * State variable: protocol for access to file source
1832      */
1833     DataSourceType protocol;
1834
1835     String _file; // alignment file or URL spec
1836
1837     String _file2; // second alignment file or URL spec
1838
1839     JalviewLite applet;
1840
1841     private void dbgMsg(String msg)
1842     {
1843       if (JalviewLite.debug)
1844       {
1845         System.err.println(msg);
1846       }
1847     }
1848
1849     /**
1850      * update the protocol state variable for accessing the datasource located
1851      * by file.
1852      * 
1853      * @param path
1854      * @return possibly updated datasource string
1855      */
1856     public String resolveFileProtocol(String path)
1857     {
1858       /*
1859        * is it paste data?
1860        */
1861       if (path.startsWith("PASTE"))
1862       {
1863         protocol = DataSourceType.PASTE;
1864         return path.substring(5);
1865       }
1866
1867       /*
1868        * is it a URL?
1869        */
1870       if (path.indexOf("://") != -1)
1871       {
1872         protocol = DataSourceType.URL;
1873         return path;
1874       }
1875
1876       /*
1877        * try relative to document root
1878        */
1879       URL documentBase = getDocumentBase();
1880       String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
1881       if (HttpUtils.isValidUrl(withDocBase))
1882       {
1883         if (debug)
1884         {
1885           System.err.println("Prepended document base '" + documentBase
1886                   + "' to make: '" + withDocBase + "'");
1887         }
1888         protocol = DataSourceType.URL;
1889         return withDocBase;
1890       }
1891
1892       /*
1893        * try relative to codebase (if different to document base)
1894        */
1895       URL codeBase = getCodeBase();
1896       String withCodeBase = applet.resolveUrlForLocalOrAbsolute(path,
1897               codeBase);
1898       if (!withCodeBase.equals(withDocBase)
1899               && HttpUtils.isValidUrl(withCodeBase))
1900       {
1901         protocol = DataSourceType.URL;
1902         if (debug)
1903         {
1904           System.err.println("Prepended codebase '" + codeBase
1905                   + "' to make: '" + withCodeBase + "'");
1906         }
1907         return withCodeBase;
1908       }
1909
1910       /*
1911        * try locating by classloader; try this last so files in the directory
1912        * are resolved using document base
1913        */
1914       if (inArchive(path))
1915       {
1916         protocol = DataSourceType.CLASSLOADER;
1917       }
1918       return path;
1919     }
1920
1921     public LoadingThread(String file, String file2, JalviewLite _applet)
1922     {
1923       this._file = file;
1924       this._file2 = file2;
1925       applet = _applet;
1926     }
1927
1928     @Override
1929     public void run()
1930     {
1931       LoadJmolThread jmolchecker = new LoadJmolThread();
1932       jmolchecker.start();
1933       while (jmolchecker.notFinished())
1934       {
1935         // wait around until the Jmol check is complete.
1936         try
1937         {
1938           Thread.sleep(2);
1939         } catch (Exception e)
1940         {
1941         }
1942       }
1943       startLoading();
1944       // applet.callInitCallback();
1945     }
1946
1947     /**
1948      * Load the alignment and any related files as specified by applet
1949      * parameters
1950      */
1951     private void startLoading()
1952     {
1953       dbgMsg("Loading thread started with:\n>>file\n" + _file
1954               + ">>endfile");
1955
1956       dbgMsg("Loading started.");
1957
1958       AlignFrame newAlignFrame = readAlignment(_file);
1959       AlignFrame newAlignFrame2 = readAlignment(_file2);
1960       if (newAlignFrame != null)
1961       {
1962         addToDisplay(newAlignFrame, newAlignFrame2);
1963         loadTree(newAlignFrame);
1964
1965         loadScoreFile(newAlignFrame);
1966
1967         loadFeatures(newAlignFrame);
1968
1969         loadAnnotations(newAlignFrame);
1970
1971         loadJnetFile(newAlignFrame);
1972
1973         loadPdbFiles(newAlignFrame);
1974       }
1975       else
1976       {
1977         fileFound = false;
1978         applet.remove(launcher);
1979         applet.repaint();
1980       }
1981       callInitCallback();
1982     }
1983
1984     /**
1985      * Add an AlignFrame to the display; or if two are provided, a SplitFrame.
1986      * 
1987      * @param af
1988      * @param af2
1989      */
1990     public void addToDisplay(AlignFrame af, AlignFrame af2)
1991     {
1992       if (af2 != null)
1993       {
1994         AlignmentI al1 = af.viewport.getAlignment();
1995         AlignmentI al2 = af2.viewport.getAlignment();
1996         AlignmentI cdna = al1.isNucleotide() ? al1 : al2;
1997         AlignmentI prot = al1.isNucleotide() ? al2 : al1;
1998         if (AlignmentUtils.mapProteinAlignmentToCdna(prot, cdna))
1999         {
2000           al2.alignAs(al1);
2001           SplitFrame sf = new SplitFrame(af, af2);
2002           sf.addToDisplay(embedded, JalviewLite.this);
2003           return;
2004         }
2005         else
2006         {
2007           String msg = "Could not map any sequence in " + af2.getTitle()
2008                   + " as "
2009                   + (al1.isNucleotide() ? "protein product" : "cDNA")
2010                   + " for " + af.getTitle();
2011           System.err.println(msg);
2012         }
2013       }
2014
2015       af.addToDisplay(embedded);
2016     }
2017
2018     /**
2019      * Read the alignment file (from URL, text 'paste', or archive by
2020      * classloader).
2021      * 
2022      * @return
2023      */
2024     protected AlignFrame readAlignment(String fileParam)
2025     {
2026       if (fileParam == null)
2027       {
2028         return null;
2029       }
2030       String resolvedFile = resolveFileProtocol(fileParam);
2031       AlignmentI al = null;
2032       try
2033       {
2034         FileFormatI format = new IdentifyFile().identify(resolvedFile,
2035                 protocol);
2036         dbgMsg("File identified as '" + format + "'");
2037         al = new AppletFormatAdapter().readFile(resolvedFile, protocol,
2038                 format);
2039         if ((al != null) && (al.getHeight() > 0))
2040         {
2041           dbgMsg("Successfully loaded file.");
2042           al.setDataset(null);
2043           AlignFrame newAlignFrame = new AlignFrame(al, applet,
2044                   resolvedFile, embedded, false);
2045           newAlignFrame.setTitle(resolvedFile);
2046           if (initialAlignFrame == null)
2047           {
2048             initialAlignFrame = newAlignFrame;
2049           }
2050           // update the focus.
2051           currentAlignFrame = newAlignFrame;
2052
2053           if (protocol == DataSourceType.PASTE)
2054           {
2055             newAlignFrame.setTitle(MessageManager
2056                     .formatMessage("label.sequences_from", new Object[]
2057                     { applet.getDocumentBase().toString() }));
2058           }
2059
2060           newAlignFrame.statusBar.setText(MessageManager.formatMessage(
2061                   "label.successfully_loaded_file", new Object[]
2062                   { resolvedFile }));
2063
2064           return newAlignFrame;
2065         }
2066       } catch (java.io.IOException ex)
2067       {
2068         dbgMsg("File load exception.");
2069         ex.printStackTrace();
2070         if (debug)
2071         {
2072           try
2073           {
2074             FileParse fp = new FileParse(resolvedFile, protocol);
2075             String ln = null;
2076             dbgMsg(">>>Dumping contents of '" + resolvedFile + "' " + "("
2077                     + protocol + ")");
2078             while ((ln = fp.nextLine()) != null)
2079             {
2080               dbgMsg(ln);
2081             }
2082             dbgMsg(">>>Dump finished.");
2083           } catch (Exception e)
2084           {
2085             System.err.println(
2086                     "Exception when trying to dump the content of the file parameter.");
2087             e.printStackTrace();
2088           }
2089         }
2090       }
2091       return null;
2092     }
2093
2094     /**
2095      * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
2096      * else false.
2097      * 
2098      * @param alignFrame
2099      * @return
2100      */
2101     protected boolean loadPdbFiles(AlignFrame alignFrame)
2102     {
2103       boolean result = false;
2104       /*
2105        * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
2106        * related to JAL-434
2107        */
2108
2109       applet.setAlignPdbStructures(
2110               getDefaultParameter("alignpdbfiles", false));
2111       /*
2112        * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
2113        * PDB|1GAQ|1GAQ|C">
2114        * 
2115        * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
2116        * 
2117        * <param name="PDBfile3" value="1q0o Q45135_9MICO">
2118        */
2119
2120       int pdbFileCount = 0;
2121       // Accumulate pdbs here if they are heading for the same view (if
2122       // alignPdbStructures is true)
2123       Vector pdbs = new Vector();
2124       // create a lazy matcher if we're asked to
2125       jalview.analysis.SequenceIdMatcher matcher = (applet
2126               .getDefaultParameter("relaxedidmatch", false))
2127                       ? new jalview.analysis.SequenceIdMatcher(
2128                               alignFrame.getAlignViewport().getAlignment()
2129                                       .getSequencesArray())
2130                       : null;
2131
2132       String param;
2133       do
2134       {
2135         if (pdbFileCount > 0)
2136         {
2137           param = applet.getParameter("PDBFILE" + pdbFileCount);
2138         }
2139         else
2140         {
2141           param = applet.getParameter("PDBFILE");
2142         }
2143
2144         if (param != null)
2145         {
2146           PDBEntry pdb = new PDBEntry();
2147
2148           String seqstring;
2149           SequenceI[] seqs = null;
2150           String[] chains = null;
2151
2152           StringTokenizer st = new StringTokenizer(param, " ");
2153
2154           if (st.countTokens() < 2)
2155           {
2156             String sequence = applet.getParameter("PDBSEQ");
2157             if (sequence != null)
2158             {
2159               seqs = new SequenceI[] { matcher == null
2160                       ? (Sequence) alignFrame.getAlignViewport()
2161                               .getAlignment().findName(sequence)
2162                       : matcher.findIdMatch(sequence) };
2163             }
2164
2165           }
2166           else
2167           {
2168             param = st.nextToken();
2169             List<SequenceI> tmp = new ArrayList<>();
2170             List<String> tmp2 = new ArrayList<>();
2171
2172             while (st.hasMoreTokens())
2173             {
2174               seqstring = st.nextToken();
2175               StringTokenizer st2 = new StringTokenizer(seqstring, "=");
2176               if (st2.countTokens() > 1)
2177               {
2178                 // This is the chain
2179                 tmp2.add(st2.nextToken());
2180                 seqstring = st2.nextToken();
2181               }
2182               tmp.add(matcher == null
2183                       ? (Sequence) alignFrame.getAlignViewport()
2184                               .getAlignment().findName(seqstring)
2185                       : matcher.findIdMatch(seqstring));
2186             }
2187
2188             seqs = tmp.toArray(new SequenceI[tmp.size()]);
2189             if (tmp2.size() == tmp.size())
2190             {
2191               chains = tmp2.toArray(new String[tmp2.size()]);
2192             }
2193           }
2194           param = resolveFileProtocol(param);
2195           // TODO check JAL-357 for files in a jar (CLASSLOADER)
2196           pdb.setFile(param);
2197
2198           if (seqs != null)
2199           {
2200             for (int i = 0; i < seqs.length; i++)
2201             {
2202               if (seqs[i] != null)
2203               {
2204                 ((Sequence) seqs[i]).addPDBId(pdb);
2205                 StructureSelectionManager
2206                         .getStructureSelectionManager(applet)
2207                         .registerPDBEntry(pdb);
2208               }
2209               else
2210               {
2211                 if (JalviewLite.debug)
2212                 {
2213                   // this may not really be a problem but we give a warning
2214                   // anyway
2215                   System.err.println(
2216                           "Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
2217                                   + i + ")");
2218                 }
2219               }
2220             }
2221
2222             if (!alignPdbStructures)
2223             {
2224               alignFrame.newStructureView(applet, pdb, seqs, chains,
2225                       protocol);
2226             }
2227             else
2228             {
2229               pdbs.addElement(new Object[] { pdb, seqs, chains, protocol });
2230             }
2231           }
2232         }
2233
2234         pdbFileCount++;
2235       } while (param != null || pdbFileCount < 10);
2236       if (pdbs.size() > 0)
2237       {
2238         SequenceI[][] seqs = new SequenceI[pdbs.size()][];
2239         PDBEntry[] pdb = new PDBEntry[pdbs.size()];
2240         String[][] chains = new String[pdbs.size()][];
2241         String[] protocols = new String[pdbs.size()];
2242         for (int pdbsi = 0, pdbsiSize = pdbs
2243                 .size(); pdbsi < pdbsiSize; pdbsi++)
2244         {
2245           Object[] o = (Object[]) pdbs.elementAt(pdbsi);
2246           pdb[pdbsi] = (PDBEntry) o[0];
2247           seqs[pdbsi] = (SequenceI[]) o[1];
2248           chains[pdbsi] = (String[]) o[2];
2249           protocols[pdbsi] = (String) o[3];
2250         }
2251         alignFrame.alignedStructureView(applet, pdb, seqs, chains,
2252                 protocols);
2253         result = true;
2254       }
2255       return result;
2256     }
2257
2258     /**
2259      * Load in a Jnetfile if specified by parameter. Returns true if loaded,
2260      * else false.
2261      * 
2262      * @param alignFrame
2263      * @return
2264      */
2265     protected boolean loadJnetFile(AlignFrame alignFrame)
2266     {
2267       boolean result = false;
2268       String param = applet.getParameter("jnetfile");
2269       if (param == null)
2270       {
2271         // jnet became jpred around 2016
2272         param = applet.getParameter("jpredfile");
2273       }
2274       if (param != null)
2275       {
2276         try
2277         {
2278           param = resolveFileProtocol(param);
2279           JPredFile predictions = new JPredFile(param, protocol);
2280           JnetAnnotationMaker.add_annotation(predictions,
2281                   alignFrame.viewport.getAlignment(), 0, false);
2282           // false == do not add sequence profile from concise output
2283
2284           alignFrame.viewport.getAlignment().setupJPredAlignment();
2285
2286           alignFrame.alignPanel.fontChanged();
2287           alignFrame.alignPanel.setScrollValues(0, 0);
2288           result = true;
2289         } catch (Exception ex)
2290         {
2291           ex.printStackTrace();
2292         }
2293       }
2294       return result;
2295     }
2296
2297     /**
2298      * Load annotations if specified by parameter. Returns true if loaded, else
2299      * false.
2300      * 
2301      * @param alignFrame
2302      * @return
2303      */
2304     protected boolean loadAnnotations(AlignFrame alignFrame)
2305     {
2306       boolean result = false;
2307       String param = applet.getParameter("annotations");
2308       if (param != null)
2309       {
2310         param = resolveFileProtocol(param);
2311
2312         if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
2313                 param, protocol))
2314         {
2315           alignFrame.alignPanel.fontChanged();
2316           alignFrame.alignPanel.setScrollValues(0, 0);
2317           result = true;
2318         }
2319         else
2320         {
2321           System.err.println(
2322                   "Annotations were not added from annotation file '"
2323                           + param + "'");
2324         }
2325       }
2326       return result;
2327     }
2328
2329     /**
2330      * Load features file and view settings as specified by parameters. Returns
2331      * true if features were loaded, else false.
2332      * 
2333      * @param alignFrame
2334      * @return
2335      */
2336     protected boolean loadFeatures(AlignFrame alignFrame)
2337     {
2338       boolean result = false;
2339       // ///////////////////////////
2340       // modify display of features
2341       // we do this before any features have been loaded, ensuring any hidden
2342       // groups are hidden when features first displayed
2343       //
2344       // hide specific groups
2345       //
2346       String param = applet.getParameter("hidefeaturegroups");
2347       if (param != null)
2348       {
2349         alignFrame.setFeatureGroupState(separatorListToArray(param), false);
2350         // applet.setFeatureGroupStateOn(newAlignFrame, param, false);
2351       }
2352       // show specific groups
2353       param = applet.getParameter("showfeaturegroups");
2354       if (param != null)
2355       {
2356         alignFrame.setFeatureGroupState(separatorListToArray(param), true);
2357         // applet.setFeatureGroupStateOn(newAlignFrame, param, true);
2358       }
2359       // and now load features
2360       param = applet.getParameter("features");
2361       if (param != null)
2362       {
2363         param = resolveFileProtocol(param);
2364
2365         result = alignFrame.parseFeaturesFile(param, protocol);
2366       }
2367
2368       param = applet.getParameter("showFeatureSettings");
2369       if (param != null && param.equalsIgnoreCase(TRUE))
2370       {
2371         alignFrame.viewport.setShowSequenceFeatures(true);
2372         new FeatureSettings(alignFrame.alignPanel);
2373       }
2374       return result;
2375     }
2376
2377     /**
2378      * Load a score file if specified by parameter. Returns true if file was
2379      * loaded, else false.
2380      * 
2381      * @param alignFrame
2382      */
2383     protected boolean loadScoreFile(AlignFrame alignFrame)
2384     {
2385       boolean result = false;
2386       String sScoreFile = applet.getParameter("scoreFile");
2387       if (sScoreFile != null && !"".equals(sScoreFile))
2388       {
2389         try
2390         {
2391           if (debug)
2392           {
2393             System.err.println(
2394                     "Attempting to load T-COFFEE score file from the scoreFile parameter");
2395           }
2396           result = alignFrame.loadScoreFile(sScoreFile);
2397           if (!result)
2398           {
2399             System.err.println(
2400                     "Failed to parse T-COFFEE parameter as a valid score file ('"
2401                             + sScoreFile + "')");
2402           }
2403         } catch (Exception e)
2404         {
2405           System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
2406                   sScoreFile, e.getMessage());
2407         }
2408       }
2409       return result;
2410     }
2411
2412     /**
2413      * Load a tree for the alignment if specified by parameter. Returns true if
2414      * a tree was loaded, else false.
2415      * 
2416      * @param alignFrame
2417      * @return
2418      */
2419     protected boolean loadTree(AlignFrame alignFrame)
2420     {
2421       boolean result = false;
2422       String treeFile = applet.getParameter("tree");
2423       if (treeFile == null)
2424       {
2425         treeFile = applet.getParameter("treeFile");
2426       }
2427
2428       if (treeFile != null)
2429       {
2430         try
2431         {
2432           treeFile = resolveFileProtocol(treeFile);
2433           NewickFile fin = new NewickFile(treeFile, protocol);
2434           fin.parse();
2435
2436           if (fin.getTree() != null)
2437           {
2438             alignFrame.loadTree(fin, treeFile);
2439             result = true;
2440             dbgMsg("Successfully imported tree.");
2441           }
2442           else
2443           {
2444             dbgMsg("Tree parameter did not resolve to a valid tree.");
2445           }
2446         } catch (Exception ex)
2447         {
2448           ex.printStackTrace();
2449         }
2450       }
2451       return result;
2452     }
2453
2454     /**
2455      * Discovers whether the given file is in the Applet Archive
2456      * 
2457      * @param f
2458      *          String
2459      * @return boolean
2460      */
2461     boolean inArchive(String f)
2462     {
2463       // This might throw a security exception in certain browsers
2464       // Netscape Communicator for instance.
2465       try
2466       {
2467         boolean rtn = (getClass().getResourceAsStream("/" + f) != null);
2468         if (debug)
2469         {
2470           System.err.println("Resource '" + f + "' was "
2471                   + (rtn ? "" : "not ") + "located by classloader.");
2472         }
2473         return rtn;
2474       } catch (Exception ex)
2475       {
2476         System.out.println("Exception checking resources: " + f + " " + ex);
2477         return false;
2478       }
2479     }
2480   }
2481
2482   /**
2483    * @return the default alignFrame acted on by the public applet methods. May
2484    *         return null with an error message on System.err indicating the
2485    *         fact.
2486    */
2487   public AlignFrame getDefaultTargetFrame()
2488   {
2489     if (currentAlignFrame != null)
2490     {
2491       return currentAlignFrame;
2492     }
2493     if (initialAlignFrame != null)
2494     {
2495       return initialAlignFrame;
2496     }
2497     System.err.println(
2498             "Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
2499     return null;
2500   }
2501
2502   /**
2503    * separator used for separatorList
2504    */
2505   protected String separator = "" + ((char) 0x00AC); // the default used to be
2506                                                      // '|' but many sequence
2507                                                      // IDS include pipes.
2508
2509   /**
2510    * set to enable the URL based javascript execution mechanism
2511    */
2512   public boolean jsfallbackEnabled = false;
2513
2514   /**
2515    * parse the string into a list
2516    * 
2517    * @param list
2518    * @return elements separated by separator
2519    */
2520   public String[] separatorListToArray(String list)
2521   {
2522     return separatorListToArray(list, separator);
2523   }
2524
2525   /**
2526    * parse the string into a list
2527    * 
2528    * @param list
2529    * @param separator
2530    * @return elements separated by separator
2531    */
2532   public static String[] separatorListToArray(String list, String separator)
2533   {
2534     // TODO use StringUtils version (slightly different...)
2535     int seplen = separator.length();
2536     if (list == null || list.equals("") || list.equals(separator))
2537     {
2538       return null;
2539     }
2540     java.util.Vector jv = new Vector();
2541     int cp = 0, pos;
2542     while ((pos = list.indexOf(separator, cp)) > cp)
2543     {
2544       jv.addElement(list.substring(cp, pos));
2545       cp = pos + seplen;
2546     }
2547     if (cp < list.length())
2548     {
2549       String c = list.substring(cp);
2550       if (!c.equals(separator))
2551       {
2552         jv.addElement(c);
2553       }
2554     }
2555     if (jv.size() > 0)
2556     {
2557       String[] v = new String[jv.size()];
2558       for (int i = 0; i < v.length; i++)
2559       {
2560         v[i] = (String) jv.elementAt(i);
2561       }
2562       jv.removeAllElements();
2563       if (debug)
2564       {
2565         System.err.println("Array from '" + separator
2566                 + "' separated List:\n" + v.length);
2567         for (int i = 0; i < v.length; i++)
2568         {
2569           System.err.println("item " + i + " '" + v[i] + "'");
2570         }
2571       }
2572       return v;
2573     }
2574     if (debug)
2575     {
2576       System.err.println(
2577               "Empty Array from '" + separator + "' separated List");
2578     }
2579     return null;
2580   }
2581
2582   /**
2583    * concatenate the list with separator
2584    * 
2585    * @param list
2586    * @return concatenated string
2587    */
2588   public String arrayToSeparatorList(String[] list)
2589   {
2590     return arrayToSeparatorList(list, separator);
2591   }
2592
2593   /**
2594    * concatenate the list with separator
2595    * 
2596    * @param list
2597    * @param separator
2598    * @return concatenated string
2599    */
2600   public static String arrayToSeparatorList(String[] list, String separator)
2601   {
2602     // TODO use StringUtils version
2603     StringBuffer v = new StringBuffer();
2604     if (list != null && list.length > 0)
2605     {
2606       for (int i = 0, iSize = list.length; i < iSize; i++)
2607       {
2608         if (list[i] != null)
2609         {
2610           if (i > 0)
2611           {
2612             v.append(separator);
2613           }
2614           v.append(list[i]);
2615         }
2616       }
2617       if (debug)
2618       {
2619         System.err
2620                 .println("Returning '" + separator + "' separated List:\n");
2621         System.err.println(v);
2622       }
2623       return v.toString();
2624     }
2625     if (debug)
2626     {
2627       System.err.println(
2628               "Returning empty '" + separator + "' separated List\n");
2629     }
2630     return "" + separator;
2631   }
2632
2633   /*
2634    * (non-Javadoc)
2635    * 
2636    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroups()
2637    */
2638   @Override
2639   public String getFeatureGroups()
2640   {
2641     String lst = arrayToSeparatorList(
2642             getDefaultTargetFrame().getFeatureGroups());
2643     return lst;
2644   }
2645
2646   /*
2647    * (non-Javadoc)
2648    * 
2649    * @see
2650    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOn(jalview.appletgui.AlignFrame
2651    * )
2652    */
2653   @Override
2654   public String getFeatureGroupsOn(AlignFrame alf)
2655   {
2656     String lst = arrayToSeparatorList(alf.getFeatureGroups());
2657     return lst;
2658   }
2659
2660   /*
2661    * (non-Javadoc)
2662    * 
2663    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfState(boolean)
2664    */
2665   @Override
2666   public String getFeatureGroupsOfState(boolean visible)
2667   {
2668     return arrayToSeparatorList(
2669             getDefaultTargetFrame().getFeatureGroupsOfState(visible));
2670   }
2671
2672   /*
2673    * (non-Javadoc)
2674    * 
2675    * @see
2676    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfStateOn(jalview.appletgui
2677    * .AlignFrame, boolean)
2678    */
2679   @Override
2680   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
2681   {
2682     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
2683   }
2684
2685   /*
2686    * (non-Javadoc)
2687    * 
2688    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupStateOn(jalview.appletgui.
2689    * AlignFrame, java.lang.String, boolean)
2690    */
2691   @Override
2692   public void setFeatureGroupStateOn(final AlignFrame alf,
2693           final String groups, boolean state)
2694   {
2695     final boolean st = state;// !(state==null || state.equals("") ||
2696     // state.toLowerCase(Locale.ROOT).equals("false"));
2697     java.awt.EventQueue.invokeLater(new Runnable()
2698     {
2699       @Override
2700       public void run()
2701       {
2702         alf.setFeatureGroupState(separatorListToArray(groups), st);
2703       }
2704     });
2705   }
2706
2707   /*
2708    * (non-Javadoc)
2709    * 
2710    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupState(java.lang.String,
2711    * boolean)
2712    */
2713   @Override
2714   public void setFeatureGroupState(String groups, boolean state)
2715   {
2716     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
2717   }
2718
2719   /*
2720    * (non-Javadoc)
2721    * 
2722    * @see jalview.bin.JalviewLiteJsApi#getSeparator()
2723    */
2724   @Override
2725   public String getSeparator()
2726   {
2727     return separator;
2728   }
2729
2730   /*
2731    * (non-Javadoc)
2732    * 
2733    * @see jalview.bin.JalviewLiteJsApi#setSeparator(java.lang.String)
2734    */
2735   @Override
2736   public void setSeparator(String separator)
2737   {
2738     if (separator == null || separator.length() < 1)
2739     {
2740       // reset to default
2741       separator = "" + ((char) 0x00AC);
2742     }
2743     this.separator = separator;
2744     if (debug)
2745     {
2746       System.err.println("Default Separator now: '" + separator + "'");
2747     }
2748   }
2749
2750   /**
2751    * get boolean value of applet parameter 'name' and return default if
2752    * parameter is not set
2753    * 
2754    * @param name
2755    *          name of paremeter
2756    * @param def
2757    *          the value to return otherwise
2758    * @return true or false
2759    */
2760   public boolean getDefaultParameter(String name, boolean def)
2761   {
2762     String stn;
2763     if ((stn = getParameter(name)) == null)
2764     {
2765       return def;
2766     }
2767     if (TRUE.equalsIgnoreCase(stn))
2768     {
2769       return true;
2770     }
2771     return false;
2772   }
2773
2774   /*
2775    * (non-Javadoc)
2776    * 
2777    * @see jalview.bin.JalviewLiteJsApi#addPdbFile(jalview.appletgui.AlignFrame,
2778    * java.lang.String, java.lang.String, java.lang.String)
2779    */
2780   @Override
2781   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
2782           String pdbEntryString, String pdbFile)
2783   {
2784     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
2785   }
2786
2787   protected void setAlignPdbStructures(boolean alignPdbStructures)
2788   {
2789     this.alignPdbStructures = alignPdbStructures;
2790   }
2791
2792   public boolean isAlignPdbStructures()
2793   {
2794     return alignPdbStructures;
2795   }
2796
2797   @Override
2798   public void start()
2799   {
2800     // callInitCallback();
2801   }
2802
2803   private Hashtable<String, long[]> jshashes = new Hashtable<>();
2804
2805   private Hashtable<String, Hashtable<String, String[]>> jsmessages = new Hashtable<>();
2806
2807   public void setJsMessageSet(String messageclass, String viewId,
2808           String[] colcommands)
2809   {
2810     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2811     if (msgset == null)
2812     {
2813       msgset = new Hashtable<>();
2814       jsmessages.put(messageclass, msgset);
2815     }
2816     msgset.put(viewId, colcommands);
2817     long[] l = new long[colcommands.length];
2818     for (int i = 0; i < colcommands.length; i++)
2819     {
2820       l[i] = colcommands[i].hashCode();
2821     }
2822     jshashes.put(messageclass + "|" + viewId, l);
2823   }
2824
2825   /*
2826    * (non-Javadoc)
2827    * 
2828    * @see jalview.bin.JalviewLiteJsApi#getJsMessage(java.lang.String,
2829    * java.lang.String)
2830    */
2831   @Override
2832   public String getJsMessage(String messageclass, String viewId)
2833   {
2834     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
2835     if (msgset != null)
2836     {
2837       String[] msgs = msgset.get(viewId);
2838       if (msgs != null)
2839       {
2840         for (int i = 0; i < msgs.length; i++)
2841         {
2842           if (msgs[i] != null)
2843           {
2844             String m = msgs[i];
2845             msgs[i] = null;
2846             return m;
2847           }
2848         }
2849       }
2850     }
2851     return "";
2852   }
2853
2854   public boolean isJsMessageSetChanged(String string, String string2,
2855           String[] colcommands)
2856   {
2857     long[] l = jshashes.get(string + "|" + string2);
2858     if (l == null && colcommands != null)
2859     {
2860       return true;
2861     }
2862     for (int i = 0; i < colcommands.length; i++)
2863     {
2864       if (l[i] != colcommands[i].hashCode())
2865       {
2866         return true;
2867       }
2868     }
2869     return false;
2870   }
2871
2872   private Vector jsExecQueue = new Vector();
2873
2874   public Vector getJsExecQueue()
2875   {
2876     return jsExecQueue;
2877   }
2878
2879   public void setExecutor(JSFunctionExec jsFunctionExec2)
2880   {
2881     jsFunctionExec = jsFunctionExec2;
2882   }
2883
2884   /**
2885    * return the given colour value parameter or the given default if parameter
2886    * not given
2887    * 
2888    * @param colparam
2889    * @param defcolour
2890    * @return
2891    */
2892   public Color getDefaultColourParameter(String colparam, Color defcolour)
2893   {
2894     String colprop = getParameter(colparam);
2895     if (colprop == null || colprop.trim().length() == 0)
2896     {
2897       return defcolour;
2898     }
2899     Color col = ColorUtils.parseColourString(colprop);
2900     if (col == null)
2901     {
2902       System.err.println("Couldn't parse '" + colprop + "' as a colour for "
2903               + colparam);
2904     }
2905     return (col == null) ? defcolour : col;
2906   }
2907
2908   public void openJalviewHelpUrl()
2909   {
2910     String helpUrl = getParameter("jalviewhelpurl");
2911     if (helpUrl == null || helpUrl.trim().length() < 5)
2912     {
2913       helpUrl = "http://www.jalview.org/help.html";
2914     }
2915     showURL(helpUrl, "HELP");
2916   }
2917
2918   /**
2919    * form a complete URL given a path to a resource and a reference location on
2920    * the same server
2921    * 
2922    * @param targetPath
2923    *          - an absolute path on the same server as localref or a document
2924    *          located relative to localref
2925    * @param localref
2926    *          - a URL on the same server as url
2927    * @return a complete URL for the resource located by url
2928    */
2929   private String resolveUrlForLocalOrAbsolute(String targetPath,
2930           URL localref)
2931   {
2932     String resolvedPath = "";
2933     if (targetPath.startsWith("/"))
2934     {
2935       String codebase = localref.toString();
2936       String localfile = localref.getFile();
2937       resolvedPath = codebase.substring(0,
2938               codebase.length() - localfile.length()) + targetPath;
2939       return resolvedPath;
2940     }
2941
2942     /*
2943      * get URL path and strip off any trailing file e.g.
2944      * www.jalview.org/examples/index.html#applets?a=b is trimmed to
2945      * www.jalview.org/examples/
2946      */
2947     String urlPath = localref.toString();
2948     String directoryPath = urlPath;
2949     int lastSeparator = directoryPath.lastIndexOf("/");
2950     if (lastSeparator > 0)
2951     {
2952       directoryPath = directoryPath.substring(0, lastSeparator + 1);
2953     }
2954
2955     if (targetPath.startsWith("/"))
2956     {
2957       /*
2958        * construct absolute URL to a file on the server - this is not allowed?
2959        */
2960       // String localfile = localref.getFile();
2961       // resolvedPath = urlPath.substring(0,
2962       // urlPath.length() - localfile.length())
2963       // + targetPath;
2964       resolvedPath = directoryPath + targetPath.substring(1);
2965     }
2966     else
2967     {
2968       resolvedPath = directoryPath + targetPath;
2969     }
2970     if (debug)
2971     {
2972       System.err.println(
2973               "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
2974     }
2975     return resolvedPath;
2976   }
2977
2978   /**
2979    * open a URL in the browser - resolving it according to relative refs and
2980    * coping with javascript: protocol if necessary.
2981    * 
2982    * @param url
2983    * @param target
2984    */
2985   public void showURL(String url, String target)
2986   {
2987     try
2988     {
2989       if (url.indexOf(":") == -1)
2990       {
2991         // TODO: verify (Bas Vroling bug) prepend codebase or server URL to
2992         // form valid URL
2993         // Should really use docbase, not codebase.
2994         URL prepend;
2995         url = resolveUrlForLocalOrAbsolute(url,
2996                 prepend = getDefaultParameter("resolvetocodebase", false)
2997                         ? getCodeBase()
2998                         : getDocumentBase());
2999         if (debug)
3000         {
3001           System.err.println("Show url (prepended " + prepend
3002                   + " - toggle resolvetocodebase if code/docbase resolution is wrong): "
3003                   + url);
3004         }
3005       }
3006       else
3007       {
3008         if (debug)
3009         {
3010           System.err.println("Show url: " + url);
3011         }
3012       }
3013       if (url.indexOf("javascript:") == 0)
3014       {
3015         // no target for the javascript context
3016         getAppletContext().showDocument(new java.net.URL(url));
3017       }
3018       else
3019       {
3020         getAppletContext().showDocument(new java.net.URL(url), target);
3021       }
3022     } catch (Exception ex)
3023     {
3024       ex.printStackTrace();
3025     }
3026   }
3027
3028   /**
3029    * bind structures in a viewer to any matching sequences in an alignFrame (use
3030    * sequenceIds to limit scope of search to specific sequences)
3031    * 
3032    * @param alFrame
3033    * @param viewer
3034    * @param sequenceIds
3035    * @return TODO: consider making an exception structure for indicating when
3036    *         binding fails public SequenceStructureBinding
3037    *         addStructureViewInstance( AlignFrame alFrame, Object viewer, String
3038    *         sequenceIds) {
3039    * 
3040    *         if (sequenceIds != null && sequenceIds.length() > 0) { return
3041    *         alFrame.addStructureViewInstance(viewer,
3042    *         separatorListToArray(sequenceIds)); } else { return
3043    *         alFrame.addStructureViewInstance(viewer, null); } // return null; }
3044    */
3045 }