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