JAL-3446 reorganized JalviewJSApp (accidentally put that in jalview/api)
[jalview.git] / src / jalview / bin / JalviewJSApp.java
1 package jalview.bin;
2
3 import java.awt.EventQueue;
4 //import java.applet.AppletContext;
5 import java.io.IOException;
6 import java.net.URL;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.StringTokenizer;
10 import java.util.Vector;
11
12 import jalview.api.JalviewJSApi;
13 import jalview.api.StructureSelectionManagerProvider;
14 import jalview.datamodel.Alignment;
15 import jalview.datamodel.AlignmentI;
16 import jalview.datamodel.AlignmentOrder;
17 import jalview.datamodel.ColumnSelection;
18 import jalview.datamodel.HiddenColumns;
19 import jalview.datamodel.PDBEntry;
20 import jalview.datamodel.Sequence;
21 import jalview.datamodel.SequenceGroup;
22 import jalview.datamodel.SequenceI;
23 import jalview.gui.AlignFrame;
24 import jalview.gui.AlignViewport;
25 import jalview.gui.CalculationChooser;
26 import jalview.gui.Desktop;
27 import jalview.gui.StructureViewer;
28 import jalview.io.AnnotationFile;
29 import jalview.io.AppletFormatAdapter;
30 import jalview.io.DataSourceType;
31 import jalview.io.FeaturesFile;
32 import jalview.io.FileFormat;
33 import jalview.io.FileFormatI;
34 import jalview.io.FileFormats;
35 import jalview.io.IdentifyFile;
36 import jalview.io.JPredFile;
37 import jalview.io.JnetAnnotationMaker;
38 import jalview.io.NewickFile;
39 import jalview.structure.SelectionListener;
40 import jalview.structure.SelectionSource;
41 import jalview.structure.StructureSelectionManager;
42 import jalview.util.HttpUtils;
43 import jalview.util.Platform;
44
45 /**
46  * Basically the JalviewLite application, but without JalviewLite
47  * 
48  * Processing all "applet parameters" and also all "applet interface" methods.
49  * 
50  * @author hansonr
51  *
52  */
53 public class JalviewJSApp implements JalviewJSApi
54 {
55   private ArgsParser aparser;
56
57   private String[] ret = new String[1];
58
59   // private boolean alignPDBStructures; From JalviewLite; not implemented
60
61   private String separator = "\u00AC"; // JalviewLite note: the default used to
62                                        // be '|', but many sequence IDS include
63                                        // pipes.
64
65   /**
66    * We maintain a pointer to the jalview instance here, because only with that
67    * do we have a direct connection from the JavaScript "applet" object to the
68    * proper instance of Jalview in case there are multiple applets on a page.
69    */
70   private Jalview jalview;
71
72   public class JsSelectionListener
73           implements jalview.structure.SelectionListener
74   {
75
76     AlignFrame _alf;
77
78     String _listener;
79
80     public JsSelectionListener(AlignFrame alf, String listener)
81     {
82       _alf = alf;
83       _listener = listener;
84     }
85
86     public boolean isFor(AlignFrame alf, String listener)
87     {
88       return (_alf == null || _alf == alf) && _listener.equals(listener);
89     }
90
91     @Override
92     public void selection(SequenceGroup seqsel, ColumnSelection colsel,
93             HiddenColumns hidden, SelectionSource source)
94     {
95       // System.err.println("Testing selection event relay to
96       // jsfunction:"+_listener);
97       String setid = "";
98       AlignFrame srcFrame = (_alf == null ? getCurrentAlignFrame() : _alf);
99       if (source != null)
100       {
101         if (source instanceof AlignViewport
102                 && srcFrame.getViewport() != source)
103         {
104           return;
105         }
106       }
107       String[] seqs = new String[] {};
108       String[] cols = new String[] {};
109       int strt = 0, end = (srcFrame == null) ? -1
110               : srcFrame.alignPanel.av.getAlignment().getWidth();
111       if (seqsel != null && seqsel.getSize() > 0)
112       {
113         seqs = new String[seqsel.getSize()];
114         for (int i = 0; i < seqs.length; i++)
115         {
116           seqs[i] = seqsel.getSequenceAt(i).getName();
117         }
118         if (strt < seqsel.getStartRes())
119         {
120           strt = seqsel.getStartRes();
121         }
122         if (end == -1 || end > seqsel.getEndRes())
123         {
124           end = seqsel.getEndRes();
125         }
126       }
127       if (colsel != null && !colsel.isEmpty())
128       {
129         if (end == -1)
130         {
131           end = colsel.getMax() + 1;
132         }
133         cols = new String[colsel.getSelected().size()];
134         for (int i = 0; i < cols.length; i++)
135         {
136           cols[i] = "" + (1 + colsel.getSelected().get(i).intValue());
137         }
138       }
139       else
140       {
141         if (seqsel != null && seqsel.getSize() > 0)
142         {
143           // send a valid range, otherwise we send the empty selection
144           cols = new String[1];
145           cols[0] = "" + (1 + strt) + "-" + (1 + end);
146         }
147       }
148       doSendCallback(_listener,
149               new Object[]
150               { Jalview.getInstance().j2sAppletID, srcFrame, source, setid,
151                   seqs, cols });
152     }
153
154   }
155
156   public JalviewJSApp(Jalview jalview, ArgsParser aparser, AlignFrame alf)
157   {
158     Platform.setAppClass(this);
159     this.jalview = jalview;
160     this.aparser = aparser;
161     initFromParams(alf);
162     callInitCallback();
163   }
164
165   @Override
166   public boolean addPdbFile(String sequenceId, String pdbId, String pdbFile,
167           AlignFrame alf)
168   {
169     if (alf == null)
170     {
171       alf = getCurrentAlignFrame();
172     }
173     SequenceI seq = alf.getViewport().getAlignment().findName(sequenceId);
174     if (seq != null)
175     {
176       Vector<PDBEntry> pdbe = seq.getAllPDBEntries();
177       PDBEntry pdbentry = null;
178       if (pdbe != null && pdbe.size() > 0)
179       {
180         for (int pe = 0, peSize = pdbe.size(); pe < peSize; pe++)
181         {
182           pdbentry = pdbe.elementAt(pe);
183           if (!pdbentry.getId().equals(pdbId)
184                   || pdbFile != null && !pdbentry.getFile().equals(pdbFile))
185           {
186             pdbentry = null;
187           }
188         }
189       }
190       if (pdbentry == null)
191       {
192         pdbentry = new PDBEntry(pdbId, null, pdbFile);
193         if (pdbFile != null)
194         {
195           DataSourceType protocol = AppletFormatAdapter
196                   .resolveProtocol(pdbFile, FileFormat.PDB);
197           if (protocol == null)
198             return false;
199           pdbentry.setProperty("protocol", protocol);
200         }
201         seq.addPDBId(pdbentry);
202         alf.alignPanel.getStructureSelectionManager()
203                 .registerPDBEntry(pdbentry);
204       }
205     }
206     return true;
207   }
208
209   @Override
210   public String getAlignment(String format, boolean addSuffix,
211           AlignFrame alf)
212   {
213     try
214     {
215       if (alf == null)
216       {
217         alf = getCurrentAlignFrame();
218       }
219
220       FileFormatI theFormat = FileFormats.getInstance().forName(format);
221       String reply = new AppletFormatAdapter().formatSequences(theFormat,
222               alf.getViewport().getAlignment(), addSuffix);
223       return reply;
224     } catch (IllegalArgumentException ex)
225     {
226       ex.printStackTrace();
227       return "Error retrieving alignment, possibly invalid format specifier: "
228               + format;
229     }
230   }
231
232   @Override
233   public String[] getAlignmentOrder(AlignFrame alf)
234   {
235     if (alf == null)
236     {
237       alf = getCurrentAlignFrame();
238     }
239     AlignmentI alorder = alf.getViewport().getAlignment();
240     String[] order = new String[alorder.getHeight()];
241     for (int i = 0; i < order.length; i++)
242     {
243       order[i] = alorder.getSequenceAt(i).getName();
244     }
245     return order;// arrayToSeparatorList(order, sep);
246   }
247
248   @Override
249   public String getAnnotation(AlignFrame alf)
250   {
251     if (alf == null)
252     {
253       alf = getCurrentAlignFrame();
254     }
255     String annotation = new AnnotationFile()
256             .printAnnotationsForView(alf.getViewport());
257     return annotation;
258   }
259
260   /**
261    * Get the applet-like code base even though this is an application.
262    */
263
264   @Override
265   public URL getCodeBase()
266   {
267     return Platform.getCodeBase();
268   }
269
270   @Override
271   public AlignFrame getCurrentAlignFrame()
272   {
273     // if (jalview != Jalview.getInstance() || jalview.currentAlignFrame !=
274     // Jalview.getCurrentAlignFrame()) {
275     // /** @j2sNative debugger */
276     // }
277     return jalview.currentAlignFrame;
278   }
279
280   /**
281    * Get the applet-like document base even though this is an application.
282    */
283
284   @Override
285   public URL getDocumentBase()
286   {
287     return Platform.getDocumentBase();
288   }
289
290   @Override
291   public String[] getFeatureGroups(AlignFrame alf)
292   {
293     if (alf == null)
294     {
295       alf = getCurrentAlignFrame();
296     }
297     return alf.getFeatureGroups();
298   }
299
300   @Override
301   public String[] getFeatureGroupsOfState(boolean visible, AlignFrame alf)
302   {
303     if (alf == null)
304     {
305       alf = getCurrentAlignFrame();
306     }
307     return alf.getFeatureGroupsOfState(visible);
308   }
309
310   /**
311    * JavaScript interface to print the alignment frame
312    * 
313    * @param format
314    *          "jalview" or "gff" with or without ";includeComplement" or
315    *          ";includeNonpositional"; default with no ";" is
316    *          ";includeNonpositional"
317    * @param alf
318    * 
319    * @return
320    */
321   @Override
322   public String getFeatures(String format, AlignFrame alf)
323   {
324     if (alf == null)
325     {
326       alf = getCurrentAlignFrame();
327     }
328     String features;
329     FeaturesFile formatter = new FeaturesFile();
330     format = format.toLowerCase();
331     if (format.indexOf(";") < 0)
332       format += ";includenonpositional";
333     boolean nonpos = format.indexOf(";includenonpositional") >= 0;
334     boolean compl = format.indexOf(";includecomplement") >= 0;
335     if (format.startsWith("jalview"))
336     {
337       features = formatter.printJalviewFormat(
338               alf.getViewport().getAlignment().getSequencesArray(),
339               alf.alignPanel.getFeatureRenderer(), nonpos, compl);
340     }
341     else
342     {
343       features = formatter.printGffFormat(
344               alf.getViewport().getAlignment().getSequencesArray(),
345               alf.alignPanel.getFeatureRenderer(), nonpos, compl);
346     }
347
348     if (features == null)
349     {
350       features = "";
351     }
352     return features;
353
354   }
355
356   /**
357    * Get an applet parameter as a string.
358    * 
359    */
360   @Override
361   public String getParameter(String name)
362   {
363     return (String) aparser.getAppletValue(name, null, true);
364   }
365
366   /**
367    * Get an applet parameter as an Object.
368    */
369
370   @Override
371   public Object getParameterAsObject(String name)
372   {
373     return aparser.getAppletValue(name, null, false);
374   }
375
376   /**
377    * read sequence1...sequenceN as a raw alignment
378    * 
379    * @param jalviewApp
380    * @return
381    */
382   public String getPastedSequence(JalviewJSApp jalviewApp)
383   {
384     StringBuffer data = new StringBuffer("PASTE");
385     int i = 1;
386     String file = null;
387     while ((file = getParameter("sequence" + i)) != null)
388     {
389       data.append(file.toString() + "\n");
390       i++;
391     }
392     if (data.length() > 5)
393     {
394       file = data.toString();
395     }
396     return file;
397   }
398
399   /**
400    * @j2sAlias getSelectedSequences
401    * 
402    * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
403    *      .AlignFrame)
404    */
405   @Override
406   public SequenceI[] getSelectedSequences(AlignFrame alf)
407   {
408     // return getSelectedSequencesFrom(alf, null);
409     // }
410     //
411     // @Override
412     // public SequenceI[] getSelectedSequencesFrom(AlignFrame alf, String sep)
413     // {
414     if (alf == null)
415     {
416       alf = getCurrentAlignFrame();
417     }
418     AlignViewport v = alf.getViewport();
419     if (v.getSelectionGroup() != null)
420     {
421       return v.getSelectionGroup().getSequencesInOrder(v.getAlignment());
422     }
423     return null;
424   }
425   // /**
426   // *
427   // * @see
428   // jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
429   // * .AlignFrame, java.lang.String)
430   // */
431   // @Override
432   // public void highlight(String sequenceId, String position,
433   // String alignedPosition)
434   // {
435   // highlightIn(null, sequenceId, position, alignedPosition);
436   // }
437
438   /**
439    * @j2sAlias getSelectedSequencesAsAlignment
440    */
441   @Override
442   public String getSelectedSequencesAsAlignment(String format,
443           boolean addSuffix, AlignFrame alf)
444   {
445
446     if (alf == null)
447     {
448       alf = getCurrentAlignFrame();
449     }
450     try
451     {
452       AlignViewport vp = alf.getViewport();
453       FileFormatI theFormat = FileFormats.getInstance().forName(format);
454       if (vp.getSelectionGroup() != null)
455       {
456         // JBPNote: getSelectionAsNewSequence behaviour has changed - this
457         // method now returns a full copy of sequence data
458         // TODO consider using getSequenceSelection instead here
459         String reply = new AppletFormatAdapter().formatSequences(theFormat,
460                 new Alignment(vp.getSelectionAsNewSequence()), addSuffix);
461         return reply;
462       }
463     } catch (IllegalArgumentException ex)
464     {
465       ex.printStackTrace();
466       return "Error retrieving alignment, possibly invalid format specifier: "
467               + format;
468     }
469     return "";
470   }
471
472   @Override
473   public void highlight(String sequenceId, String position,
474           String alignedPosition, AlignFrame alf)
475   {
476     if (alf == null)
477     {
478       alf = getCurrentAlignFrame();
479     }
480     // TODO: could try to highlight in all alignments if alf==null
481     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
482             alf.getViewport().getAlignment().getSequencesArray());
483     final SequenceI sq = matcher.findIdMatch(sequenceId);
484     if (sq != null)
485     {
486       int apos = -1;
487       try
488       {
489         apos = Integer.valueOf(position).intValue();
490         apos--;
491       } catch (NumberFormatException ex)
492       {
493         return;
494       }
495       final int pos = apos;
496       // use vamsas listener to broadcast to all listeners in scope
497       if (alignedPosition != null && (alignedPosition.trim().length() == 0
498               || alignedPosition.toLowerCase().indexOf("false") > -1))
499       {
500         java.awt.EventQueue.invokeLater(new Runnable()
501         {
502           @Override
503           public void run()
504           {
505             StructureSelectionManager
506                     .getStructureSelectionManager(Desktop.getInstance())
507                     .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
508           }
509         });
510       }
511       else
512       {
513         java.awt.EventQueue.invokeLater(new Runnable()
514         {
515           @Override
516           public void run()
517           {
518             StructureSelectionManager
519                     .getStructureSelectionManager(Desktop.getInstance())
520                     .mouseOverVamsasSequence(sq, pos, null);
521           }
522         });
523       }
524     }
525   }
526
527   @Override
528   public AlignFrame loadAlignment(String text, String title, int width,
529           int height)
530   {
531     AlignmentI al = null;
532
533     try
534     {
535       FileFormatI format = new IdentifyFile().identify(text,
536               DataSourceType.PASTE);
537       al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE,
538               format);
539       if (al.getHeight() > 0)
540       {
541         return new AlignFrame(al,
542                 width > 0 ? width : AlignFrame.DEFAULT_WIDTH,
543                 height > 0 ? height : AlignFrame.DEFAULT_HEIGHT, title);
544       }
545     } catch (IOException ex)
546     {
547       ex.printStackTrace();
548     }
549     return null;
550   }
551
552   @Override
553   public void loadAnnotation(String annotation, AlignFrame alf)
554   {
555     if (alf == null)
556     {
557       alf = getCurrentAlignFrame();
558     }
559     if (new AnnotationFile().annotateAlignmentView(alf.getViewport(),
560             annotation, DataSourceType.PASTE))
561     {
562       alf.alignPanel.fontChanged();
563       alf.alignPanel.setScrollValues(0, 0);
564     }
565     else
566     {
567       alf.parseFeaturesFile(annotation, DataSourceType.PASTE);
568     }
569   }
570
571   @Override
572   public boolean loadFeatures(String features, boolean autoenabledisplay,
573           AlignFrame alf)
574   {
575     if (alf == null)
576     {
577       alf = getCurrentAlignFrame();
578     }
579     boolean ret = alf.parseFeaturesFile(features, DataSourceType.PASTE);
580     if (!ret)
581     {
582       return false;
583     }
584     if (autoenabledisplay)
585     {
586       alf.getViewport().setShowSequenceFeatures(true);
587       // this next was for a checkbox in JalviewLite
588       // ((AlignFrame) alf).getViewport().sequenceFeatures.setState(true);
589     }
590     return true;
591   }
592
593   @Override
594   public boolean loadScoreFile(String fileName, AlignFrame alf)
595   {
596     try
597     {
598       (alf == null ? getCurrentAlignFrame() : alf)
599               .loadJalviewDataFile(fileName, null, null, null);
600       return true;
601     } catch (Throwable t)
602     {
603       return false;
604     }
605   }
606
607   /**
608    * @j2sAlias openPcaPanel
609    * 
610    *           public static method for JalviewJS API to open a PCAPanel without
611    *           necessarily using a dialog.
612    * @param modelName
613    * @param alf
614    * 
615    * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
616    *         if number of sequences selected is inappropriate
617    */
618   @Override
619   public Object openPcaPanel(String modelName, AlignFrame alf)
620   {
621     if (alf == null)
622     {
623       alf = getCurrentAlignFrame();
624     }
625     return CalculationChooser.openPcaPanel(alf, modelName, null);
626   }
627
628   /**
629    * @j2sAlias openTreePanel
630    * 
631    *           Open a new Tree panel on the desktop statically. Params are
632    *           standard (not set by Groovy). No dialog is opened.
633    * @param treeType
634    * @param modelName
635    * @param alf
636    * 
637    * @return null, or the string "label.you_need_at_least_n_sequences" if number
638    *         of sequences selected is inappropriate
639    */
640   @Override
641   public Object openTreePanel(String treeType, String modelName,
642           AlignFrame alf)
643   {
644     if (alf == null)
645     {
646       alf = getCurrentAlignFrame();
647     }
648     return CalculationChooser.openTreePanel(alf, treeType, modelName, null);
649   }
650
651   @Override
652   public boolean orderAlignment(String[] ids, String undoName,
653           AlignFrame alf)
654   {
655     if (alf == null)
656       alf = getCurrentAlignFrame();
657     SequenceI[] sqs = null;
658     if (ids != null && ids.length > 0)
659     {
660       jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
661               alf.getViewport().getAlignment().getSequencesArray());
662       int s = 0;
663       sqs = new SequenceI[ids.length];
664       for (int i = 0; i < ids.length; i++)
665       {
666         if (ids[i].trim().length() == 0)
667         {
668           continue;
669         }
670         SequenceI sq = matcher.findIdMatch(ids[i]);
671         if (sq != null)
672         {
673           sqs[s++] = sq;
674         }
675       }
676       if (s > 0)
677       {
678         SequenceI[] sqq = new SequenceI[s];
679         System.arraycopy(sqs, 0, sqq, 0, s);
680         sqs = sqq;
681       }
682       else
683       {
684         sqs = null;
685       }
686     }
687     if (sqs == null)
688     {
689       return false;
690     }
691     ;
692     final AlignmentOrder aorder = new AlignmentOrder(sqs);
693
694     if (undoName != null && undoName.trim().length() == 0)
695     {
696       undoName = null;
697     }
698     final String _undoName = undoName;
699     // TODO: deal with synchronization here: cannot raise any events until
700     // alfter
701     // this has returned.
702     return alf.sortBy(aorder, _undoName);
703   }
704
705   /**
706    * Allow an outside entity to initiate the second half of argument parsing
707    * (only).
708    * 
709    * @param args
710    * @return null is good
711    */
712   @Override
713   public Object parseArguments(String[] args)
714   {
715
716     try
717     {
718       jalview.parseArguments(new ArgsParser(args), false);
719       return null;
720     } catch (Throwable t)
721     {
722       return t;
723     }
724   }
725
726   /**
727    * @j2sAlias parseFeatureFile
728    * 
729    * @param filename
730    * @param alf
731    * @return
732    */
733   @Override
734   public boolean parseFeaturesFile(String filename, AlignFrame alf)
735   {
736     ret[0] = filename;
737     DataSourceType protocol = resolveFileProtocol(ret);
738     if (protocol == null)
739       return false;
740     return (alf == null ? getCurrentAlignFrame() : alf)
741             .parseFeaturesFile(ret[0], protocol);
742   }
743
744   @Override
745   public void removeSelectionListener(String listener, AlignFrame alf)
746   {
747
748     List<SelectionListener> listeners = Desktop
749             .getStructureSelectionManager().getListeners();
750     for (int i = listeners.size(); --i >= 0;)
751     {
752       SelectionListener l = listeners.get(i);
753       if (l instanceof JsSelectionListener
754               && ((JsSelectionListener) l).isFor(alf, listener))
755       {
756         listeners.remove(i);
757         break;
758       }
759     }
760   }
761
762   private DataSourceType resolveFileProtocol(String[] retPath)
763   {
764     String path = retPath[0];
765     /*
766      * is it paste data?
767      */
768     if (path.startsWith("PASTE"))
769     {
770       retPath[0] = path.substring(5);
771       return DataSourceType.PASTE;
772     }
773
774     /*
775      * is it a URL?
776      */
777     if (path.indexOf("://") >= 0)
778     {
779       return DataSourceType.URL;
780     }
781
782     /*
783      * try relative to document root
784      */
785     URL documentBase = getDocumentBase();
786     String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
787     if (HttpUtils.isValidUrl(withDocBase))
788     {
789       // if (debug)
790       // {
791       // System.err.println("Prepended document base '" + documentBase
792       // + "' to make: '" + withDocBase + "'");
793       // }
794       retPath[0] = withDocBase;
795       return DataSourceType.URL;
796     }
797
798     /*
799      * try relative to codebase (if different to document base)
800      */
801     URL codeBase = getCodeBase();
802     String withCodeBase = resolveUrlForLocalOrAbsolute(path, codeBase);
803     if (!withCodeBase.equals(withDocBase)
804             && HttpUtils.isValidUrl(withCodeBase))
805     {
806       // if (debug)
807       // {
808       // System.err.println("Prepended codebase '" + codeBase
809       // + "' to make: '" + withCodeBase + "'");
810       // }
811       retPath[0] = withCodeBase;
812       return DataSourceType.URL;
813     }
814
815     /*
816      * try locating by classloader; try this last so files in the directory
817      * are resolved using document base
818      */
819     if (inArchive(getClass(), path))
820     {
821       return DataSourceType.CLASSLOADER;
822     }
823     return null;
824   }
825
826   @Override
827   public void scrollViewTo(int topRow, int leftHandColumn, AlignFrame alf)
828   {
829     // TODO test
830     java.awt.EventQueue.invokeLater(new Runnable()
831     {
832       @Override
833       public void run()
834       {
835         try
836         {
837           (alf == null ? getCurrentAlignFrame() : alf).scrollTo(topRow,
838                   leftHandColumn);
839         } catch (Exception ex)
840         {
841           System.err.println("Couldn't parse integer arguments (topRow='"
842                   + topRow + "' and leftHandColumn='" + leftHandColumn
843                   + "')");
844           ex.printStackTrace();
845         }
846       }
847     });
848   }
849
850   @Override
851   public void select(String ids[], String cols[], AlignFrame alf)
852   {
853     if (alf == null)
854       alf = getCurrentAlignFrame();
855     final SequenceGroup sel = new SequenceGroup();
856     final ColumnSelection csel = new ColumnSelection();
857     AlignmentI al = alf.getViewport().getAlignment();
858     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
859             alf.getViewport().getAlignment().getSequencesArray());
860     int start = 0, end = al.getWidth(), alw = al.getWidth();
861     boolean seqsfound = true;
862     if (ids != null && ids.length > 0)
863     {
864       seqsfound = false;
865       for (int i = 0; i < ids.length; i++)
866       {
867         if (ids[i].trim().length() == 0)
868         {
869           continue;
870         }
871         SequenceI sq = matcher.findIdMatch(ids[i]);
872         if (sq != null)
873         {
874           seqsfound = true;
875           sel.addSequence(sq, false);
876         }
877       }
878     }
879     boolean inseqpos = false;
880     if (cols != null && cols.length > 0)
881     {
882       boolean seset = false;
883       for (int i = 0; i < cols.length; i++)
884       {
885         String cl = cols[i].trim();
886         if (cl.length() == 0)
887         {
888           continue;
889         }
890         int p;
891         if ((p = cl.indexOf("-")) > -1)
892         {
893           int from = -1, to = -1;
894           try
895           {
896             from = Integer.valueOf(cl.substring(0, p)).intValue();
897             from--;
898           } catch (NumberFormatException ex)
899           {
900             System.err.println(
901                     "ERROR: Couldn't parse first integer in range element column selection string '"
902                             + cl + "' - format is 'from-to'");
903             return;
904           }
905           try
906           {
907             to = Integer.valueOf(cl.substring(p + 1)).intValue();
908             to--;
909           } catch (NumberFormatException ex)
910           {
911             System.err.println(
912                     "ERROR: Couldn't parse second integer in range element column selection string '"
913                             + cl + "' - format is 'from-to'");
914             return;
915           }
916           if (from >= 0 && to >= 0)
917           {
918             // valid range
919             if (from < to)
920             {
921               int t = to;
922               to = from;
923               to = t;
924             }
925             if (!seset)
926             {
927               start = from;
928               end = to;
929               seset = true;
930             }
931             else
932             {
933               // comment to prevent range extension
934               if (start > from)
935               {
936                 start = from;
937               }
938               if (end < to)
939               {
940                 end = to;
941               }
942             }
943             for (int r = from; r <= to; r++)
944             {
945               if (r >= 0 && r < alw)
946               {
947                 csel.addElement(r);
948               }
949             }
950           }
951           else
952           {
953             System.err.println("ERROR: Invalid Range '" + cl
954                     + "' deparsed as [" + from + "," + to + "]");
955           }
956         }
957         else
958         {
959           int r = -1;
960           try
961           {
962             r = Integer.valueOf(cl).intValue();
963             r--;
964           } catch (NumberFormatException ex)
965           {
966             if (cl.toLowerCase().equals("sequence"))
967             {
968               // we are in the dataset sequence's coordinate frame.
969               inseqpos = true;
970             }
971             else
972             {
973               System.err.println(
974                       "ERROR: Couldn't parse integer from point selection element of column selection string '"
975                               + cl + "'");
976               return;
977             }
978           }
979           if (r >= 0 && r <= alw)
980           {
981             if (!seset)
982             {
983               start = r;
984               end = r;
985               seset = true;
986             }
987             else
988             {
989               // comment to prevent range extension
990               if (start > r)
991               {
992                 start = r;
993               }
994               if (end < r)
995               {
996                 end = r;
997               }
998             }
999             csel.addElement(r);
1000           }
1001           else
1002           {
1003             System.err.println("ERROR: Invalid Point selection '" + cl
1004                     + "' deparsed as [" + r + "]");
1005           }
1006         }
1007       }
1008     }
1009     if (seqsfound)
1010     {
1011       // we only propagate the selection when it was the null selection, or the
1012       // given sequences were found in the alignment.
1013       if (inseqpos && sel.getSize() > 0)
1014       {
1015         // assume first sequence provides reference frame ?
1016         SequenceI rs = sel.getSequenceAt(0);
1017         start = rs.findIndex(start);
1018         end = rs.findIndex(end);
1019         List<Integer> cs = new ArrayList<>(csel.getSelected());
1020         csel.clear();
1021         for (Integer selectedCol : cs)
1022         {
1023           csel.addElement(rs.findIndex(selectedCol));
1024         }
1025       }
1026       sel.setStartRes(start);
1027       sel.setEndRes(end);
1028       AlignFrame af = alf;
1029       EventQueue.invokeLater(new Runnable()
1030       {
1031         @Override
1032         public void run()
1033         {
1034           af.select(sel, csel,
1035                   af.getCurrentView().getAlignment().getHiddenColumns());
1036         }
1037       });
1038     }
1039   }
1040
1041   //
1042   // @Override
1043   // public void setFeatureGroupState(String[] groups, boolean state)
1044   // {
1045   // setFeatureGroupState(null, groups, state);
1046   // }
1047   //
1048   // @Override
1049   // public void setFeatureGroupState(String[] groups, boolean state)
1050   // { // JalviewLite API
1051   // setFeatureGroupStateOn(null, groups, state);
1052   // }
1053   //
1054   @Override
1055   public void setFeatureGroupState(final String[] groups,
1056           boolean state, AlignFrame alf)
1057   {
1058     // setFeatureGroupState(alf, groups, state);
1059     // java.awt.EventQueue.invokeLater(new Runnable()
1060     // {
1061     // @Override
1062     // public void run()
1063     // {
1064     // (alf == null ? getCurrentAlignFrame() : alf)
1065     // .setFeatureGroupState(
1066     // separatorListToArray(groups, separator), state);
1067     // }
1068     // });
1069     // }
1070     //
1071     // public void setFeatureGroupState(AlignFrame alf, String[] groups, boolean
1072     // state) {
1073     (alf == null ? getCurrentAlignFrame() : alf)
1074             .setFeatureGroupState(groups, state);
1075   }
1076
1077   @Override
1078   public void setSelectionListener(String listener, AlignFrame alf)
1079   {
1080     Desktop.getStructureSelectionManager()
1081             .addSelectionListener(new JsSelectionListener(alf, listener));
1082   }
1083
1084   @Override
1085   public void showOverview()
1086   {
1087     getCurrentAlignFrame().overviewMenuItem_actionPerformed(null);
1088   }
1089
1090   /**
1091    * @j2sAlias showStructure
1092    */
1093   @Override
1094   public void showStructure(String pdbID, String fileType, AlignFrame alf)
1095   {
1096     if (alf == null)
1097       alf = getCurrentAlignFrame();
1098     PDBEntry pe = null;
1099     SequenceI[] seqs = null;
1100     if (pdbID == null)
1101     {
1102       seqs = alf.getViewport().getSequenceSelection();
1103       if (seqs.length == 0)
1104         seqs = alf.getViewport().getAlignment().getSequencesArray();
1105       for (int i = 0; i < seqs.length; i++)
1106       {
1107         Vector<PDBEntry> list = seqs[i].getAllPDBEntries();
1108         if (list.size() > 0)
1109         {
1110           pe = list.get(0);
1111           break;
1112         }
1113       }
1114     }
1115     if (pe == null)
1116     {
1117       if (pdbID == null)
1118         return;
1119       pe = new PDBEntry(pdbID, null, fileType);
1120       List<SequenceI> list = alf.getViewport().getAlignment()
1121               .getSequences();
1122       List<SequenceI> tmp = new ArrayList<SequenceI>();
1123       for (int i = 0; i < list.size(); i++)
1124       {
1125         SequenceI seq = list.get(i);
1126         if (seq.getPDBEntry(pdbID) != null)
1127         {
1128           tmp.add(seq);
1129         }
1130       }
1131       seqs = tmp.toArray(new SequenceI[tmp.size()]);
1132       alf.alignPanel.selectSequences(tmp);
1133     }
1134     StructureViewer.launchStructureViewer(alf.alignPanel, pe, seqs);
1135   }
1136
1137   // private or package-private methods
1138
1139   /**
1140    * form a complete URL given a path to a resource and a reference location on
1141    * the same server
1142    * 
1143    * @param targetPath
1144    *          - an absolute path on the same server as localref or a document
1145    *          located relative to localref
1146    * @param localref
1147    *          - a URL on the same server as url
1148    * @return a complete URL for the resource located by url
1149    */
1150   private static String resolveUrlForLocalOrAbsolute(String targetPath,
1151           URL localref)
1152   {
1153     String resolvedPath = "";
1154     if (targetPath.startsWith("/"))
1155     {
1156       String codebase = localref.toString();
1157       String localfile = localref.getFile();
1158       resolvedPath = codebase.substring(0,
1159               codebase.length() - localfile.length()) + targetPath;
1160       return resolvedPath;
1161     }
1162
1163     /*
1164      * get URL path and strip off any trailing file e.g.
1165      * www.jalview.org/examples/index.html#applets?a=b is trimmed to
1166      * www.jalview.org/examples/
1167      */
1168     String urlPath = localref.toString();
1169     String directoryPath = urlPath;
1170     int lastSeparator = directoryPath.lastIndexOf("/");
1171     if (lastSeparator > 0)
1172     {
1173       directoryPath = directoryPath.substring(0, lastSeparator + 1);
1174     }
1175
1176     if (targetPath.startsWith("/"))
1177     {
1178       /*
1179        * construct absolute URL to a file on the server - this is not allowed?
1180        */
1181       // String localfile = localref.getFile();
1182       // resolvedPath = urlPath.substring(0,
1183       // urlPath.length() - localfile.length())
1184       // + targetPath;
1185       resolvedPath = directoryPath + targetPath.substring(1);
1186     }
1187     else
1188     {
1189       resolvedPath = directoryPath + targetPath;
1190     }
1191     // if (debug)
1192     // {
1193     // System.err.println(
1194     // "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
1195     // }
1196     return resolvedPath;
1197   }
1198
1199   /**
1200    * parse the string into a list
1201    * 
1202    * @param list
1203    * @param separator
1204    * @return elements separated by separator
1205    */
1206   private static String[] separatorListToArray(String list,
1207           String separator)
1208   {
1209     // TODO use StringUtils version (slightly different...)
1210     int seplen = separator.length();
1211     if (list == null || list.equals("") || list.equals(separator))
1212     {
1213       return null;
1214     }
1215     Vector<String> jv = new Vector<>();
1216     int cp = 0, pos;
1217     while ((pos = list.indexOf(separator, cp)) > cp)
1218     {
1219       jv.addElement(list.substring(cp, pos));
1220       cp = pos + seplen;
1221     }
1222     if (cp < list.length())
1223     {
1224       String c = list.substring(cp);
1225       if (!c.equals(separator))
1226       {
1227         jv.addElement(c);
1228       }
1229     }
1230     if (jv.size() > 0)
1231     {
1232       String[] v = new String[jv.size()];
1233       for (int i = 0; i < v.length; i++)
1234       {
1235         v[i] = jv.elementAt(i);
1236       }
1237       jv.removeAllElements();
1238       return v;
1239     }
1240     return null;
1241   }
1242
1243   /**
1244    * Discovers whether the given file is in the Applet Archive
1245    * 
1246    * @param f
1247    *          String
1248    * @return boolean
1249    */
1250   private static boolean inArchive(Class<?> c, String f)
1251   {
1252     // This might throw a security exception in certain browsers
1253     // Netscape Communicator for instance.
1254     try
1255     {
1256       boolean rtn = (c.getResourceAsStream("/" + f) != null);
1257       return rtn;
1258     } catch (Exception ex)
1259     {
1260       System.out.println("Exception checking resources: " + f + " " + ex);
1261       return false;
1262     }
1263   }
1264
1265   /**
1266    * Allowing for a JavaScript function here.
1267    */
1268   void callInitCallback()
1269   {
1270     Object initjscallback = getParameterAsObject("oninit");
1271     if (initjscallback != null)
1272     {
1273       try
1274       {
1275         doSendCallback(initjscallback, new Object[0]);
1276       } catch (Exception e)
1277       {
1278         System.err.println("Exception when executing _oninit callback '"
1279                 + initjscallback + "'.");
1280         e.printStackTrace();
1281       }
1282     }
1283   }
1284
1285   /**
1286    * Pass the provided array prepended with Jalview.this
1287    * 
1288    * Appropriated from org.jmol.appletjs.Jmol
1289    * 
1290    * @param callback
1291    *          a window function or "alert"
1292    * @param data
1293    * @return String return from the callback method.
1294    */
1295   String doSendCallback(Object callback, Object[] data)
1296   {
1297     Jalview me = jalview;
1298
1299     if (me != null && callback != null)
1300     {
1301       /**
1302        * @j2sNative
1303        * 
1304        *            try{
1305        * 
1306        *            if (callback == "alert") { alert(data[0]); return ""; } var
1307        *            o; if (typeof callback == "function") { o = callback; } else
1308        *            { if (!callback)return; var tokens = callback.split("."); o
1309        *            = window[tokens[0]]; for (var i = 1; i < tokens.length; i++)
1310        *            o = o[tokens[i]]; } var a = [me]; for (var i = 0; i <
1311        *            data.length; i++) a.push(data[i] ? data[i].booleanValue &&
1312        *            (data[i] = data[i].booleanValue()) : data[i]); return
1313        *            o.apply(null,a) } catch (e) { System.out.println(callback +
1314        *            " failed " + e); }
1315        */
1316     }
1317     return "";
1318   }
1319
1320   /**
1321    * Initialize from Info.key/value pairs that match the old JalviewLite applet
1322    * parameters.
1323    * 
1324    * See http://www.jalview.org/old/v2_8/examples/appletParameters.html
1325    * 
1326    * Note that some of these parameters are handled as command-line arguments,
1327    * as determined in ArgsParser.
1328    * 
1329    * @param alf
1330    */
1331   private void initFromParams(AlignFrame alf)
1332   {
1333     String sep = getParameter("separator");
1334     if (sep != null && sep.length() > 0)
1335     {
1336       separator = sep;
1337     }
1338     initTree(alf);
1339     initScoreFile(alf);
1340     initFeatures(alf);
1341     initAnnotations(alf);
1342     initJnetFile(alf);
1343     initPdbFiles(alf);
1344   }
1345
1346   /**
1347    * Load annotations if specified by parameter. Returns true if loaded, else
1348    * false.
1349    * 
1350    * 
1351    * @param alignFrame
1352    * @return
1353    */
1354   private boolean initAnnotations(AlignFrame alf)
1355   {
1356
1357     String param = getParameter("annotations");
1358     if (param == null)
1359       return false;
1360     ret[0] = param;
1361     DataSourceType protocol = resolveFileProtocol(ret);
1362     param = ret[0];
1363     if (!new AnnotationFile().annotateAlignmentView(alf.getViewport(),
1364             param, protocol))
1365     {
1366       System.err.println("Annotations were not added from annotation file '"
1367               + param + "'");
1368       return false;
1369     }
1370     updateForAnnotations();
1371     return true;
1372   }
1373
1374   /**
1375    * Load features file and view settings as specified by parameters. Returns
1376    * true if features were loaded, else false.
1377    * 
1378    * @param
1379    * 
1380    * @param alignFrame
1381    * @return
1382    */
1383   private boolean initFeatures(AlignFrame alf)
1384   {
1385
1386     // ///////////////////////////
1387     // modify display of features
1388     // we do this before any features have been loaded, ensuring any hidden
1389     // groups are hidden when features first displayed
1390     //
1391     // hide specific groups
1392     //
1393     String param = getParameter("hidefeaturegroups");
1394     if (param != null)
1395     {
1396       setFeatureGroupState(separatorListToArray(param, separator),
1397               false, alf);
1398       // setFeatureGroupStateOn(newAlignFrame, param, false);
1399     }
1400     // show specific groups
1401     param = getParameter("showfeaturegroups");
1402     if (param != null)
1403     {
1404       setFeatureGroupState(separatorListToArray(param, separator),
1405               true, alf);
1406       // setFeatureGroupStateOn(newAlignFrame, param, true);
1407     }
1408     // and now load features
1409     param = getParameter("features");
1410     if (param == null)
1411     {
1412       return false;
1413     }
1414     if (!parseFeaturesFile(param, alf))
1415       return false;
1416     param = getParameter("showFeatureSettings");
1417     if (param != null && param.equalsIgnoreCase("true"))
1418     {
1419       alf.showFeatureSettingsUI();
1420     }
1421     return true;
1422   }
1423
1424   /**
1425    * Load in a Jnetfile if specified by parameter. Returns true if loaded, else
1426    * false.
1427    * 
1428    * @param alignFrame
1429    * @return
1430    */
1431   private boolean initJnetFile(AlignFrame alf)
1432   {
1433
1434     String param = getParameter("jnetfile");
1435     if (param == null)
1436     {
1437       // jnet became jpred around 2016
1438       param = getParameter("jpredfile");
1439     }
1440     if (param != null)
1441     {
1442       try
1443       {
1444         ret[0] = param;
1445         DataSourceType protocol = resolveFileProtocol(ret);
1446         JPredFile predictions = new JPredFile(ret[0], protocol);
1447         JnetAnnotationMaker.add_annotation(predictions,
1448                 alf.getViewport().getAlignment(), 0, false);
1449         // false == do not add sequence profile from concise output
1450         alf.getViewport().getAlignment().setupJPredAlignment();
1451         updateForAnnotations();
1452       } catch (Exception ex)
1453       {
1454         ex.printStackTrace();
1455         return false;
1456       }
1457     }
1458     return true;
1459   }
1460
1461   /**
1462    * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
1463    * else false.
1464    * 
1465    * @param loaderFrame
1466    * @return
1467    */
1468   private boolean initPdbFiles(AlignFrame alf)
1469   {
1470
1471     /*
1472      * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
1473      * related to JAL-434
1474      */
1475
1476     // not supported (as for JalviewLite)
1477     // boolean doAlign = false;//"true".equalsIgnoreCase("" +
1478     // getAppletParameter("alignpdbfiles", false));
1479     // setAlignPdbStructures(doAlign);
1480     /*
1481      * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
1482      * PDB|1GAQ|1GAQ|C">
1483      * 
1484      * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
1485      * 
1486      * <param name="PDBfile3" value="1q0o Q45135_9MICO">
1487      */
1488
1489     // Accumulate pdbs here if they are heading for the same view (if
1490     // alignPdbStructures is true)
1491     // ArrayList<Object[]> pdbs = new ArrayList<>();
1492     // init a lazy matcher if we're asked to
1493     boolean relaxed = "true"
1494             .equalsIgnoreCase(getParameter("relaxedidmatch"));
1495     jalview.analysis.SequenceIdMatcher matcher = relaxed
1496             ? new jalview.analysis.SequenceIdMatcher(
1497                     alf.getViewport().getAlignment().getSequencesArray())
1498             : null;
1499
1500     String param = getParameter("PDBFILE");
1501     int plast = (param == null ? 9 : 1);
1502     if (param == null && (param = getParameter("PDBFILE1")) == null)
1503     {
1504       return false;
1505     }
1506     for (int p = 1; p <= plast; p++)
1507     {
1508       if (p > 1)
1509       {
1510         param = getParameter("PDBFILE" + p);
1511         if (param == null)
1512           break;
1513       }
1514       PDBEntry pdb = new PDBEntry();
1515
1516       String seqstring;
1517       SequenceI[] seqs = null;
1518       String[] chains = null;
1519
1520       StringTokenizer st = new StringTokenizer(param, " ");
1521
1522       if (st.countTokens() < 2)
1523       {
1524         String sequence = getParameter("PDBSEQ");
1525         if (sequence != null)
1526         {
1527           seqs = new SequenceI[] { matcher == null
1528                   ? (Sequence) alf.getViewport().getAlignment()
1529                           .findName(sequence)
1530                   : matcher.findIdMatch(sequence) };
1531         }
1532
1533       }
1534       else
1535       {
1536         param = st.nextToken();
1537         List<SequenceI> tmp = new ArrayList<>();
1538         List<String> tmp2 = new ArrayList<>();
1539
1540         while (st.hasMoreTokens())
1541         {
1542           seqstring = st.nextToken();
1543           StringTokenizer st2 = new StringTokenizer(seqstring, "=");
1544           if (st2.countTokens() > 1)
1545           {
1546             // This is the chain
1547             tmp2.add(st2.nextToken());
1548             seqstring = st2.nextToken();
1549           }
1550           tmp.add(matcher == null
1551                   ? (Sequence) alf.getViewport().getAlignment()
1552                           .findName(seqstring)
1553                   : matcher.findIdMatch(seqstring));
1554         }
1555
1556         seqs = tmp.toArray(new SequenceI[tmp.size()]);
1557         if (tmp2.size() == tmp.size())
1558         {
1559           chains = tmp2.toArray(new String[tmp2.size()]);
1560         }
1561       }
1562       pdb.setId(param);
1563       ret[0] = param;
1564       DataSourceType protocol = resolveFileProtocol(ret);
1565       // TODO check JAL-357 for files in a jar (CLASSLOADER)
1566       pdb.setFile(ret[0]);
1567
1568       if (seqs != null)
1569       {
1570         for (int i = 0; i < seqs.length; i++)
1571         {
1572           if (seqs[i] != null)
1573           {
1574             ((Sequence) seqs[i]).addPDBId(pdb);
1575             StructureSelectionManager
1576                     .getStructureSelectionManager(
1577                             (StructureSelectionManagerProvider) this)
1578                     .registerPDBEntry(pdb);
1579           }
1580         }
1581
1582         // if (doAlign)
1583         // {
1584         // pdbs.add(new Object[] { pdb, seqs, chains, protocol });
1585         // }
1586         // else
1587         {
1588           StructureViewer.launchStructureViewer(
1589                   (alf == null ? getCurrentAlignFrame() : alf).alignPanel,
1590                   pdb, seqs);
1591         }
1592       }
1593     }
1594     //
1595     // if (doAlign && pdbs.size() > 0)
1596     // {
1597     // SequenceI[][] seqs = new SequenceI[pdbs.size()][];
1598     // PDBEntry[] pdb = new PDBEntry[pdbs.size()];
1599     // String[][] chains = new String[pdbs.size()][];
1600     // String[] protocols = new String[pdbs.size()];
1601     // for (int pdbsi = 0, pdbsiSize = pdbs
1602     // .size(); pdbsi < pdbsiSize; pdbsi++)
1603     // {
1604     // Object[] o = pdbs.get(pdbsi);
1605     // pdb[pdbsi] = (PDBEntry) o[0];
1606     // seqs[pdbsi] = (SequenceI[]) o[1];
1607     // chains[pdbsi] = (String[]) o[2];
1608     // protocols[pdbsi] = (String) o[3];
1609     // }
1610     //// alignedStructureView(pdb, seqs, chains, protocols);
1611     // result = true;
1612     // }
1613     return true;
1614   }
1615
1616   /**
1617    * Load a score file if specified by parameter. Returns true if file was
1618    * loaded, else false.
1619    * 
1620    * @param loaderFrame
1621    */
1622   private boolean initScoreFile(AlignFrame alf)
1623   {
1624
1625     String sScoreFile = getParameter("scoreFile");
1626     if (sScoreFile != null && !"".equals(sScoreFile))
1627     {
1628       try
1629       {
1630         if (loadScoreFile(sScoreFile, alf))
1631         {
1632           return true;
1633         }
1634         System.err.println(
1635                 "Failed to parse T-COFFEE parameter as a valid score file ('"
1636                         + sScoreFile + "')");
1637       } catch (Exception e)
1638       {
1639         System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
1640                 sScoreFile, e.getMessage());
1641       }
1642     }
1643     return false;
1644   }
1645
1646   /**
1647    * Load a tree for the alignment if specified by parameter. Returns true if a
1648    * tree was loaded, else false.
1649    * 
1650    * @return
1651    */
1652   private boolean initTree(AlignFrame alf)
1653   {
1654     String treeFile;
1655     if ((treeFile = getParameter("tree")) == null
1656             && (treeFile = getParameter("treefile")) == null)
1657       return false;
1658     if (alf == null)
1659       alf = getCurrentAlignFrame();
1660     try
1661     {
1662       ret[0] = treeFile;
1663       NewickFile nf = new NewickFile(treeFile, resolveFileProtocol(ret));
1664       nf.parse();
1665       if (nf.getTree() != null)
1666       {
1667         treeFile = ret[0];
1668         alf.getViewport()
1669                 .setCurrentTree(alf.showNewickTree(nf, treeFile).getTree());
1670         return true;
1671       }
1672     } catch (Exception ex)
1673     {
1674       ex.printStackTrace();
1675     }
1676     return false;
1677   }
1678
1679   private void updateForAnnotations()
1680   {
1681     getCurrentAlignFrame().updateForAnnotations();
1682   }
1683 }