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