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