1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2021-12-31 18:52:23 -0600 (Fri, 31 Dec 2021) $
4  * $Revision: 22287 $
5  *
6  * Copyright (C) 2003-2006  Miguel, Jmol Development, www.jmol.org
7  *
8  * Contact: jmol-developers@lists.sf.net
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 package org.jmol.script;
25 
26 import java.util.Arrays;
27 import java.util.Hashtable;
28 import java.util.Map;
29 
30 import org.jmol.api.Interface;
31 import org.jmol.api.JmolParallelProcessor;
32 import org.jmol.api.JmolScriptFunction;
33 import org.jmol.api.SymmetryInterface;
34 import org.jmol.atomdata.RadiusData;
35 import org.jmol.atomdata.RadiusData.EnumType;
36 import org.jmol.c.PAL;
37 import org.jmol.c.STR;
38 import org.jmol.c.VDW;
39 import org.jmol.i18n.GT;
40 import org.jmol.modelset.Atom;
41 import org.jmol.modelset.BondSet;
42 import org.jmol.modelset.Group;
43 import org.jmol.modelset.TickInfo;
44 import org.jmol.shape.MeshCollection;
45 import org.jmol.shape.Shape;
46 import org.jmol.thread.JmolThread;
47 import org.jmol.util.BSUtil;
48 import org.jmol.util.ColorEncoder;
49 import org.jmol.util.Edge;
50 import org.jmol.util.Elements;
51 import org.jmol.util.Escape;
52 import org.jmol.util.Font;
53 import org.jmol.util.Logger;
54 import org.jmol.util.Parser;
55 import org.jmol.util.SimpleUnitCell;
56 import org.jmol.util.Txt;
57 import org.jmol.viewer.ActionManager;
58 import org.jmol.viewer.FileManager;
59 import org.jmol.viewer.JC;
60 import org.jmol.viewer.ShapeManager;
61 import org.jmol.viewer.StateManager;
62 import org.jmol.viewer.TransformManager;
63 import org.jmol.viewer.Viewer;
64 
65 import javajs.util.A4;
66 import javajs.util.AU;
67 import javajs.util.BArray;
68 import javajs.util.BS;
69 import javajs.util.Base64;
70 import javajs.util.Lst;
71 import javajs.util.M3;
72 import javajs.util.M4;
73 import javajs.util.Measure;
74 import javajs.util.OC;
75 import javajs.util.P3;
76 import javajs.util.P4;
77 import javajs.util.PT;
78 import javajs.util.Quat;
79 import javajs.util.SB;
80 import javajs.util.T3;
81 import javajs.util.V3;
82 
83 public class ScriptEval extends ScriptExpr {
84 
85 
86   /*
87    * To make this a bit more manageable, I separated ScriptEvaluator into four parts:
88    *
89    *
90    * ScriptEval                -- entry point and script command code
91    *
92    *   extends ScriptExpr      -- expression parsing
93    *
94    *     extends ScriptParam   -- parameter parsing
95    *
96    *       extends ScriptError -- error handling
97    *
98    *   scriptext.CmdExt        -- optionally loaded, less-used commands
99    *   scriptext.IsoExt        -- optionally loaded, less-used commands
100    *   scriptext.MathExt       -- optionally loaded, less-used functions
101    *   scriptext.SmilesExt     -- optionally loaded methods for cmds and math
102    *
103    *
104    *
105    * This main class is subdivided into the following sections:
106    *
107    *  global fields
108    *
109    *
110    *
111    *
112    *  Bob Hanson, 2/27/2014
113    */
114 
115   /*
116    *
117    * The ScriptEvaluator class, the Viewer, the xxxxManagers, the Graphics3D
118    * rendering engine, the ModelSet and Shape classes, and the Adapter file
119    * reader classes form the core of the Jmol molecular visualization framework.
120    *
121    * An extension of this file is org.jmol.scriptext.ScriptExt .
122    *
123    * The ScriptEvaluator has just a few entry points, which you will find
124    * immediately following this comment. They include:
125    *
126    * public boolean compileScriptString(String script, boolean tQuiet)
127    *
128    * public boolean compileScriptFile(String filename, boolean tQuiet)
129    *
130    * public void evaluateCompiledScript(boolean isCmdLine_c_or_C_Option, boolean
131    * isCmdLine_C_Option, boolean historyDisabled, boolean listCommands)
132    *
133    * Essentially ANYTHING can be done using these three methods. A variety of
134    * other methods are available via Viewer, which is the the true portal to
135    * Jmol (via the JmolViewer interface) for application developers who want
136    * faster, more direct processing.
137    *
138    * A little Jmol history:
139    *
140    * General history notes can be found at our ConfChem paper, which can be
141    * found at
142    * http://chemapps.stolaf.edu/jmol/presentations/confchem2006/jmol-confchem
143    * .htm
144    *
145    * This ScriptEvaluator class was initially written by Michael (Miguel) Howard
146    * as Eval.java as an efficient means of reproducing the RasMol scripting
147    * language for Jmol. Key additions there included:
148    *
149    * - tokenization of commands via the Compiler class (now ScriptCompiler and
150    * ScriptCompilationTokenParser) - ScriptException error handling - a flexible
151    * yet structured command parameter syntax - implementations of RasMol
152    * secondary structure visualizations - isosurfaces, dots, labels, polyhedra,
153    * draw, stars, pmesh, more
154    *
155    * Other Miguel contributions include:
156    *
157    * - the structural bases of the Adapter, ModelSet, and ModelSetBio classes -
158    * creation of Manager classes - absolutely amazing raw pixel bitmap rendering
159    * code (org.jmol.g3d) - popup context menu - inline model loading
160    *
161    * Bob Hanson (St. Olaf College) found out about Jmol during the spring of
162    * 2004. After spending over a year working on developing online interactive
163    * documentation, he started actively writing code early in 2006. During the
164    * period 2006-2009 Bob completely reworked the script processor (and much of
165    * the rest of Jmol) to handle a much broader range of functionality. Notable
166    * improvements include:
167    *
168    * - display/hide commands - dipole, ellipsoid, geosurface, lcaoCartoon
169    * visualizations - quaternion and ramachandran commands - much expanded
170    * isosurface / draw commands - configuration, disorder, and biomolecule
171    * support - broadly 2D- and 3D-positionable echos - translateSelected and
172    * rotateSelected commands - getProperty command, providing access to more
173    * file information - data and write commands - writing of high-resolution
174    * JPG, PNG, and movie-sequence JPG - generalized export to Maya and PovRay
175    * formats
176    *
177    * - multiple file loading, including trajectories - minimization using the
178    * Universal Force Field (UFF) - atom/model deletion and addition - direct
179    * loading of properties such as partial charge or coordinates - several new
180    * file readers, including manifested zip file reading - default directory, CD
181    * command, and pop-up file open/save dialogs
182    *
183    * - "internal" molecular coordinate-based rotations - full support for
184    * crystallographic formats, including space groups, symmetry, unit cells, and
185    * fractional coordinates - support for point groups and molecular symmetry -
186    * navigation mode - antialiasing of display and imaging - save/restore/write
187    * exact Jmol state - JVXL file format for compressed rapid generation of
188    * isosurfaces
189    *
190    * - user-defined variables - addition of a Reverse Polish Notation (RPN)
191    * expression processor - extension of the RPN processor to user variables -
192    * user-defined functions - flow control commands if/else/endif, for, and
193    * while - JavaScript/Java-like brace syntax - key stroke-by-key stroke
194    * command syntax checking - integrated help command - user-definable popup
195    * menu - language switching
196    *
197    * - fully functional signed applet - applet-applet synchronization, including
198    * two-applet geoWall stereo rendering - JSON format for property delivery to
199    * JavaScript - jmolScriptWait, dual-threaded queued JavaScript scripting
200    * interface - extensive callback development - script editor panel (work in
201    * progress, June 2009)
202    *
203    * Several other people have contributed. Perhaps they will not be too shy to
204    * add their claim to victory here. Please add your contributions.
205    *
206    * - Jmol application (Egon Willighagen) - smiles support (Nico Vervelle) -
207    * readers (Rene Kanter, Egon, several others) - initial VRML export work
208    * (Nico Vervelle) - WebExport (Jonathan Gutow) - internationalization (Nico,
209    * Egon, Angel Herriez) - Jmol Wiki and user guide book (Angel Herriez)
210    *
211    * While this isn't necessarily the best place for such discussion, open
212    * source principles require proper credit given to those who have
213    * contributed. This core class seems to me a place to acknowledge this core
214    * work of the Jmol team.
215    *
216    * Bob Hanson, 6/2009 hansonr@stolaf.edu
217    */
218 
219 
220 
221   /////////////////// global fields ///////////////////
222 
223   private final static String saveList =
224       "bonds? context? coordinates? orientation? rotation? selection? state? structure?";
225 
226   private static int iProcess;
227 
228   public ShapeManager sm;
229 
230   public boolean isJS;
231 
232   private JmolThread scriptDelayThread, fileLoadThread;
233 
234   private boolean allowJSThreads = true;
235 
236   @Override
getAllowJSThreads()237   public boolean getAllowJSThreads() {
238     return allowJSThreads;
239   }
240 
setAllowJSThreads(boolean b)241   public void setAllowJSThreads(boolean b) {
242     allowJSThreads = b;
243   }
244 
245   private boolean isFuncReturn;
246 
247   // execution options:
248 
249   public boolean historyDisabled; // set by ScriptExt.evaluateParallel
250 
251   private boolean debugScript;
252   private boolean isCmdLine_C_Option;
253   private boolean isCmdLine_c_or_C_Option;
254   private boolean listCommands;
255 
256   public boolean tQuiet;
257 
doReport()258   public boolean doReport() {
259     return (!tQuiet && scriptLevel <= scriptReportingLevel);
260   }
261 
262   private boolean executionStopped;
263   private boolean executionPaused;
264   private boolean executionStepping;
265   private boolean executing;
266 
267   private long timeBeginExecution;
268   private long timeEndExecution;
269 
270   private boolean mustResumeEval; // see resumeEval
271 
272   //private static int evalID;
273 
274   private Thread currentThread;
275   public ScriptCompiler compiler;
276 
277   public SB outputBuffer;
278 
279   private String contextPath = "";
280   public String scriptFileName;
281   public String functionName;
282 
283   public boolean isStateScript;
284 
285   @Override
isStateScript()286   public boolean isStateScript() {
287     return isStateScript;
288   }
289 
290   public int scriptLevel;
291   public static final String CONTEXT_HOLD_QUEUE = "getEvalContextAndHoldQueue";
292   public static final String CONTEXT_DELAY = "delay";
293 
294   private static final long DELAY_INTERRUPT_MS = 1000000; // testing!
295 
296   private static final int EXEC_ASYNC = -1;
297   private static final int EXEC_ERR   = 1;
298   private static final int EXEC_OK    = 0;
299 
300   public static int commandHistoryLevelMax = 0;
301   private static int contextDepthMax = 100; // mutable using set scriptLevelMax
302   private static int scriptReportingLevel = 0;
303 
304   /**
305    * set a static variable, with checking for range
306    */
307   @Override
setStatic(int tok, int ival)308   public int setStatic(int tok, int ival) {
309     switch (tok) {
310     case T.contextdepthmax:
311       if (ival >= 10)
312         contextDepthMax = ival;
313       return contextDepthMax;
314     case T.historylevel:
315       if (ival >= 0)
316         commandHistoryLevelMax = ival;
317       return commandHistoryLevelMax;
318     case T.scriptreportinglevel:
319       if (ival >= 0)
320       scriptReportingLevel = ival;
321       return scriptReportingLevel;
322     }
323     return 0;
324   }
325 
326   // created by Compiler:
327 
328   public T[][] aatoken;
329   private short[] lineNumbers;
330   private int[][] lineIndices;
331 
332   private String script;
333   private String scriptExtensions;
334 
335   @Override
getScript()336   public String getScript() {
337     return script;
338   }
339 
340   // specific to current statement:
341 
342   public int pc; // program counter
343   public String thisCommand;
344   public String fullCommand;
345   private int lineEnd;
346   private int pcEnd;
347 
348   // for specific commmands:
349 
350   private boolean forceNoAddHydrogens;
351 
352   private boolean isEmbedded;
353 
354 
ScriptEval()355   public ScriptEval() {
356     // by reflection as well as directly
357     currentThread = Thread.currentThread();
358     //evalID++;
359   }
360 
361   @Override
setViewer(Viewer vwr)362   public ScriptEval setViewer(Viewer vwr) {
363     this.vwr = vwr;
364     this.compiler = (compiler == null ? (ScriptCompiler) vwr.compiler
365         : compiler);
366     isJS = vwr.isSingleThreaded;
367     return this;
368   }
369 
370   @Override
setCompiler()371   public void setCompiler() {
372     vwr.compiler = compiler = new ScriptCompiler(vwr);
373   }
374 
375   // //////////////// primary interfacing methods //////////////////
376 
377   /*
378    * see Viewer.evalStringWaitStatus for how these are implemented
379    */
380   @Override
compileScriptString(String script, boolean tQuiet)381   public boolean compileScriptString(String script, boolean tQuiet) {
382     clearState(tQuiet);
383     contextPath = "[script]";
384     return compileScript(null, script, debugScript);
385   }
386 
387   @Override
compileScriptFile(String filename, boolean tQuiet)388   public boolean compileScriptFile(String filename, boolean tQuiet) {
389     clearState(tQuiet);
390     contextPath = filename;
391     String script = getScriptFileInternal(filename, null, null, null);
392     return  (script != null && compileScript(filename, script, debugScript));
393   }
394 
395   @Override
evaluateCompiledScript(boolean isCmdLine_c_or_C_Option, boolean isCmdLine_C_Option, boolean historyDisabled, boolean listCommands, SB outputBuffer, boolean allowThreads)396   public void evaluateCompiledScript(boolean isCmdLine_c_or_C_Option,
397                                      boolean isCmdLine_C_Option,
398                                      boolean historyDisabled,
399                                      boolean listCommands, SB outputBuffer,
400                                      boolean allowThreads) {
401     boolean tempOpen = this.isCmdLine_C_Option;
402     this.isCmdLine_C_Option = isCmdLine_C_Option;
403     chk = this.isCmdLine_c_or_C_Option = isCmdLine_c_or_C_Option;
404     this.historyDisabled = historyDisabled;
405     this.outputBuffer = outputBuffer;
406     privateFuncs = null;
407     currentThread = Thread.currentThread();
408     setAllowJSThreads(allowThreads & !vwr.getBoolean(T.nodelay));
409     this.listCommands = listCommands;
410     timeBeginExecution = System.currentTimeMillis();
411     executionStopped = executionPaused = false;
412     executionStepping = false;
413     executing = true;
414     vwr.pushHoldRepaintWhy("runEval");
415     setScriptExtensions();
416     executeCommands(false, true);
417     this.isCmdLine_C_Option = tempOpen;
418     if(isStateScript)
419       ScriptManager.setStateScriptVersion(vwr, null); // set by compiler
420   }
421 
useThreads()422   public boolean useThreads() {
423     return (!chk && !vwr.headless && !vwr.autoExit
424         && vwr.haveDisplay && outputBuffer == null && allowJSThreads);
425   }
426 
executeCommands(boolean isTry, boolean reportCompletion)427   private int executeCommands(boolean isTry, boolean reportCompletion) {
428     boolean haveError = false;
429     try {
430       if (!dispatchCommands(false, false, isTry))
431         return EXEC_ASYNC;
432     } catch (Error er) {
433       vwr.handleError(er, false);
434       setErrorMessage("" + er + " " + vwr.getShapeErrorState());
435       errorMessageUntranslated = "" + er;
436       report(errorMessage, true);
437       haveError = true;
438     } catch (ScriptException e) {
439       if (e instanceof ScriptInterruption && (!isTry || !e.isError)) {
440 
441         // ScriptInterruption will be called in Java or JavaScript
442         // by a THROW command, but in that case e.isError == true
443 
444         // ScriptInterruption will be called in JavaScript to
445         // stop this thread and initiate a setTimeout sequence
446         // that is responsible for getting us back to the
447         // current point using resumeEval again.
448         // it's not a real exception, but it has that
449         // property so that it can be caught here.
450 
451         return EXEC_ASYNC;
452       }
453       if (isTry) {
454         vwr.setStringProperty("_errormessage", "" + e);
455         return EXEC_ERR;
456       }
457       setErrorMessage(e.toString());
458       errorMessageUntranslated = e.getErrorMessageUntranslated();
459       report(errorMessage, true);
460       vwr
461           .notifyError(
462               (errorMessage != null
463                   && errorMessage.indexOf("java.lang.OutOfMemoryError") >= 0 ? "Error"
464                   : "ScriptException"), errorMessage, errorMessageUntranslated);
465       haveError = true;
466     }
467     if (haveError || !isJS || !allowJSThreads) {
468       vwr.setTainted(true);
469       vwr.popHoldRepaint("executeCommands" + " "
470           + (scriptLevel > 0 ? JC.REPAINT_IGNORE : ""));
471     }
472     timeEndExecution = System.currentTimeMillis();
473     if (errorMessage == null && executionStopped)
474       setErrorMessage("execution interrupted");
475     else if (!tQuiet && reportCompletion)
476       vwr.scriptStatus(JC.SCRIPT_COMPLETED);
477     executing = chk = this.isCmdLine_c_or_C_Option = this.historyDisabled = false;
478     String msg = getErrorMessageUntranslated();
479     vwr.setErrorMessage(errorMessage, msg);
480     if (!tQuiet && reportCompletion)
481       vwr.setScriptStatus("Jmol script terminated", errorMessage,
482           1 + (int) (timeEndExecution - timeBeginExecution), msg);
483     return (haveError ? EXEC_ERR : EXEC_OK);
484   }
485 
486 
487   /**
488    * From dispatchCommands and JmolThread resumeEval.
489    *
490    * After throwing a ScriptInterruption, all statements following the current
491    * one are lost. When a JavaScript timeout returns from a DELAY, MOVE, MOVETO,
492    * or other sleep-requiring command, it is the ScriptContext that contains all
493    * have to worry about this, because the current thread is just put to sleep,
494    * not stopped, but in JavaScript, where we only have one thread, we need to
495    * manage this more carefully.
496    *
497    * We re-enter the halted script here, using a saved script context. The
498    * program counter is incremented to skip the initiating statement, and all
499    * parent contexts up the line are set with mustResumeEval = true.
500    *
501    * @param sco
502    */
503 
504   @Override
resumeEval(Object sco)505   public void resumeEval(Object sco) {
506 
507     ScriptContext sc = (ScriptContext) sco;
508 
509     //
510     //
511     //      |
512     //      |
513     //     INTERRUPT---
514     //     (1)         |
515     //      |          |
516     //      |          |
517     //      |      INTERRUPT----------------
518     //      |         (2)                   |
519     //      |          |                    |
520     //      |          |                    |
521     //      |     resumeEval-->(1)     MOVETO_INIT
522     //   (DONE)
523     //                                 (new thread)
524     //                                 MOVETO_FINISH
525     //                                      |
526     //                                 resumeEval-->(2)
527     //
528     //  In Java, this is one overall thread that sleeps
529     //  during the MOVETO. But in JavaScript, the setTimeout()
530     //  starts a new thread and (1) and (2) are never executed.
531     //  We must run resumeEval at the end of each dispatch loop.
532     //
533     //  Thus, it is very important that nothing is ever executed
534     //  after dispatchCommands.
535     //
536     //
537     //  Functions are tricky, though. How do we restart them?
538     //
539     //     main
540     //      |
541     //      ---------test()
542     //                 |
543     //                 |
544     //                 |
545     //                 -------------------zoomTo
546     //                (2)                   |
547     //                 |                    |
548     //      ---------return                 |
549     //      |                          MOVETO_INIT
550     //   (DONE)
551     //                                 (new thread)
552     //                                 MOVETO_FINISH
553     //                                      |
554     //                                 resumeEval-->(2)
555     //
556     // When the zoomTo is initiated, a ScriptInterrpt is thrown, which
557     // stops processing in test() and main.
558     //
559     //
560     setErrorMessage(null);
561     if (executionStopped || sc == null || !sc.mustResumeEval) {
562       resumeViewer("resumeEval");
563       return;
564     }
565     thisContext = sc;
566 
567     if (sc.scriptLevel > 0 && sc.why != CONTEXT_HOLD_QUEUE)
568       scriptLevel = sc.scriptLevel - 1;
569     if (sc.isTryCatch) {
570       postProcessTry(null);
571       pcResume = -1;
572     } else {
573       if (!executionPaused)
574         sc.pc++;
575       restoreScriptContext(sc, true, false, false);
576       pcResume = sc.pc;
577     }
578     switch (executeCommands(thisContext != null && thisContext.isTryCatch,
579         scriptLevel <= 0)) {
580     case EXEC_ASYNC:
581       break;
582     case EXEC_ERR:
583     case EXEC_OK:
584       postProcessTry(null);
585       if (executing)
586         executeCommands(true, false);
587       break;
588     }
589     pcResume = -1;
590   }
591 
resumeViewer(String why)592   private void resumeViewer(String why) {
593     vwr.setTainted(true);
594     vwr.popHoldRepaint(why + (chk ? JC.REPAINT_IGNORE : ""));
595     vwr.queueOnHold = false;
596   }
597 
598   @Override
runScript(String script)599   public void runScript(String script) throws ScriptException {
600     if (!vwr.isPreviewOnly)
601       runScriptBuffer(script, outputBuffer, false);
602   }
603 
604   /**
605    * runs a script immediately and sends selected output to a provided SB
606    * @param outputBuffer
607    * @param script
608    *
609    * @throws ScriptException
610    */
611   @Override
runScriptBuffer(String script, SB outputBuffer, boolean isFuncReturn)612   public void runScriptBuffer(String script, SB outputBuffer, boolean isFuncReturn)
613       throws ScriptException {
614     pushContext(null, "runScriptBuffer");
615     contextPath += " >> script() ";
616     this.outputBuffer = outputBuffer;
617     setAllowJSThreads(false);
618     boolean fret = this.isFuncReturn;
619     this.isFuncReturn |= isFuncReturn;
620     if (compileScript(null, script + JC.SCRIPT_EDITOR_IGNORE
621         + JC.REPAINT_IGNORE, false))
622       dispatchCommands(false, false, false);
623     popContext(false, false);
624     this.isFuncReturn = fret;
625   }
626 
627   /**
628    * a method for just checking a script
629    *
630    * @param script
631    * @return a ScriptContext that indicates errors and provides a tokenized
632    *         version of the script that has passed all syntax checking, both in
633    *         the compiler and the evaluator
634    *
635    */
636   @Override
checkScriptSilent(String script)637   public ScriptContext checkScriptSilent(String script) {
638     ScriptContext sc = compiler.compile(null, script, false, true, false, true);
639     if (sc.errorType != null)
640       return sc;
641     restoreScriptContext(sc, false, false, false);
642     chk = true;
643     isCmdLine_c_or_C_Option = isCmdLine_C_Option = false;
644     pc = 0;
645     try {
646       dispatchCommands(false, false, false);
647     } catch (ScriptException e) {
648       setErrorMessage(e.toString());
649       sc = getScriptContext("checkScriptSilent");
650     }
651     chk = false;
652     return sc;
653   }
654 
getContextTrace(Viewer vwr, ScriptContext sc, SB sb, boolean isTop)655   static SB getContextTrace(Viewer vwr, ScriptContext sc, SB sb,
656                             boolean isTop) {
657     if (sb == null)
658       sb = new SB();
659     int pc = Math.min(sc.pc, sc.lineNumbers[sc.lineNumbers.length - 1]);
660     sb.append(getErrorLineMessage(sc.functionName, sc.scriptFileName,
661         sc.lineNumbers[pc], pc, ScriptEval.statementAsString(vwr,
662             sc.statement, (isTop ? sc.iToken : 9999), false)));
663     if (sc.parentContext != null)
664       getContextTrace(vwr, sc.parentContext, sb, false);
665     return sb;
666   }
667 
668   // //////////////////////// script execution /////////////////////
669 
670   @Override
setDebugging()671   public void setDebugging() {
672     debugScript = vwr.getBoolean(T.debugscript);
673     debugHigh = (debugScript && Logger.debugging);
674   }
675 
676 
677   @Override
haltExecution()678   public void haltExecution() {
679     if (isEmbedded) {
680       vwr.setBooleanProperty("allowEmbeddedScripts", true);
681       isEmbedded = false;
682     }
683     resumePausedExecution();
684     executionStopped = true;
685   }
686 
687   @Override
pauseExecution(boolean withDelay)688   public void pauseExecution(boolean withDelay) {
689     if (chk || vwr.headless)
690       return;
691     if (withDelay && !isJS)
692       delayScript(-100);
693     vwr.popHoldRepaint("pauseExecution " + withDelay);
694     executionStepping = false;
695     executionPaused = true;
696   }
697 
698   @Override
stepPausedExecution()699   public void stepPausedExecution() {
700     executionStepping = true;
701     executionPaused = false;
702     // releases a paused thread but
703     // sets it to pause for the next command.
704   }
705 
706   @Override
resumePausedExecution()707   public void resumePausedExecution() {
708     executionPaused = false;
709     executionStepping = false;
710   }
711 
712   @Override
isExecuting()713   public boolean isExecuting() {
714     return executing && !executionStopped;
715   }
716 
717   @Override
isPaused()718   public boolean isPaused() {
719     return executionPaused;
720   }
721 
722   @Override
isStepping()723   public boolean isStepping() {
724     return executionStepping;
725   }
726 
727   @Override
isStopped()728   public boolean isStopped() {
729     return executionStopped || !isJS && currentThread != Thread.currentThread();
730   }
731 
732   /**
733    * when paused, indicates what statement will be next
734    *
735    * @return a string indicating the statement
736    */
737   @Override
getNextStatement()738   public String getNextStatement() {
739     return (pc < aatoken.length ? getErrorLineMessage(functionName,
740         scriptFileName, getLinenumber(null), pc,
741         statementAsString(vwr, aatoken[pc], -9999, debugHigh)) : "");
742   }
743 
744   /**
745    * used for recall of commands in the application console
746    *
747    * @param pc
748    * @param allThisLine
749    * @param addSemi
750    * @return a string representation of the command
751    */
getCommand(int pc, boolean allThisLine, boolean addSemi)752   private String getCommand(int pc, boolean allThisLine, boolean addSemi) {
753     if (pc >= lineIndices.length)
754       return "";
755     if (allThisLine) {
756       int pt0 = -1;
757       int pt1 = script.length();
758       for (int i = 0; i < lineNumbers.length; i++)
759         if (lineNumbers[i] == lineNumbers[pc]) {
760           if (pt0 < 0)
761             pt0 = lineIndices[i][0];
762           pt1 = lineIndices[i][1];
763         } else if (lineNumbers[i] == 0 || lineNumbers[i] > lineNumbers[pc]) {
764           break;
765         }
766       String s = script;
767       if (s.indexOf('\1') >= 0)
768         s = s.substring(0, s.indexOf('\1'));
769       if (pt1 == s.length() - 1 && s.endsWith("}"))
770         pt1++;
771       return (pt0 == s.length() || pt1 < pt0 ? "" : s.substring(
772           Math.max(pt0, 0), Math.min(s.length(), pt1)));
773     }
774     int ichBegin = lineIndices[pc][0];
775     int ichEnd = lineIndices[pc][1];
776     // (pc + 1 == lineIndices.length || lineIndices[pc + 1][0] == 0 ? script
777     // .length()
778     // : lineIndices[pc + 1]);
779     String s = "";
780     if (ichBegin < 0 || ichEnd <= ichBegin || ichEnd > script.length())
781       return "";
782     try {
783       s = script.substring(ichBegin, ichEnd);
784       if (s.indexOf("\\\n") >= 0)
785         s = PT.rep(s, "\\\n", "  ");
786       if (s.indexOf("\\\r") >= 0)
787         s = PT.rep(s, "\\\r", "  ");
788       // int i;
789       // for (i = s.length(); --i >= 0 && !ScriptCompiler.eol(s.charAt(i), 0);
790       // ){
791       // }
792       // s = s.substring(0, i + 1);
793       if (s.length() > 0 && !s.endsWith(";")/*
794                                              * && !s.endsWith("{") &&
795                                              * !s.endsWith("}")
796                                              */)
797         s += ";";
798     } catch (Exception e) {
799       Logger.error("darn problem in Eval getCommand: ichBegin=" + ichBegin
800           + " ichEnd=" + ichEnd + " len = " + script.length() + "\n" + e);
801     }
802     return s;
803   }
804 
logDebugScript(T[] st, int ifLevel)805   private void logDebugScript(T[] st, int ifLevel) {
806     iToken = -9999;
807     if (debugHigh) {
808       if (st.length > 0)
809         Logger.debug(st[0].toString());
810       for (int i = 1; i < st.length; ++i)
811         if (st[i] != null)
812           Logger.debug(st[i].toString());
813       SB strbufLog = new SB();
814       String s = (ifLevel > 0 ? "                          ".substring(0,
815           ifLevel * 2) : "");
816       strbufLog.append(s).append(
817           statementAsString(vwr, st, iToken, debugHigh));
818       vwr.scriptStatus(strbufLog.toString());
819     } else {
820       String cmd = getCommand(pc, false, false);
821       if (cmd != "")
822         vwr.scriptStatus(cmd);
823     }
824 
825   }
826 
827   // /////////////// string-based evaluation support /////////////////////
828 
829   /**
830    * a general-use method to evaluate a "SET" type expression.
831    * @param asVariable
832    * @param expr
833    *
834    * @return an object of one of the following types: Boolean, Integer, Float,
835    *         String, Point3f, BitSet
836    */
837 
838 
839   @Override
evaluateExpression(Object expr, boolean asVariable, boolean compileOnly)840   public Object evaluateExpression(Object expr, boolean asVariable, boolean compileOnly) {
841     // Text.formatText for MESSAGE and ECHO
842     // prior to 12.[2/3].32 was not thread-safe for compilation.
843     ScriptEval e = (new ScriptEval()).setViewer(vwr);
844     try {
845       // disallow end-of-script message and JavaScript script queuing
846       e.thisContext = thisContext;
847       e.contextVariables = contextVariables;
848       e.pushContext(null, "evalExp");
849       e.setAllowJSThreads(false);
850     } catch (ScriptException e1) {
851       //ignore
852     }
853     boolean exec0 = executing;
854     Object o = (e.evaluate(expr, asVariable, compileOnly));
855     executing = exec0;
856     return o;
857   }
858 
859 
860 
runBufferedSafely(String script, SB outputBuffer)861   public void runBufferedSafely(String script, SB outputBuffer) {
862     if (outputBuffer == null)
863       outputBuffer = this.outputBuffer;
864     ScriptEval e = (new ScriptEval()).setViewer(vwr);
865     boolean exec0 = executing;
866     try {
867       e.runScriptBuffer(script, outputBuffer, false);
868     } catch (ScriptException e1) {
869       e1.printStackTrace();
870       //ignore
871     }
872     executing = exec0;
873   }
874 
875 
876 
877 
runUserAction(String functionName, Object[] params, Viewer vwr)878   public static SV runUserAction(String functionName, Object[] params, Viewer vwr) {
879     ScriptEval ev = (new ScriptEval()).setViewer(vwr);
880     JmolScriptFunction func = vwr.getFunction(functionName.toLowerCase());
881     if (func == null)
882       return null;
883     try {
884       Lst<SV> svparams = SV.getVariableAO(params).getList();
885       ev.restoreFunction(func, svparams, null);
886       ev.dispatchCommands(false, true, false);
887     } catch (ScriptException e) {
888       return null;
889     }
890     SV ret = ev.getContextVariableAsVariable("_retval", false);
891     return (ret == null ? SV.vT : ret);
892   }
893 
evaluate(Object expr, boolean asVariable, boolean compileOnly)894   private Object evaluate(Object expr, boolean asVariable, boolean compileOnly) {
895     try {
896       if (expr instanceof String) {
897         if (compileScript(null, "e_x_p_r_e_s_s_i_o_n = " + expr, false)) {
898           if (compileOnly)
899             return aatoken[0];
900           setStatement(aatoken[0], 1);
901           return (asVariable ? parameterExpressionList(2, -1, false).get(0)
902               : parameterExpressionString(2, 0));
903         }
904       } else if (expr instanceof T[]) {
905         BS bs = atomExpression((T[]) expr, 0, 0, true, false, null, false);
906         return (asVariable ? SV.newV(T.bitset, bs) : bs);
907       } else if (expr instanceof T[][]) {
908         setStatement(((T[][])expr)[0], 1);
909         return (asVariable ? parameterExpressionList(0, -1, false).get(0)
910             : parameterExpressionString(0, -1));
911       }
912     } catch (Exception ex) {
913       Logger.error("Error evaluating: " + expr + "\n" + ex);
914     }
915     return (asVariable ? SV.getVariable("ERROR") : "ERROR");
916   }
917 
918   /**
919    * Check a map for a WHERE phrase
920    *
921    */
922   @Override
checkSelect(Map<String, SV> h, T[] where)923   public boolean checkSelect(Map<String, SV> h, T[] where) {
924     boolean ok = false;
925     try {
926       pushContext(null, "checkSelect");
927       ok = parameterExpressionSelect(h, where);
928     } catch (Exception ex) {
929       Logger.error("checkSelect " + ex);
930     }
931     popContext(false, false);
932     return ok;
933   }
934 
935   /**
936    * A general method to evaluate a string representing an atom set.
937    * Excepts one atom expression or one per line as "OR".
938    * Excepts "()" as "none".
939    *
940    *
941    * @param atomExpression
942    * @return is a bitset indicating the selected atoms
943    *
944    */
945   @Override
getAtomBitSet(Object atomExpression)946   public BS getAtomBitSet(Object atomExpression) {
947 
948     // called by ScriptExpr and ScriptManager
949 
950     if (atomExpression instanceof BS)
951       return (BS) atomExpression;
952     BS bs = new BS();
953     boolean executing = this.executing;
954     try {
955       pushContext(null, "getAtomBitSet");
956       String scr = "select (" + atomExpression + ")";
957       scr = PT.replaceAllCharacters(scr, "\n\r", "),(");
958       scr = PT.rep(scr, "()", "(none)");
959       if (compileScript(null, scr, false)) {
960         st = aatoken[0];
961         setStatement(st, 0);
962         bs = atomExpression(st, 1, 0, false, false, null, true);
963       }
964       popContext(false, false);
965     } catch (Exception ex) {
966       Logger.error("getAtomBitSet " + atomExpression + "\n" + ex);
967     }
968     this.executing = executing;
969     return bs;
970   }
971 
972 
973   // ////////////////////// supporting methods for compilation and loading
974 
compileScript(String filename, String strScript, boolean debugCompiler)975   public boolean compileScript(String filename, String strScript,
976                                boolean debugCompiler) {
977     scriptFileName = filename;
978     strScript = fixScriptPath(strScript, filename);
979     ScriptContext sc = compiler.compile(filename, strScript, false, false,
980         debugCompiler && Logger.debugging, false);
981     addFunction(null);
982     Map<String, ScriptFunction> pf = privateFuncs;
983     restoreScriptContext(sc, false, false, false);
984     privateFuncs = null;
985     if (thisContext != null)
986       thisContext.privateFuncs = pf;
987     isStateScript = compiler.isStateScript;
988     forceNoAddHydrogens = (isStateScript && script.indexOf("pdbAddHydrogens") < 0);
989     String s = script;
990     pc = setScriptExtensions();
991     if (!chk && vwr.scriptEditorVisible
992         && strScript.indexOf(JC.SCRIPT_EDITOR_IGNORE) < 0)
993       vwr.scriptStatus("");
994     script = s;
995     return !error;
996   }
997 
fixScriptPath(String strScript, String filename)998   private String fixScriptPath(String strScript, String filename) {
999     if (filename != null && strScript.indexOf("$SCRIPT_PATH$") >= 0) {
1000       String path = filename;
1001       // we first check for paths into ZIP files and adjust accordingly
1002       int pt = Math.max(filename.lastIndexOf("|"), filename.lastIndexOf("/"));
1003       path = path.substring(0, pt + 1);
1004       strScript = PT.rep(strScript, "$SCRIPT_PATH$/", path);
1005       // now replace the variable itself
1006       strScript = PT.rep(strScript, "$SCRIPT_PATH$", path);
1007     }
1008     return strScript;
1009   }
1010 
setScriptExtensions()1011   private int setScriptExtensions() {
1012     String extensions = scriptExtensions;
1013     if (extensions == null)
1014       return 0;
1015     int pt = extensions.indexOf("##SCRIPT_STEP");
1016     if (pt >= 0) {
1017       executionStepping = true;
1018     }
1019     pt = extensions.indexOf("##SCRIPT_START=");
1020     if (pt < 0)
1021       return 0;
1022     pt = PT.parseInt(extensions.substring(pt + 15));
1023     if (pt == Integer.MIN_VALUE)
1024       return 0;
1025     for (pc = 0; pc < lineIndices.length; pc++) {
1026       if (lineIndices[pc][0] > pt || lineIndices[pc][1] >= pt)
1027         break;
1028     }
1029     if (pc > 0 && pc < lineIndices.length && lineIndices[pc][0] > pt)
1030       --pc;
1031     return pc;
1032   }
1033 
1034   /**
1035    * Retrieve the uncompiled script or null if failed
1036    * @param filename
1037    * @param localPath
1038    * @param remotePath
1039    * @param scriptPath
1040    * @return  Jmol script or null
1041    */
getScriptFileInternal(String filename, String localPath, String remotePath, String scriptPath)1042   private String getScriptFileInternal(String filename, String localPath,
1043                                             String remotePath, String scriptPath) {
1044     // from "script" command, with push/pop surrounding or vwr
1045     if (filename.toLowerCase().indexOf("javascript:") == 0) {
1046       return vwr.jsEval(filename.substring(11));
1047     }
1048     String[] data = new String[2];
1049     data[0] = filename;
1050     if (!vwr.fm.getFileDataAsString(data, -1, false, true, false)) { // first opening
1051       setErrorMessage("io error reading " + data[0] + ": " + data[1]);
1052       return null;
1053     }
1054     String movieScript = "";
1055     if (("\n" + data[1]).indexOf("\nJmolManifest.txt\n") >= 0) {
1056       String path;
1057       if (filename.endsWith(".all.pngj") || filename.endsWith(".all.png")) {
1058         path = "|state.spt";
1059         filename += "|";
1060       } else {
1061         if (data[1].indexOf("movie.spt") >= 0) {
1062           data[0] = filename + "|movie.spt";
1063           if (vwr.fm.getFileDataAsString(data, -1, false, true, false)) { // movie.spt
1064             movieScript = data[1];
1065           }
1066         }
1067         filename += "|JmolManifest.txt";
1068         data[0] = filename;
1069         if (!vwr.fm.getFileDataAsString(data, -1, false, true, false)) { // second entry
1070           setErrorMessage("io error reading " + data[0] + ": " + data[1]);
1071           return null;
1072         }
1073         path = FileManager.getManifestScriptPath(data[1]);
1074       }
1075       if (path != null && path.length() > 0) {
1076         data[0] = filename = filename.substring(0, filename.lastIndexOf("|"))
1077             + path;
1078         if (!vwr.fm.getFileDataAsString(data, -1, false, true, false)) { // third entry
1079           setErrorMessage("io error reading " + data[0] + ": " + data[1]);
1080           return null;
1081         }
1082       }
1083       if (filename.endsWith("|state.spt")) {
1084         vwr.g.setO("_pngjFile", filename.substring(0, filename.length() - 10)
1085             + "?");
1086       }
1087     }
1088     scriptFileName = filename;
1089     data[1] = FileManager.getEmbeddedScript(data[1]);
1090     String script = fixScriptPath(data[1], data[0]);
1091     if (scriptPath == null) {
1092       scriptPath = vwr.fm.getFilePath(filename, false, false);
1093       scriptPath = scriptPath.substring(0,
1094           Math.max(scriptPath.lastIndexOf("|"), scriptPath.lastIndexOf("/")));
1095     }
1096     return FileManager.setScriptFileReferences(script, localPath, remotePath,
1097         scriptPath) + movieScript;
1098   }
1099 
1100   // ///////////// Jmol function support  // ///////////////
1101 
1102   private JmolParallelProcessor parallelProcessor;
1103 
1104   public int pcResume = -1;
1105 
1106   @Override
1107   @SuppressWarnings("unchecked")
evalFunctionFloat(Object func, Object params, float[] values)1108   public float evalFunctionFloat(Object func, Object params, float[] values) {
1109     try {
1110       Lst<SV> p = (Lst<SV>) params;
1111       for (int i = 0; i < values.length; i++)
1112         p.get(i).value = Float.valueOf(values[i]);
1113       ScriptFunction f = (ScriptFunction) func;
1114       return SV.fValue(runFunctionAndRet(f, f.name, p, null, true, false, false));
1115     } catch (Exception e) {
1116       return Float.NaN;
1117     }
1118 
1119   }
1120 
getUserFunctionResult(String name, Lst<SV> params, SV tokenAtom)1121   public SV getUserFunctionResult(String name, Lst<SV> params, SV tokenAtom)
1122       throws ScriptException {
1123 
1124     // called by ScriptExpr(getBitsetProperty) and ScriptExt(evaluateUserFunction)
1125     //
1126 
1127     return runFunctionAndRet(null, name, params, tokenAtom, true, true, false);
1128   }
1129 
runFunctionAndRet(JmolScriptFunction function, String name, Lst<SV> params, SV tokenAtom, boolean getReturn, boolean setContextPath, boolean allowThreads)1130   private SV runFunctionAndRet(JmolScriptFunction function, String name,
1131                            Lst<SV> params, SV tokenAtom, boolean getReturn,
1132                            boolean setContextPath, boolean allowThreads)
1133       throws ScriptException {
1134 
1135     // called by cmdFlow(TRY command), cmdFunc(user-defined command),
1136     // evalFunctionFloat (isosurface looking for a value), getFunctionRet
1137     // (above)
1138     //
1139     if (function == null) {
1140       // general function call
1141       name = name.toLowerCase();
1142       function = getFunction(name);
1143       if (function == null)
1144         return null;
1145       if (setContextPath)
1146         contextPath += " >> function " + name;
1147     } else if (setContextPath) {
1148       // "try"; not from evalFunctionFloat
1149       contextPath += " >> " + name;
1150     }
1151     pushContext(null, "runFunctionAndRet ");
1152     if (allowJSThreads)
1153       setAllowJSThreads(allowThreads);
1154     boolean isTry = (function.getTok() == T.trycmd);
1155     thisContext.isTryCatch = isTry;
1156     thisContext.isFunction = !isTry;
1157     functionName = name;
1158     if (isTry) {
1159       resetError();
1160       thisContext.displayLoadErrorsSave = vwr.displayLoadErrors;
1161       thisContext.tryPt = ++vwr.tryPt;
1162       vwr.displayLoadErrors = false;
1163       restoreFunction(function, params, tokenAtom);
1164       contextVariables.put("_breakval", SV.newI(Integer.MAX_VALUE));
1165       contextVariables.put("_errorval", SV.newS(""));
1166       Map<String, SV> cv = contextVariables;
1167       switch(executeCommands(true, false)) {
1168       case EXEC_ASYNC:
1169         // do this later
1170         break;
1171       case EXEC_ERR:
1172       case EXEC_OK:
1173       //JavaScript will not return here after DELAY
1174         postProcessTry(cv);
1175       }
1176       return null;
1177     } else if (function instanceof JmolParallelProcessor) {
1178       synchronized (function) // can't do this -- too general
1179       {
1180         parallelProcessor = (JmolParallelProcessor) function;
1181         restoreFunction(function, params, tokenAtom);
1182         dispatchCommands(false, true, false); // to load the processes
1183         ((JmolParallelProcessor) function).runAllProcesses(vwr);
1184       }
1185     } else {
1186       restoreFunction(function, params, tokenAtom);
1187       dispatchCommands(false, true, false);
1188       //JavaScript will not return here after DELAY or after what???
1189     }
1190     SV v = (getReturn ? getContextVariableAsVariable("_retval", false) : null);
1191     popContext(false, false);
1192     return v;
1193   }
1194 
postProcessTry(Map<String, SV> cv)1195   private void postProcessTry(Map<String, SV> cv) {
1196     if (thisContext == null)
1197       return;
1198     while (thisContext.tryPt > vwr.tryPt)
1199       popContext(false, false);
1200     boolean isJSReturn = (cv == null);
1201     if (isJSReturn) {
1202       cv = contextVariables;
1203     }
1204     vwr.displayLoadErrors = thisContext.displayLoadErrorsSave;
1205     popContext(false, false);
1206     String err = (String) vwr.getP("_errormessage");
1207     if (err.length() > 0) {
1208       cv.put("_errorval", SV.newS(err));
1209       resetError();
1210     }
1211     cv.put("_tryret", cv.get("_retval"));
1212     SV ret = cv.get("_tryret");
1213     if (ret.value != null || ret.intValue != Integer.MAX_VALUE) {
1214       try {
1215         cmdReturn(ret);
1216       } catch (ScriptException e) {
1217         e.printStackTrace();
1218       }
1219       return;
1220     }
1221     String errMsg = (String) (cv.get("_errorval")).value;
1222     if (errMsg.length() == 0) {
1223       int iBreak = (cv.get("_breakval")).intValue;
1224       if (iBreak != Integer.MAX_VALUE) {
1225         breakAt(pc - iBreak);
1226         return;
1227       }
1228     }
1229     // normal return will skip the catch
1230     if (pc + 1 < aatoken.length && aatoken[pc + 1][0].tok == T.catchcmd) {
1231       // set the intValue positive to indicate "not done" for the IF evaluation
1232       ContextToken ct = (ContextToken) aatoken[pc + 1][0];
1233       if (ct.contextVariables != null && ct.name0 != null)
1234         ct.contextVariables.put(ct.name0, SV.newS(errMsg));
1235       ct.intValue = (errMsg.length() > 0 ? 1 : -1) * Math.abs(ct.intValue);
1236     }
1237     if (isJSReturn)
1238       pc++;
1239   }
1240 
breakAt(int pt)1241   private void breakAt(int pt) {
1242     if (pt < 0) {
1243       // if pt is a backward reference
1244       // this is a break within a try{...} block
1245       getContextVariableAsVariable("_breakval", false).intValue = -pt;
1246       pcEnd = pc;
1247       return;
1248     }
1249     // pt is to the FOR, WHILE, or SWITCH statement that is being exited
1250     int ptEnd = Math.abs(aatoken[pt][0].intValue);
1251     int tok = aatoken[pt][0].tok;
1252     if (tok == T.casecmd || tok == T.defaultcmd) {
1253       // breaking from SWITCH
1254       theToken = aatoken[ptEnd--][0];
1255       int ptNext = Math.abs(theToken.intValue);
1256       if (theToken.tok != T.end)
1257         theToken.intValue = -ptNext;
1258     } else {
1259       // breaking from FOR or WHILE (or PROCESS?)
1260       pc = -1;
1261       while (pc != pt && thisContext != null) {
1262         while (thisContext != null
1263             && !ScriptCompiler.isBreakableContext(thisContext.token.tok))
1264           popContext(true, false);
1265         pc = thisContext.pc;
1266         popContext(true, false);
1267       }
1268     }
1269     pc = ptEnd;
1270   }
1271 
1272   /**
1273    * note that functions requiring motion cannot be run in JavaScript
1274    *
1275    * @param f
1276    * @param params
1277    * @param tokenAtom
1278    * @throws ScriptException
1279    */
restoreFunction(JmolScriptFunction f, Lst<SV> params, SV tokenAtom)1280   private void restoreFunction(JmolScriptFunction f, Lst<SV> params,
1281                                SV tokenAtom) throws ScriptException {
1282     ScriptFunction function = (ScriptFunction) f;
1283     aatoken = function.aatoken;
1284     lineNumbers = function.lineNumbers;
1285     lineIndices = function.lineIndices;
1286     script = function.script;
1287     pc = 0;
1288     if (function.names != null) {
1289       contextVariables = new Hashtable<String, SV>();
1290       function.setVariables(contextVariables, params);
1291     }
1292     if (tokenAtom != null)
1293       contextVariables.put("_x", tokenAtom);
1294   }
1295 
1296 
1297   ////////////////////////// defined atom sets ////////////////////////
1298 
clearDefinedVariableAtomSets()1299   public void clearDefinedVariableAtomSets() {
1300     vwr.definedAtomSets.remove("# variable");
1301   }
1302 
1303   /**
1304    * support for @xxx or define xxx commands
1305    *
1306    */
defineSets()1307   private void defineSets() {
1308     if (!vwr.definedAtomSets.containsKey("# static")) {
1309       for (int i = 0; i < JC.predefinedStatic.length; i++)
1310         defineAtomSet(JC.predefinedStatic[i]);
1311       defineAtomSet("# static");
1312     }
1313     if (vwr.definedAtomSets.containsKey("# variable"))
1314       return;
1315     for (int i = 0; i < JC.predefinedVariable.length; i++)
1316       defineAtomSet(JC.predefinedVariable[i]);
1317     // Now, define all the elements as predefined sets
1318 
1319     // name ==> elemno=n for all standard elements, isotope-blind
1320     // _Xx ==> elemno=n for of all elements, isotope-blind
1321     for (int i = Elements.elementNumberMax; --i >= 0;) {
1322       String definition = " elemno=" + i;
1323       defineAtomSet("@" + Elements.elementNameFromNumber(i) + definition);
1324       defineAtomSet("@_" + Elements.elementSymbolFromNumber(i) + definition);
1325     }
1326     // name ==> _e=nn for each alternative element
1327     for (int i = Elements.firstIsotope; --i >= 0;) {
1328       String definition = "@" + Elements.altElementNameFromIndex(i) + " _e="
1329           + Elements.altElementNumberFromIndex(i);
1330       defineAtomSet(definition);
1331     }
1332     // these variables _e, _x can't be more than two characters
1333     // name ==> _isotope=iinn for each isotope
1334     // _T ==> _isotope=iinn for each isotope
1335     // _3H ==> _isotope=iinn for each isotope
1336     for (int i = Elements.altElementMax; --i >= Elements.firstIsotope;) {
1337       int ei = Elements.altElementNumberFromIndex(i);
1338       String def = " _e=" + ei;
1339       String definition = "@_" + Elements.altElementSymbolFromIndex(i);
1340       defineAtomSet(definition + def);
1341 
1342       definition = "@_" + Elements.altIsotopeSymbolFromIndex(i);
1343       defineAtomSet(definition + def);
1344       definition = "@_" + Elements.altIsotopeSymbolFromIndex2(i);
1345       defineAtomSet(definition + def);
1346 
1347       definition = "@" + Elements.altElementNameFromIndex(i);
1348       if (definition.length() > 1)
1349         defineAtomSet(definition + def);
1350 
1351       // @_12C _e=6
1352       // @_C12 _e=6
1353       int e = Elements.getElementNumber(ei);
1354       ei = Elements.getNaturalIsotope(e);
1355       if (ei > 0) {
1356         def = Elements.elementSymbolFromNumber(e);
1357         defineAtomSet("@_" + def + ei + " _e=" + e);
1358         defineAtomSet("@_" + ei + def + " _e=" + e);
1359       }
1360     }
1361     defineAtomSet("# variable");
1362   }
1363 
defineAtomSet(String script)1364   private void defineAtomSet(String script) {
1365     if (script.indexOf("#") == 0) {
1366       vwr.definedAtomSets.put(script, Boolean.TRUE);
1367       return;
1368     }
1369     ScriptContext sc = compiler.compile("#predefine", script, true, false,
1370         false, false);
1371     if (sc.errorType != null) {
1372       vwr
1373           .scriptStatus("JmolConstants.java ERROR: predefined set compile error:"
1374               + script + "\ncompile error:" + sc.errorMessageUntranslated);
1375       return;
1376     }
1377 
1378     if (sc.getTokenCount() != 1) {
1379       vwr
1380           .scriptStatus("JmolConstants.java ERROR: predefinition does not have exactly 1 command:"
1381               + script);
1382       return;
1383     }
1384     T[] statement = sc.getToken(0);
1385     if (statement.length <= 2) {
1386       vwr.scriptStatus("JmolConstants.java ERROR: bad predefinition length:"
1387           + script);
1388       return;
1389     }
1390     int tok = statement[1].tok;
1391     if (!T.tokAttr(tok, T.identifier) && !T.tokAttr(tok, T.predefinedset)) {
1392       vwr.scriptStatus("JmolConstants.java ERROR: invalid variable name:"
1393           + script);
1394       return;
1395     }
1396     String name = ((String) statement[1].value).toLowerCase();
1397     if (name.startsWith("dynamic_"))
1398       name = "!" + name.substring(8);
1399     vwr.definedAtomSets.put(name, statement);
1400   }
1401 
lookupIdentifierValue(String identifier)1402   public BS lookupIdentifierValue(String identifier) throws ScriptException {
1403 
1404     // called by ScriptExpr and ScriptExt
1405 
1406     // all variables and possible residue names for PDB
1407     // or atom names for non-pdb atoms are processed here.
1408 
1409     // priority is given to a defined variable.
1410 
1411     BS bs = lookupValue(identifier, false);
1412     if (bs != null)
1413       return BSUtil.copy(bs);
1414 
1415     // next we look for names of groups (PDB) or atoms (non-PDB)
1416     bs = getAtomBits(T.identifier, identifier);
1417     return (bs == null ? new BS() : bs);
1418   }
1419 
lookupValue(String setName, boolean plurals)1420   private BS lookupValue(String setName, boolean plurals)
1421       throws ScriptException {
1422     if (chk) {
1423       return new BS();
1424     }
1425     defineSets();
1426     setName = setName.toLowerCase();
1427     Object value = vwr.definedAtomSets.get(setName);
1428     boolean isDynamic = false;
1429     if (value == null) {
1430       value = vwr.definedAtomSets.get("!" + setName);
1431       isDynamic = (value != null);
1432     }
1433     if (value instanceof BS)
1434       return (BS) value;
1435     if (value instanceof T[]) { // j2s OK -- any Array here
1436       pushContext(null, "lookupValue");
1437       BS bs = atomExpression((T[]) value, -2, 0, true, false, null, true);
1438       popContext(false, false);
1439       if (!isDynamic)
1440         vwr.definedAtomSets.put(setName, bs);
1441       return bs;
1442     }
1443     if (setName.equals("water")) {
1444       BS bs = vwr.ms.getAtoms(T.solvent, null);
1445       if (!isDynamic)
1446         vwr.definedAtomSets.put(setName, bs);
1447       return bs;
1448     }
1449     if (plurals)
1450       return null;
1451     int len = setName.length();
1452     if (len < 5) // iron is the shortest
1453       return null;
1454     if (setName.charAt(len - 1) != 's')
1455       return null;
1456     if (setName.endsWith("ies"))
1457       setName = setName.substring(0, len - 3) + 'y';
1458     else
1459       setName = setName.substring(0, len - 1);
1460     return lookupValue(setName, true);
1461   }
1462 
1463   @Override
deleteAtomsInVariables(BS bsDeleted)1464   public void deleteAtomsInVariables(BS bsDeleted) {
1465     for (Map.Entry<String, Object> entry : vwr.definedAtomSets.entrySet()) {
1466       Object value = entry.getValue();
1467       if (value instanceof BS) {
1468         BSUtil.deleteBits((BS) value, bsDeleted);
1469         if (!entry.getKey().startsWith("!"))
1470           vwr
1471               .g.setUserVariable("@" + entry.getKey(), SV.newV(T.bitset, value));
1472       }
1473     }
1474   }
1475 
1476 
1477   // ///////////////// Script context support //////////////////////
1478 
1479  @Override
getThisContext()1480   public ScriptContext getThisContext() {
1481     return thisContext;
1482   }
1483 
clearState(boolean tQuiet)1484   private void clearState(boolean tQuiet) {
1485     thisContext = null;
1486     scriptLevel = 0;
1487     setErrorMessage(null);
1488     contextPath = "";
1489     this.tQuiet = tQuiet;
1490   }
1491 
1492   @Override
pushContextDown(String why)1493   public void pushContextDown(String why) {
1494     scriptLevel--;
1495     pushContext2(null, why);
1496   }
1497 
pushContext(ContextToken token, String why)1498   private void pushContext(ContextToken token, String why)
1499       throws ScriptException {
1500     if (scriptLevel == contextDepthMax)
1501       error(ERROR_tooManyScriptLevels);
1502     pushContext2(token, why);
1503   }
1504 
pushContext2(ContextToken token, String why)1505   private void pushContext2(ContextToken token, String why) {
1506     thisContext = getScriptContext(why);
1507     thisContext.token = token;
1508     if (token == null) {
1509       scriptLevel = ++thisContext.scriptLevel;
1510     } else {
1511       thisContext.scriptLevel = -1;
1512       contextVariables = new Hashtable<String, SV>();
1513       if (token.contextVariables != null)
1514         for (String key : token.contextVariables.keySet())
1515           ScriptCompiler.addContextVariable(contextVariables, key);
1516     }
1517     if (debugHigh || isCmdLine_c_or_C_Option)
1518       Logger.info("-->>----------------------".substring(0,
1519           Math.min(15, scriptLevel + 5))
1520           + scriptLevel
1521           + " "
1522           + scriptFileName
1523           + " "
1524           + token
1525           + " "
1526           + thisContext.id + " " + why + " path=" + thisContext.contextPath);
1527   }
1528 
1529   @Override
getScriptContext(String why)1530   public ScriptContext getScriptContext(String why) {
1531     ScriptContext context = new ScriptContext();
1532     if (debugHigh)
1533       Logger.info("creating context " + context.id + " for " + why + " path=" + contextPath);
1534     context.why = why;
1535     context.scriptLevel = scriptLevel;
1536     context.parentContext = thisContext;
1537     context.contextPath = contextPath;
1538     context.scriptFileName = scriptFileName;
1539     context.parallelProcessor = parallelProcessor;
1540     context.functionName = functionName;
1541     context.script = script;
1542     context.lineNumbers = lineNumbers;
1543     context.lineIndices = lineIndices;
1544     context.saveTokens(aatoken);
1545     context.statement = st;
1546     context.statementLength = slen;
1547     context.pc = context.pc0 = pc;
1548     context.lineEnd = lineEnd;
1549     context.pcEnd = pcEnd;
1550     context.iToken = iToken;
1551     context.theToken = theToken;
1552     context.theTok = theTok;
1553     context.outputBuffer = outputBuffer;
1554     context.vars = contextVariables;
1555     context.isStateScript = isStateScript;
1556 
1557     context.errorMessage = errorMessage;
1558     context.errorType = errorType;
1559     context.iCommandError = iCommandError;
1560     context.chk = chk;
1561     context.executionStepping = executionStepping;
1562     context.executionPaused = executionPaused;
1563     context.scriptExtensions = scriptExtensions;
1564 
1565     context.mustResumeEval = mustResumeEval;
1566     context.allowJSThreads = allowJSThreads;
1567     return context;
1568   }
1569 
popContext(boolean isFlowCommand, boolean statementOnly)1570   void popContext(boolean isFlowCommand, boolean statementOnly) {
1571     if (thisContext == null)
1572       return;
1573     if (thisContext.scriptLevel > 0)
1574       scriptLevel = thisContext.scriptLevel - 1;
1575     // we must save (and thus NOT restore) the current statement
1576     // business when doing push/pop for commands like FOR and WHILE
1577     ScriptContext scTemp = (isFlowCommand ? getScriptContext("popFlow") : null);
1578     restoreScriptContext(thisContext, true, isFlowCommand, statementOnly);
1579     if (scTemp != null)
1580       restoreScriptContext(scTemp, true, false, true);
1581     if (debugHigh || isCmdLine_c_or_C_Option)
1582       Logger.info("--<<------------".substring(0,
1583           Math.min(15, scriptLevel + 5))
1584           + (scriptLevel + 1)
1585           + " "
1586           + scriptFileName
1587           + " isFlow "
1588           + isFlowCommand
1589           + " thisContext="
1590           + (thisContext == null ? "" : "" + thisContext.id)
1591           + " pc=" + pc + "-->" + pc + " path=" + (thisContext == null ? "" : thisContext.contextPath));
1592   }
1593 
restoreScriptContext(ScriptContext context, boolean isPopContext, boolean isFlowCommand, boolean statementOnly)1594   public void restoreScriptContext(ScriptContext context, boolean isPopContext,
1595                                    boolean isFlowCommand, boolean statementOnly) {
1596 
1597     executing = !chk;
1598     if (context == null)
1599       return;
1600     if (!isFlowCommand) {
1601       st = context.statement;
1602       slen = context.statementLength;
1603       pc = context.pc;
1604       lineEnd = context.lineEnd;
1605       pcEnd = context.pcEnd;
1606       if (statementOnly)
1607         return;
1608     }
1609     if (context.privateFuncs != null)
1610       privateFuncs = context.privateFuncs;
1611     mustResumeEval = context.mustResumeEval;
1612     script = context.script;
1613     lineNumbers = context.lineNumbers;
1614     lineIndices = context.lineIndices;
1615     aatoken = context.restoreTokens();
1616     contextVariables = context.vars;
1617     scriptExtensions = context.scriptExtensions;
1618 
1619     if (isPopContext) {
1620       contextPath = context.contextPath;
1621       int pt = (contextPath == null ? -1 : contextPath.indexOf(" >> "));
1622       if (pt >= 0)
1623         contextPath = contextPath.substring(0, pt);
1624       scriptFileName = context.scriptFileName;
1625       parallelProcessor = context.parallelProcessor;
1626       functionName = context.functionName;
1627       iToken = context.iToken;
1628       theToken = context.theToken;
1629       theTok = context.theTok;
1630 
1631       outputBuffer = context.outputBuffer;
1632       isStateScript = context.isStateScript;
1633       thisContext = context.parentContext;
1634       allowJSThreads = context.allowJSThreads;
1635       if (debugHigh || isCmdLine_c_or_C_Option)
1636         Logger.info("--r------------".substring(0,
1637             Math.min(15, scriptLevel + 5))
1638             + scriptLevel
1639             + " "
1640             + scriptFileName
1641             + " isPop "
1642             + isPopContext
1643             + " isFlow "
1644             + isFlowCommand
1645             + " context.id="
1646             + context.id + " pc=" + pc + "-->" + context.pc + " " + contextPath);
1647     } else {
1648       error = (context.errorType != null);
1649       //isComplete = context.isComplete;
1650       errorMessage = context.errorMessage;
1651       errorMessageUntranslated = context.errorMessageUntranslated;
1652       iCommandError = context.iCommandError;
1653       errorType = context.errorType;
1654     }
1655   }
1656 
1657   // /////////////// error message support /////////////////
1658 
setException(ScriptException sx, String msg, String untranslated)1659   public void setException(ScriptException sx, String msg, String untranslated) {
1660     // from ScriptException, while initializing
1661     sx.untranslated = (untranslated == null ? msg : untranslated);
1662     boolean isThrown = "!".equals(untranslated);
1663     errorType = msg;
1664     iCommandError = pc;
1665     if (sx.message == null) {
1666       sx.message = "";
1667       return;
1668     }
1669     String s = ScriptEval.getContextTrace(vwr,
1670         getScriptContext("setException"), null, true).toString();
1671     while (thisContext != null && !thisContext.isTryCatch)
1672       popContext(false, false);
1673     if (sx.message.indexOf(s) < 0) {
1674       sx.message += s;
1675       sx.untranslated += s;
1676     }
1677     resumeViewer(isThrown ? "throw context" : "scriptException");
1678     if (isThrown || thisContext != null || chk
1679         || msg.indexOf(JC.NOTE_SCRIPT_FILE) >= 0)
1680       return;
1681     Logger.error("eval ERROR: " + s + "\n" + toString());
1682     if (vwr.autoExit)
1683       vwr.exitJmol();
1684   }
1685 
1686   @SuppressWarnings("unchecked")
statementAsString(Viewer vwr, T[] statement, int iTok, boolean doLogMessages)1687   public static String statementAsString(Viewer vwr, T[] statement,
1688                                          int iTok, boolean doLogMessages) {
1689     if (statement.length == 0)
1690       return "";
1691     SB sb = new SB();
1692     int tok = statement[0].tok;
1693     switch (tok) {
1694     case T.nada:
1695       return (String) statement[0].value;
1696     case T.end:
1697       if (statement.length == 2
1698           && (statement[1].tok == T.function || statement[1].tok == T.parallel))
1699         return ((ScriptFunction) (statement[1].value)).toString();
1700     }
1701     boolean useBraces = true;// (!Token.tokAttr(tok,
1702     // Token.atomExpressionCommand));
1703     boolean inBrace = false;
1704     boolean inClauseDefine = false;
1705     boolean setEquals = (statement.length > 1 && tok == T.set
1706         && statement[0].value.equals("")
1707         && (statement[0].intValue == '=' || statement[0].intValue == '#') && statement[1].tok != T.expressionBegin);
1708     int len = statement.length;
1709     for (int i = 0; i < len; ++i) {
1710       T token = statement[i];
1711       if (token == null) {
1712         len = i;
1713         break;
1714       }
1715       if (iTok == i - 1)
1716         sb.append(" <<");
1717       if (i != 0)
1718         sb.appendC(' ');
1719       if (i == 2 && setEquals) {
1720         if ((setEquals = (token.tok != T.opEQ)) || statement[0].intValue == '#') {
1721           sb.append(setEquals ? "= " : "== ");
1722           if (!setEquals)
1723             continue;
1724         }
1725       }
1726       if (iTok == i && token.tok != T.expressionEnd)
1727         sb.append("<<<<");
1728       switch (token.tok) {
1729       case T.expressionBegin:
1730         if (useBraces)
1731           sb.append("{");
1732         continue;
1733       case T.expressionEnd:
1734         if (inClauseDefine && i == statement.length - 1)
1735           useBraces = false;
1736         if (useBraces)
1737           sb.append("}");
1738         continue;
1739       case T.leftsquare:
1740       case T.rightsquare:
1741         break;
1742       case T.leftbrace:
1743       case T.rightbrace:
1744         inBrace = (token.tok == T.leftbrace);
1745         break;
1746       case T.define:
1747         if (i > 0 && ((String) token.value).equals("define")) {
1748           sb.append("@");
1749           if (i + 1 < statement.length
1750               && statement[i + 1].tok == T.expressionBegin) {
1751             if (!useBraces)
1752               inClauseDefine = true;
1753             useBraces = true;
1754           }
1755           continue;
1756         }
1757         break;
1758       case T.on:
1759         sb.append("true");
1760         continue;
1761       case T.off:
1762         sb.append("false");
1763         continue;
1764       case T.select:
1765         break;
1766       case T.integer:
1767         sb.appendI(token.intValue);
1768         continue;
1769       case T.point3f:
1770       case T.point4f:
1771       case T.bitset:
1772         sb.append(SV.sValue(token)); // list
1773         continue;
1774       case T.hash:
1775          if (Boolean.TRUE == ((Map<String, Object>)token.value).get("$_BINARY_$")) {
1776            sb.append("<BINARY DATA>");
1777            continue;
1778          }
1779         //$FALL-THROUGH$
1780       case T.varray:
1781         sb.append(((SV) token).escape()); // list
1782         continue;
1783       case T.inscode:
1784         sb.appendC('^');
1785         continue;
1786       case T.spec_seqcode_range:
1787         if (token.intValue != Integer.MAX_VALUE)
1788           sb.appendI(token.intValue);
1789         else
1790           sb.append(Group.getSeqcodeStringFor(getSeqCode(token)));
1791         token = statement[++i];
1792         sb.appendC(' ');
1793         // if (token.intValue == Integer.MAX_VALUE)
1794         sb.append(inBrace ? "-" : "- ");
1795         //$FALL-THROUGH$
1796       case T.spec_seqcode:
1797         if (token.intValue != Integer.MAX_VALUE)
1798           sb.appendI(token.intValue);
1799         else
1800           sb.append(Group.getSeqcodeStringFor(getSeqCode(token)));
1801         continue;
1802       case T.spec_chain:
1803         sb.append("*:");
1804         sb.append(vwr.getChainIDStr(token.intValue));
1805         continue;
1806       case T.spec_alternate:
1807         sb.append("*%");
1808         if (token.value != null)
1809           sb.append(token.value.toString());
1810         continue;
1811       case T.spec_model:
1812         sb.append("*/");
1813         //$FALL-THROUGH$
1814       case T.spec_model2:
1815       case T.decimal:
1816         if (token.intValue < Integer.MAX_VALUE) {
1817           sb.append(Escape.escapeModelFileNumber(token.intValue));
1818         } else {
1819           sb.append("" + token.value);
1820         }
1821         continue;
1822       case T.spec_resid:
1823         sb.appendC('[');
1824         int ptr = token.intValue * 6 + 1;
1825         sb.append(Group.standardGroupList.substring(ptr, ptr + 3).trim());
1826         sb.appendC(']');
1827         continue;
1828       case T.spec_name_pattern:
1829         sb.appendC('[');
1830         sb.appendO(token.value);
1831         sb.appendC(']');
1832         continue;
1833       case T.spec_atom:
1834         sb.append("*.");
1835         break;
1836       case T.cell:
1837         if (token.value instanceof P3) {
1838           P3 pt = (P3) token.value;
1839           sb.append("cell=").append(Escape.eP(pt));
1840           continue;
1841         }
1842         break;
1843       case T.string:
1844         sb.append("\"").appendO(token.value).append("\"");
1845         continue;
1846       case T.opEQ:
1847       case T.opLE:
1848       case T.opGE:
1849       case T.opGT:
1850       case T.opLT:
1851       case T.opNE:
1852         // not quite right -- for "inmath"
1853         if (token.intValue == T.property) {
1854           sb.append((String) statement[++i].value).append(" ");
1855         } else if (token.intValue != Integer.MAX_VALUE)
1856           sb.append(T.nameOf(token.intValue)).append(" ");
1857         break;
1858       case T.trycmd:
1859         continue;
1860       case T.end:
1861         sb.append("end");
1862         continue;
1863       default:
1864         if (T.tokAttr(token.tok, T.identifier) || !doLogMessages)
1865           break;
1866         sb.appendC('\n').append(token.toString()).appendC('\n');
1867         continue;
1868       }
1869       if (token.value != null)
1870         sb.append(token.value.toString());
1871     }
1872 //    if (iTok >= len - 1 && iTok != 9999)
1873 //      sb.append(" <<");
1874     return sb.toString();
1875   }
1876 
1877   ///////////// shape get/set properties ////////////////
1878 
1879   /**
1880    * called by Viewer in setting up a PyMOL scene.
1881    */
1882   @Override
setObjectPropSafe(String id, int tokCommand)1883   public String setObjectPropSafe(String id, int tokCommand) {
1884     try {
1885       return setObjectProp(id, tokCommand, -1);
1886     } catch (ScriptException e) {
1887       return null;
1888     }
1889   }
1890 
setAtomProp(String prop, Object value, BS bs)1891   protected void setAtomProp(String prop, Object value, BS bs) {
1892     setShapePropertyBs(JC.SHAPE_BALLS, prop, value, bs);
1893   }
1894 
restrictSelected(boolean isBond, boolean doInvert)1895   public void restrictSelected(boolean isBond, boolean doInvert) {
1896 
1897     // called by ScriptParam
1898 
1899     if (!chk)
1900       sm.restrictSelected(isBond, doInvert);
1901   }
1902 
1903   //////////////////// showing strings /////////////////
1904 
showString(String str)1905   public void showString(String str) {
1906     // called by ScriptExt and ScriptError
1907     showStringPrint(str, false);
1908   }
1909 
1910   @Override
showStringPrint(String s, boolean mustDo)1911   public void showStringPrint(String s, boolean mustDo) {
1912     if (chk || s == null)
1913       return;
1914     if (outputBuffer == null)
1915       vwr.showString(s, mustDo);
1916     else
1917       appendBuffer(s, mustDo);
1918   }
1919 
report(String s, boolean isError)1920   public void report(String s, boolean isError) {
1921     if (chk || isError && s.indexOf(" of try:") >= 0)
1922       return;
1923     if (outputBuffer == null)
1924       vwr.scriptStatus(s);
1925     else
1926       appendBuffer(s, isError);
1927   }
1928 
appendBuffer(String str, boolean mustDo)1929   private void appendBuffer(String str, boolean mustDo) {
1930     if (mustDo || isFuncReturn || Logger.isActiveLevel(Logger.LEVEL_INFO))
1931       outputBuffer.append(str).appendC('\n');
1932   }
1933 
1934   /*
1935    * ****************************************************************
1936    * =============== command processing checks ===============================
1937    */
1938 
addProcess(Lst<T[]> vProcess, int pc, int pt)1939   private void addProcess(Lst<T[]> vProcess, int pc, int pt) {
1940     if (parallelProcessor == null)
1941       return;
1942     T[][] statements = new T[pt][];
1943     for (int i = 0; i < vProcess.size(); i++)
1944       statements[i + 1 - pc] = vProcess.get(i);
1945     ScriptContext context = getScriptContext("addProcess");
1946     context.saveTokens(statements);
1947     context.pc = 1 - pc;
1948     context.pcEnd = pt;
1949     parallelProcessor.addProcess("p" + (++iProcess), context);
1950   }
1951 
1952   /**
1953    * checks to see if there is a pause condition, during which commands can
1954    * still be issued, but with the ! first.
1955    *
1956    * @return false if there was a problem
1957    * @throws ScriptException
1958    */
checkContinue()1959   private boolean checkContinue() throws ScriptException {
1960     if (executionStopped)
1961       return false;
1962     if (executionStepping && isCommandDisplayable(pc)) {
1963       vwr.scriptStatusMsg("Next: " + getNextStatement(),
1964           "stepping -- type RESUME to continue");
1965       executionPaused = true;
1966     } else if (!executionPaused) {
1967       return true;
1968     }
1969     if (Logger.debugging) {
1970       Logger.debug("script execution paused at command " + (pc + 1) + " level "
1971           + scriptLevel + ": " + thisCommand);
1972     }
1973     refresh(false);
1974     while (executionPaused) {
1975       vwr.popHoldRepaint("pause " + JC.REPAINT_IGNORE);
1976       // does not actually do a repaint
1977       // but clears the way for interaction
1978       String script = vwr.getInsertedCommand();
1979       if (script.length() > 0) {
1980         resumePausedExecution();
1981         setErrorMessage(null);
1982         ScriptContext scSave = getScriptContext("script insertion");
1983         pc--; // in case there is an error, we point to the PAUSE command
1984         try {
1985           runScript(script);
1986         } catch (Exception e) {
1987           setErrorMessage("" + e);
1988         } catch (Error er) {
1989           setErrorMessage("" + er);
1990         }
1991         if (error) {
1992           report(errorMessage, true);
1993           setErrorMessage(null);
1994         }
1995         restoreScriptContext(scSave, true, false, false);
1996         pauseExecution(false);
1997       }
1998       doDelay(ScriptDelayThread.PAUSE_DELAY);
1999       // JavaScript will not reach this point,
2000       // but no need to pop anyway, because
2001       // we will be out of this thread.
2002       vwr.pushHoldRepaintWhy("pause");
2003     }
2004     notifyResumeStatus();
2005     // once more to trap quit during pause
2006     return !error && !executionStopped;
2007   }
2008 
delayScript(int millis)2009   public void delayScript(int millis) {
2010     if (vwr.autoExit)
2011       return;
2012     stopScriptThreads();
2013     if (vwr.captureParams != null && millis > 0) {
2014       vwr.captureParams.put("captureDelayMS", Integer.valueOf(millis));
2015     }
2016     scriptDelayThread = new ScriptDelayThread(this, vwr, millis);
2017     if (isJS && allowJSThreads) {
2018       // abort this; wait for delay to come back.
2019        pc = aatoken.length;
2020     }
2021     scriptDelayThread.run();
2022   }
2023 
2024   /**
2025    *
2026    * @param millis
2027    *        negative here bypasses max check
2028    * @throws ScriptException
2029    */
doDelay(int millis)2030   private void doDelay(int millis) throws ScriptException {
2031     if (!useThreads())
2032       return;
2033     if (isJS)
2034       throw new ScriptInterruption(this, CONTEXT_DELAY, millis);
2035     delayScript(millis);
2036   }
2037 
2038   @Override
evalParallel(ScriptContext context, ShapeManager shapeManager)2039   public boolean evalParallel(ScriptContext context,
2040                                   ShapeManager shapeManager) {
2041 
2042     // now in ScriptExt
2043 
2044     return getCmdExt().evalParallel(context, shapeManager);
2045   }
2046 
2047   /**
2048    * provides support for the script editor
2049    *
2050    * @param i
2051    * @return true if displayable
2052    */
isCommandDisplayable(int i)2053   private boolean isCommandDisplayable(int i) {
2054     if (i >= aatoken.length || i >= pcEnd || aatoken[i] == null)
2055       return false;
2056     return (lineIndices[i][1] > lineIndices[i][0]);
2057   }
2058 
2059   /**
2060    * load a static file asynchronously
2061    */
2062   @Override
loadFileResourceAsync(String fileName)2063   public void loadFileResourceAsync(String fileName) throws ScriptException {
2064     loadFileAsync(null, fileName, -Math.abs(fileName.hashCode()), false);
2065   }
2066 
2067   /**
2068    * Allows asynchronous file loading from the LOAD or SCRIPT command. Saves the
2069    * context, initiates a FileLoadThread instance. When the file loading
2070    * completes, the file data (sans filename) is saved in the FileManager cache
2071    * under cache://localLoad_xxxxx. Context is resumed at this command in the
2072    * script, and the file is then retrieved from the cache. Only run from
2073    * JSmol/HTML5 when vwr.isJS;
2074    *
2075    * Incompatibilities:
2076    *
2077    * LOAD and SCRIPT commands, load() function only;
2078    *
2079    * only one "?" per LOAD command
2080    *
2081    * @param prefix
2082    * @param filename
2083    *        or null if end of LOAD command and now just clearing out cache
2084    * @param i
2085    * @param doClear
2086    *        ensures only one file is in the cache for a given type
2087    * @return cached file name if it exists
2088    * @throws ScriptException
2089    */
loadFileAsync(String prefix, String filename, int i, boolean doClear)2090   public String loadFileAsync(String prefix, String filename, int i,
2091                               boolean doClear) throws ScriptException {
2092     // note that we will never know the actual file name
2093     // so we construct one and point to it in the scriptContext
2094     // with a key to this point in the script.
2095 
2096     if (vwr.fm.cacheGet(filename, false) != null) {
2097       cancelFileThread();
2098       return filename;
2099     }
2100     if (prefix != null)
2101       prefix = "cache://local" + prefix;
2102     String key = pc + "_" + i + "_" + filename;
2103     String cacheName;
2104     if (thisContext == null) {
2105       // add temp context
2106       pushContext(null, "loadFileAsync");
2107     }
2108     if (thisContext.htFileCache == null) {
2109       thisContext.htFileCache = new Hashtable<String, String>();
2110     }
2111     cacheName = thisContext.htFileCache.get(key);
2112     if (cacheName != null && cacheName.length() > 0) {
2113       cancelFileThread();
2114       //no, problems with isosurface "?" map "?": popContext(false, false);
2115       vwr.queueOnHold = false;
2116       if ("#CANCELED#".equals(cacheName) || "#CANCELED#".equals(vwr.fm.cacheGet(cacheName, false)))
2117         evalError("#CANCELED#", null);
2118       return cacheName;
2119     }
2120     thisContext.htFileCache.put(key,
2121         cacheName = prefix + System.currentTimeMillis());
2122 //    if (fileLoadThread != null && i >= 0)
2123 //      evalError("#CANCELED#", null);
2124     if (doClear)
2125       vwr.cacheFileByName(prefix + "*", false);
2126     fileLoadThread = new FileLoadThread(this, vwr, filename, key, cacheName);
2127     if (vwr.testAsync)
2128       fileLoadThread.start();
2129     else
2130       fileLoadThread.run();
2131     if (i < 0) // no need to hang on to this - never "canceled"
2132       fileLoadThread = null;
2133     throw new ScriptInterruption(this, "load", 1);
2134   }
2135 
cancelFileThread()2136   private void cancelFileThread() {
2137     // file has been loaded
2138     fileLoadThread = null;
2139     if (thisContext != null && thisContext.why == "loadFileAsync") {
2140       // remove temp context
2141       popContext(false, false);
2142     }
2143   }
2144 
2145   @SuppressWarnings("unchecked")
logLoadInfo(String msg, boolean isData)2146   private void logLoadInfo(String msg, boolean isData) {
2147     if (msg.length() > 0)
2148       Logger.info(msg);
2149     SB sb = new SB();
2150     int modelCount = vwr.ms.mc;
2151     if (modelCount > 1 && !isData)
2152       sb.append((vwr.am.isMovie ? vwr.am.getFrameCount() + " frames"
2153           : modelCount + " models") + "\n");
2154     for (int i = 0; i < modelCount; i++) {
2155       Map<String, Object> moData = (Map<String, Object>) vwr
2156           .ms.getInfo(i, "moData");
2157       if (moData == null || !moData.containsKey("mos"))
2158         continue;
2159       sb.appendI(((Lst<Map<String, Object>>) moData.get("mos")).size())
2160           .append(" molecular orbitals in model ")
2161           .append(vwr.getModelNumberDotted(i)).append("\n");
2162     }
2163     if (sb.length() > 0)
2164      vwr.showString(sb.toString(), false);
2165   }
2166 
2167   @Override
notifyResumeStatus()2168   public void notifyResumeStatus() {
2169     if (!chk && !executionStopped && !executionStepping && !executionPaused) {
2170       vwr.scriptStatus("script execution "
2171           + (error || executionStopped ? "interrupted" : "resumed"));
2172     }
2173     if (Logger.debugging)
2174       Logger.debug("script execution resumed");
2175   }
2176 
2177   /**
2178    * Refresh the display NOW
2179    * @param doDelay
2180    * @throws ScriptException
2181    *
2182    */
refresh(boolean doDelay)2183   public void refresh(boolean doDelay) throws ScriptException {
2184     if (chk)
2185       return;
2186     vwr.setTainted(true);
2187     vwr.requestRepaintAndWait("refresh cmd");
2188     if (isJS && doDelay)
2189       doDelay(10); // need this to update JavaScript display
2190   }
2191 
2192   @Override
stopScriptThreads()2193   public void stopScriptThreads() {
2194     if (scriptDelayThread != null) {
2195       scriptDelayThread.interrupt();
2196       scriptDelayThread = null;
2197     }
2198     if (fileLoadThread != null) {
2199       fileLoadThread.interrupt();
2200       fileLoadThread.resumeEval();
2201       cancelFileThread();
2202     }
2203   }
2204 
2205   // from ScriptExt
2206 
getErrorLineMessage2()2207   public String getErrorLineMessage2() {
2208     return getErrorLineMessage(functionName, scriptFileName,
2209         getLinenumber(null), pc,
2210         statementAsString(vwr, st, -9999, debugHigh));
2211   }
2212 
getLinenumber(ScriptContext c)2213   public int getLinenumber(ScriptContext c) {
2214     return (c == null ? lineNumbers[pc] : c.lineNumbers[c.pc]);
2215   }
2216 
2217 
2218   ///////////////////////////////////////////////////////////////////////
2219   ///////////////////////// Jmol script commands ////////////////////////
2220   ///////////////////////////////////////////////////////////////////////
2221 
2222   /**
2223    *
2224    * @param isSpt
2225    * @param fromFunc
2226    * @param isTry
2227    * @return false only when still working through resumeEval
2228    * @throws ScriptException
2229    */
dispatchCommands(boolean isSpt, boolean fromFunc, boolean isTry)2230   public boolean dispatchCommands(boolean isSpt, boolean fromFunc, boolean isTry)
2231       throws ScriptException {
2232     if (sm == null)
2233       sm = vwr.shm;
2234     debugScript = debugHigh = false;
2235     if (!chk)
2236       setDebugging();
2237     if (pcEnd == 0)
2238       pcEnd = Integer.MAX_VALUE;
2239     if (lineEnd == 0)
2240       lineEnd = Integer.MAX_VALUE;
2241     if (aatoken == null)
2242       return true;
2243     if (!tQuiet) {
2244       tQuiet = (vwr.getInt(T.showscript) < 0);
2245     }
2246     boolean allowJSInterrupt = (
2247         isJS
2248         && !fromFunc
2249         && useThreads()
2250         && vwr.getInt(T.showscript) >= 0);
2251     commandLoop(allowJSInterrupt);
2252     if (chk)
2253       return true;
2254     String script = vwr.getInsertedCommand();
2255     if (!"".equals(script))
2256       runScriptBuffer(script, null, false);
2257     else if (isSpt && debugScript && vwr.getBoolean(T.messagestylechime))
2258       vwr.getChimeMessenger().update(null);
2259     if (!mustResumeEval && !allowJSInterrupt || fromFunc)
2260       return true;
2261     if (!isTry && mustResumeEval || thisContext == null) {
2262       boolean done = (thisContext == null);
2263       resumeEval(thisContext);
2264       mustResumeEval = false;
2265       return done;
2266     }
2267     return true;
2268   }
2269 
commandLoop(boolean allowJSInterrupt)2270   private void commandLoop(boolean allowJSInterrupt) throws ScriptException {
2271     String lastCommand = "";
2272     boolean isForCheck = false; // indicates the stage of the for command loop
2273     Lst<T[]> vProcess = null;
2274     long lastTime = System.currentTimeMillis();
2275 
2276     if (debugScript && debugHigh && !chk) {
2277       for (int i = pc; i < aatoken.length && i < pcEnd; i++) {
2278         Logger.info("Command " + i);
2279         if (debugScript)
2280           logDebugScript(aatoken[i], 0);
2281       }
2282       Logger.info("-----");
2283     }
2284 
2285     for (; pc < aatoken.length && pc < pcEnd; pc++) {
2286       if (allowJSInterrupt) {
2287         // every 1 s check for interruptions
2288         if (!executionPaused && System.currentTimeMillis() - lastTime > DELAY_INTERRUPT_MS) {
2289           pc--;
2290           doDelay(-1);
2291         }
2292         lastTime = System.currentTimeMillis();
2293       }
2294       if (!chk && !checkContinue())
2295         break;
2296       if (pc >= lineNumbers.length || lineNumbers[pc] > lineEnd)
2297         break;
2298       if (debugHigh) {
2299         long timeBegin = 0;
2300         timeBegin = System.currentTimeMillis();
2301         vwr.scriptStatus("Eval.dispatchCommands():" + timeBegin);
2302         vwr.scriptStatus(script);
2303       }
2304 
2305       if (debugScript && !chk)
2306         Logger.info("Command " + pc
2307             + (thisContext == null ? "" : " path=" + thisContext.contextPath));
2308       theToken = (aatoken[pc].length == 0 ? null : aatoken[pc][0]);
2309       // when checking scripts, we can't check statments
2310       // containing @{...}
2311       if (!historyDisabled && !chk && scriptLevel <= commandHistoryLevelMax
2312           && !tQuiet) {
2313         String cmdLine = getCommand(pc, true, true);
2314         if (theToken != null
2315             && cmdLine.length() > 0
2316             && !cmdLine.equals(lastCommand)
2317             && (theToken.tok == T.function || theToken.tok == T.parallel || !T
2318                 .tokAttr(theToken.tok, T.flowCommand)))
2319           vwr.addCommand(lastCommand = cmdLine);
2320       }
2321       if (!chk && allowJSInterrupt) {
2322         String script = vwr.getInsertedCommand();
2323         if (!"".equals(script))
2324           runScript(script);
2325       }
2326       if (!setStatement(aatoken[pc], 1)) {
2327         Logger.info(getCommand(pc, true, false)
2328             + " -- STATEMENT CONTAINING @{} SKIPPED");
2329         continue;
2330       }
2331       thisCommand = getCommand(pc, false, true);
2332       if (debugHigh || debugScript)
2333         Logger.info(thisCommand);
2334       String nextCommand = getCommand(pc + 1, false, true);
2335       fullCommand = thisCommand
2336           + (nextCommand.startsWith("#") ? nextCommand : "");
2337       getToken(0);
2338       iToken = 0;
2339       if ((listCommands || !chk && scriptLevel > 0) && !isJS) {
2340         int milliSecDelay = vwr.getInt(T.showscript);
2341         if (listCommands || milliSecDelay > 0) {
2342           if (milliSecDelay > 0)
2343             delayScript(-milliSecDelay);
2344           vwr.scriptEcho("$[" + scriptLevel + "." + lineNumbers[pc] + "."
2345               + (pc + 1) + "] " + thisCommand);
2346         }
2347       }
2348       if (vProcess != null
2349           && (theTok != T.end || slen < 2 || st[1].tok != T.process)) {
2350         vProcess.addLast(st);
2351         continue;
2352       }
2353       if (chk) {
2354         if (isCmdLine_c_or_C_Option)
2355           Logger.info(thisCommand);
2356         if (slen == 1 && st[0].tok != T.function && st[0].tok != T.parallel)
2357           continue;
2358       } else {
2359         if (debugScript)
2360           logDebugScript(st, 0);
2361         if (scriptLevel == 0 && vwr.g.logCommands)
2362           vwr.log(thisCommand);
2363         if (debugHigh && theToken != null)
2364           Logger.debug(theToken.toString());
2365       }
2366       if (theToken == null)
2367         continue;
2368       int tok = theToken.tok;
2369       switch (tok) {
2370       case T.set:
2371         cmdSet();
2372         continue;
2373       case T.forcmd:
2374         isForCheck = cmdFor(tok, isForCheck);
2375         continue;
2376       case T.process:
2377         pushContext((ContextToken) theToken, "PROCESS");
2378         if (parallelProcessor != null)
2379           vProcess = new Lst<T[]>();
2380         continue;
2381       default:
2382         if (T.tokAttr(tok, T.flowCommand)) {
2383           isForCheck = cmdFlow(tok, isForCheck, vProcess);
2384           if (theTok == T.process)
2385             vProcess = null; // "end process"
2386           continue;
2387         }
2388         processCommand(tok);
2389         setCursorWait(false);
2390         if (executionStepping) {
2391           executionPaused = (isCommandDisplayable(pc + 1));
2392         }
2393       }
2394     }
2395   }
2396 
2397   //  public void terminateAfterStep() {
2398   //    pc = pcEnd;
2399   //  }
2400 
processCommand(int tok)2401   private void processCommand(int tok) throws ScriptException {
2402     if (T.tokAttr(theToken.tok, T.shapeCommand)) {
2403       processShapeCommand(tok);
2404       return;
2405     }
2406     switch (tok) {
2407     case T.nada:
2408       if (!chk && vwr.getBoolean(T.messagestylechime))
2409         vwr.getChimeMessenger().showHash(outputBuffer, (String) theToken.value);
2410       break;
2411     case T.push:
2412       pushContext((ContextToken) theToken, "PUSH");
2413       break;
2414     case T.pop:
2415       popContext(true, false);
2416       break;
2417     case T.colon:
2418       break;
2419     case T.animation:
2420       cmdAnimation();
2421       break;
2422     case T.background:
2423       cmdBackground(1);
2424       break;
2425     case T.bind:
2426       cmdBind();
2427       break;
2428     case T.bondorder:
2429       cmdBondorder();
2430       break;
2431     case T.cd:
2432       cmdCD();
2433       break;
2434     case T.center:
2435       cmdCenter(1);
2436       break;
2437     case T.color:
2438       cmdColor();
2439       break;
2440     case T.define:
2441       cmdDefine();
2442       break;
2443     case T.delay:
2444       cmdDelay();
2445       break;
2446     case T.delete:
2447       cmdDelete();
2448       break;
2449     case T.depth:
2450       cmdSlab(true);
2451       break;
2452     case T.display:
2453       cmdDisplay(true);
2454       break;
2455     case T.exit: // flush the queue and...
2456     case T.quit: // quit this only if it isn't the first command
2457       if (chk)
2458         break;
2459       if (pc > 0 && theToken.tok == T.exit && !vwr.autoExit) {
2460         vwr.clearScriptQueue();
2461       }
2462       executionStopped = (pc > 0 || !vwr.g.useScriptQueue);
2463       break;
2464     case T.exitjmol:
2465       if (chk)
2466         return;
2467       if (outputBuffer != null)
2468         Logger.warn(outputBuffer.toString());
2469       vwr.exitJmol();
2470       break;
2471     case T.file:
2472       cmdFile();
2473       break;
2474     case T.fixed:
2475       cmdFixed();
2476       break;
2477     case T.font:
2478       cmdFont(-1, 0);
2479       break;
2480     case T.frame:
2481     case T.model:
2482       cmdModel(1);
2483       break;
2484     case T.identifier:
2485       cmdFunc(); // when a function is a command
2486       break;
2487     case T.getproperty:
2488       cmdGetProperty();
2489       break;
2490     case T.gotocmd: //
2491       if (vwr.headless)
2492         break;
2493       cmdGoto(true);
2494       break;
2495     case T.help:
2496       cmdHelp();
2497       break;
2498     case T.hide:
2499       cmdDisplay(false);
2500       break;
2501     case T.hbond:
2502       cmdHbond();
2503       break;
2504     case T.history:
2505       cmdHistory(1);
2506       break;
2507     case T.hover:
2508       cmdHover();
2509       break;
2510     case T.initialize:
2511       switch (slen) {
2512       case 1:
2513         if (!chk)
2514           vwr.initialize(!isStateScript, false);
2515         break;
2516       case 2:
2517         if (tokAt(1) == T.inchi) {
2518           vwr.getInchi(null, null, null);
2519           if (chk) {
2520           } else {
2521             if (Viewer.isJS) {
2522               vwr.showString("InChI module initialized", false);
2523               doDelay(1);
2524             }
2525           }
2526           break;
2527         }
2528         //$FALL-THROUGH$
2529       default:
2530         bad();
2531       }
2532       break;
2533     case T.javascript:
2534       cmdScript(T.javascript, null, null);
2535       break;
2536     case T.load:
2537       cmdLoad();
2538       break;
2539     case T.log:
2540       cmdLog();
2541       break;
2542     case T.loop:
2543       cmdLoop();
2544       break;
2545     case T.message:
2546       cmdMessage();
2547       break;
2548     case T.move:
2549       cmdMove();
2550       break;
2551     case T.moveto:
2552       cmdMoveto();
2553       break;
2554     case T.pause: // resume is done differently
2555       cmdPause();
2556       break;
2557     case T.print:
2558       cmdPrint();
2559       break;
2560     case T.prompt:
2561       cmdPrompt();
2562       break;
2563     case T.redomove:
2564     case T.undomove:
2565       cmdUndoRedoMove();
2566       break;
2567     case T.refresh:
2568       refresh(true);
2569       break;
2570     case T.reset:
2571       cmdReset();
2572       break;
2573     case T.restrict:
2574       cmdRestrict();
2575       break;
2576     case T.resume:
2577       if (slen == 0) {
2578         if (!chk)
2579           resumePausedExecution();
2580         break;
2581       }
2582       //$FALL-THROUGH$
2583     case T.restore:
2584       cmdRestore();
2585       break;
2586     case T.returncmd:
2587       cmdReturn(null);
2588       break;
2589     case T.rotate:
2590       cmdRotate(false, false);
2591       break;
2592     case T.rotateSelected:
2593       cmdRotate(false, true);
2594       break;
2595     case T.save:
2596       cmdSave();
2597       break;
2598     case T.script:
2599       cmdScript(T.script, null, null);
2600       break;
2601     case T.select:
2602       cmdSelect(1);
2603       break;
2604     case T.selectionhalos:
2605       cmdSelectionHalos(1);
2606       break;
2607     case T.slab:
2608       cmdSlab(false);
2609       break;
2610     //case Token.slice:
2611     // slice();
2612     //break;
2613     case T.spin:
2614       cmdRotate(true, false);
2615       break;
2616     case T.ssbond:
2617       cmdSsbond();
2618       break;
2619     case T.step:
2620       if (cmdPause())
2621         stepPausedExecution();
2622       break;
2623     case T.structure:
2624       cmdStructure();
2625       break;
2626     case T.subset:
2627       cmdSubset();
2628       break;
2629     case T.sync:
2630       cmdSync();
2631       break;
2632     case T.throwcmd:
2633       cmdThrow();
2634       break;
2635     case T.timeout:
2636       cmdTimeout(1);
2637       break;
2638     case T.translate:
2639       cmdTranslate(false);
2640       break;
2641     case T.translateSelected:
2642       cmdTranslate(true);
2643       break;
2644     case T.unbind:
2645       cmdUnbind();
2646       break;
2647     case T.var:
2648       break;
2649     case T.vibration:
2650       cmdVibration();
2651       break;
2652     case T.zap:
2653       cmdZap(true);
2654       break;
2655     case T.zoom:
2656       cmdZoom(false);
2657       break;
2658     case T.zoomTo:
2659       cmdZoom(true);
2660       break;
2661     default:
2662       checkExtension(theToken.tok);
2663     }
2664   }
2665 
checkExtension(int tok)2666   private void checkExtension(int tok) throws ScriptException {
2667     switch (tok) {
2668     case T.assign:
2669     case T.cache:
2670     case T.calculate:
2671     case T.capture:
2672     case T.centerat:
2673     case T.compare:
2674     case T.configuration:
2675     case T.connect:
2676     case T.console:
2677     case T.hbond: // hbond connect
2678     case T.image:
2679     case T.invertSelected:
2680     case T.stereo:
2681     case T.macro:
2682     case T.mapproperty:
2683     case T.minimize:
2684     case T.modelkitmode:
2685     case T.modulation:
2686     case T.mutate:
2687     case T.data:
2688     case T.navigate:
2689     case T.plot:
2690     case T.quaternion:
2691     case T.ramachandran:
2692     case T.show:
2693     case T.write:
2694       getCmdExt().dispatch(tok, false, st);
2695       break;
2696     default:
2697       System.out.println(T.nameOf(tok) + " is not a command");
2698       error(ERROR_unrecognizedCommand);
2699     }
2700   }
2701 
processShapeCommand(int tok)2702   private void processShapeCommand(int tok) throws ScriptException {
2703     int iShape = 0;
2704     switch (tok) {
2705     case T.axes:
2706       iShape = JC.SHAPE_AXES;
2707       break;
2708     case T.backbone:
2709       iShape = JC.SHAPE_BACKBONE;
2710       break;
2711     case T.boundbox:
2712       iShape = JC.SHAPE_BBCAGE;
2713       break;
2714     case T.cartoon:
2715       iShape = JC.SHAPE_CARTOON;
2716       break;
2717     case T.cgo:
2718       iShape = JC.SHAPE_CGO;
2719       break;
2720     case T.contact:
2721       iShape = JC.SHAPE_CONTACT;
2722       break;
2723     case T.dipole:
2724       iShape = JC.SHAPE_DIPOLES;
2725       break;
2726     case T.dots:
2727       iShape = JC.SHAPE_DOTS;
2728       break;
2729     case T.draw:
2730       iShape = JC.SHAPE_DRAW;
2731       break;
2732     case T.echo:
2733       iShape = JC.SHAPE_ECHO;
2734       break;
2735     case T.ellipsoid:
2736       iShape = JC.SHAPE_ELLIPSOIDS;
2737       break;
2738     case T.frank:
2739       iShape = JC.SHAPE_FRANK;
2740       break;
2741     case T.geosurface:
2742       iShape = JC.SHAPE_GEOSURFACE;
2743       break;
2744     case T.halo:
2745       iShape = JC.SHAPE_HALOS;
2746       break;
2747     case T.isosurface:
2748       iShape = JC.SHAPE_ISOSURFACE;
2749       break;
2750     case T.label:
2751       iShape = JC.SHAPE_LABELS;
2752       break;
2753     case T.lcaocartoon:
2754       iShape = JC.SHAPE_LCAOCARTOON;
2755       break;
2756     case T.measurements:
2757     case T.measure:
2758       iShape = JC.SHAPE_MEASURES;
2759       break;
2760     case T.meshRibbon:
2761       iShape = JC.SHAPE_MESHRIBBON;
2762       break;
2763     case T.mo:
2764       iShape = JC.SHAPE_MO;
2765       break;
2766     case T.nbo:
2767       iShape = JC.SHAPE_NBO;
2768       break;
2769     case T.plot3d:
2770       iShape = JC.SHAPE_PLOT3D;
2771       break;
2772     case T.pmesh:
2773       iShape = JC.SHAPE_PMESH;
2774       break;
2775     case T.polyhedra:
2776       iShape = JC.SHAPE_POLYHEDRA;
2777       break;
2778     case T.ribbon:
2779       iShape = JC.SHAPE_RIBBONS;
2780       break;
2781     case T.rocket:
2782       iShape = JC.SHAPE_ROCKETS;
2783       break;
2784     case T.spacefill: // aka cpk
2785       iShape = JC.SHAPE_BALLS;
2786       break;
2787     case T.star:
2788       iShape = JC.SHAPE_STARS;
2789       break;
2790     case T.strands:
2791       iShape = JC.SHAPE_STRANDS;
2792       break;
2793     case T.struts:
2794       iShape = JC.SHAPE_STRUTS;
2795       break;
2796     case T.trace:
2797       iShape = JC.SHAPE_TRACE;
2798       break;
2799     case T.unitcell:
2800       iShape = JC.SHAPE_UCCAGE;
2801       break;
2802     case T.vector:
2803       iShape = JC.SHAPE_VECTORS;
2804       break;
2805     case T.wireframe:
2806       iShape = JC.SHAPE_STICKS;
2807       break;
2808     default:
2809       error(ERROR_unrecognizedCommand);
2810     }
2811 
2812     // check for "OFF/delete/NONE" with no shape to avoid loading it at all
2813     if (sm.getShape(iShape) == null && slen == 2) {
2814       switch (st[1].tok) {
2815       case T.off:
2816       case T.delete:
2817       case T.none:
2818         return;
2819       }
2820     }
2821 
2822     // atom objects:
2823 
2824     switch (tok) {
2825     case T.backbone:
2826     case T.cartoon:
2827     case T.meshRibbon:
2828     case T.ribbon:
2829     case T.rocket:
2830     case T.strands:
2831     case T.trace:
2832       setSizeBio(iShape);
2833       return;
2834     case T.dots:
2835     case T.geosurface:
2836       cmdDots(iShape);
2837       return;
2838     case T.halo:
2839     case T.spacefill: // aka cpk
2840     case T.star:
2841       setSize(iShape, (tok == T.halo ? -1000f : 1f));
2842       return;
2843     case T.label:
2844       cmdLabel(1, null);
2845       return;
2846     case T.vector:
2847       cmdVector();
2848       return;
2849     case T.wireframe:
2850       cmdWireframe();
2851       return;
2852     }
2853 
2854     // other objects:
2855 
2856     switch (tok) {
2857     case T.axes:
2858       cmdAxes(1);
2859       return;
2860     case T.boundbox:
2861       cmdBoundbox(1);
2862       return;
2863     case T.echo:
2864       cmdEcho(1);
2865       return;
2866     case T.frank:
2867       cmdFrank(1);
2868       return;
2869     case T.unitcell:
2870       cmdUnitcell(1);
2871       return;
2872     case T.ellipsoid:
2873     case T.measurements:
2874     case T.measure:
2875     case T.polyhedra:
2876     case T.struts:
2877       getCmdExt().dispatch(iShape, false, st);
2878       return;
2879     case T.cgo:
2880     case T.contact:
2881     case T.dipole:
2882     case T.draw:
2883     case T.isosurface:
2884     case T.lcaocartoon:
2885     case T.mo:
2886     case T.nbo:
2887     case T.plot3d:
2888     case T.pmesh:
2889       getIsoExt().dispatch(iShape, false, st);
2890       return;
2891     }
2892   }
2893 
cmdAnimation()2894   private void cmdAnimation() throws ScriptException {
2895     boolean animate = false;
2896     switch (getToken(1).tok) {
2897     case T.on:
2898       animate = true;
2899       //$FALL-THROUGH$
2900     case T.off:
2901       if (!chk)
2902         vwr.setAnimationOn(animate);
2903       break;
2904     case T.morph:
2905       int morphCount = (int) floatParameter(2);
2906       if (!chk)
2907         vwr.am.setMorphCount(Math.abs(morphCount));
2908       break;
2909     case T.display:
2910       iToken = 2;
2911       BS bs = (tokAt(2) == T.all ? null : atomExpressionAt(2));
2912       checkLength(iToken + 1);
2913       if (!chk)
2914         vwr.setAnimDisplay(bs);
2915       return;
2916     case T.frame:
2917       if (isArrayParameter(2))
2918         setFrameSet(2);
2919       else
2920         cmdModel(2);
2921       break;
2922     case T.mode:
2923       float startDelay = 1,
2924       endDelay = 1;
2925       if (slen > 5)
2926         bad();
2927       int animationMode = T.getTokFromName(paramAsStr(2));
2928       switch (animationMode) {
2929       case T.once:
2930         startDelay = endDelay = 0;
2931         break;
2932       case T.loop:
2933       case T.palindrome:
2934         break;
2935       default:
2936         invArg();
2937       }
2938       if (slen >= 4) {
2939         startDelay = endDelay = floatParameter(3);
2940         if (slen == 5)
2941           endDelay = floatParameter(4);
2942       }
2943       if (!chk)
2944         vwr.am.setAnimationReplayMode(animationMode, startDelay, endDelay);
2945       break;
2946     case T.direction:
2947       int i = 2;
2948       int direction = 0;
2949       switch (tokAt(i)) {
2950       case T.minus:
2951         direction = -intParameter(++i);
2952         break;
2953       case T.plus:
2954         direction = intParameter(++i);
2955         break;
2956       case T.integer:
2957         direction = intParameter(i);
2958         break;
2959       default:
2960         invArg();
2961       }
2962       checkLength(++i);
2963       if (direction != 1 && direction != -1)
2964         errorStr2(ERROR_numberMustBe, "-1", "1");
2965       if (!chk)
2966         vwr.am.setAnimationDirection(direction);
2967       break;
2968     case T.fps:
2969       setIntProperty("animationFps", intParameter(checkLast(2)));
2970       break;
2971     default:
2972       frameControl(1);
2973     }
2974   }
2975 
setFrameSet(int i)2976   private void setFrameSet(int i) throws ScriptException {
2977     int[] frames = (int[]) expandFloatArray(
2978         floatParameterSet(i, 0, Integer.MAX_VALUE), 1, false);
2979     checkLength(iToken + 1);
2980     if (chk)
2981       return;
2982     Map<String, Object> movie = new Hashtable<String, Object>();
2983     if (frames.length > 0)
2984       movie.put("frames", frames);
2985     movie.put("currentFrame", Integer.valueOf(0));
2986     vwr.am.setMovie(movie);
2987   }
2988 
cmdAxes(int index)2989   private void cmdAxes(int index) throws ScriptException {
2990     // axes (index==1) or set axes (index==2)
2991     TickInfo tickInfo = tickParamAsStr(index, true, true, false);
2992     index = iToken + 1;
2993     int tok = tokAt(index);
2994     String type = optParameterAsString(index).toLowerCase();
2995     if (slen == index + 1 && PT.isOneOf(type, ";window;unitcell;molecular;")) {
2996       setBooleanProperty("axes" + type, true);
2997       return;
2998     }
2999     switch (tok) {
3000     case T.offset:
3001       setFloatProperty("axisOffset", floatParameter(++index));
3002       checkLast(iToken);
3003       return;
3004     case T.center:
3005       setShapeProperty(JC.SHAPE_AXES, "origin",
3006           centerParameter(index + 1, null));
3007       checkLast(iToken);
3008       return;
3009     case T.type:
3010       String s = stringParameter(index + 1);
3011       if (!PT.isOneOf(s, ";a;b;c;ab;ac;bc;abc;"))
3012         s = null;
3013       setShapeProperty(JC.SHAPE_AXES, "type", s);
3014       checkLast(iToken);
3015       return;
3016     case T.scale:
3017       setFloatProperty("axesScale", floatParameter(checkLast(++index)));
3018       return;
3019     case T.label:
3020       switch (tok = tokAt(index + 1)) {
3021       case T.off:
3022       case T.on:
3023         checkLength(index + 2);
3024         setShapeProperty(JC.SHAPE_AXES,
3025             "labels" + (tok == T.on ? "On" : "Off"), null);
3026         return;
3027       }
3028       String sOrigin = null;
3029       switch (slen - index) {
3030       case 7:
3031         // axes labels "X" "Y" "Z" "-X" "-Y" "-Z"
3032         setShapeProperty(JC.SHAPE_AXES, "labels", new String[] {
3033             paramAsStr(++index), paramAsStr(++index), paramAsStr(++index),
3034             paramAsStr(++index), paramAsStr(++index), paramAsStr(++index) });
3035         break;
3036       case 5:
3037         sOrigin = paramAsStr(index + 4);
3038         //$FALL-THROUGH$
3039       case 4:
3040         // axes labels "X" "Y" "Z" [origin]
3041         setShapeProperty(JC.SHAPE_AXES, "labels", new String[] {
3042             paramAsStr(++index), paramAsStr(++index), paramAsStr(++index),
3043             sOrigin });
3044         break;
3045       default:
3046         bad();
3047       }
3048       return;
3049     }
3050     // axes position [x y %]
3051     if (type.equals("position")) {
3052       P3 xyp;
3053       if (tokAt(++index) == T.off) {
3054         xyp = new P3();
3055       } else {
3056         xyp = xypParameter(index);
3057         if (xyp == null)
3058           invArg();
3059         index = iToken;
3060       }
3061       setShapeProperty(JC.SHAPE_AXES, "position", xyp);
3062       return;
3063     }
3064     int mad10 = getSetAxesTypeMad10(index);
3065     if (chk || mad10 == Integer.MAX_VALUE)
3066       return;
3067     setObjectMad10(JC.SHAPE_AXES, "axes", mad10);
3068     if (tickInfo != null)
3069       setShapeProperty(JC.SHAPE_AXES, "tickInfo", tickInfo);
3070   }
3071 
cmdBackground(int i)3072   private void cmdBackground(int i) throws ScriptException {
3073     getToken(i);
3074     int argb;
3075     if (theTok == T.image) {
3076       // background IMAGE "xxxx.jpg"
3077       Object o = null;
3078       switch (tokAt(++i)) {
3079       case T.barray:
3080       case T.hash:
3081         o = getToken(i).value;
3082         break;
3083       default:
3084         String file = paramAsStr(checkLast(i));
3085         if (chk)
3086           return;
3087         if (file.equalsIgnoreCase("none") || file.length() == 0) {
3088           vwr.setBackgroundImage(null, null);
3089           return;
3090         }
3091         o = (file.startsWith(";base64,") ?  new BArray(Base64.decodeBase64(file)) : file);
3092       }
3093       if (vwr.fm.loadImage(o, null, !useThreads()))
3094           throw new ScriptInterruption(this,"backgroundImage", 1);
3095       return;
3096     }
3097     if (theTok == T.none || isColorParam(i)) {
3098       argb = getArgbParamLast(i, true);
3099       if (chk)
3100         return;
3101       setObjectArgb("background", argb);
3102       vwr.setBackgroundImage(null, null);
3103       return;
3104     }
3105     int iShape = getShapeType(theTok);
3106     colorShape(iShape, i + 1, true);
3107   }
3108 
cmdBind()3109   private void cmdBind() throws ScriptException {
3110     /*
3111      * bind "MOUSE-ACTION" actionName bind "MOUSE-ACTION" "script"
3112      *   not implemented: range [xyrange] [xyrange]
3113      */
3114     String mouseAction = stringParameter(1);
3115     String name = paramAsStr(2);
3116     checkLength(3);
3117     if (!chk)
3118       vwr.bindAction(mouseAction, name);
3119   }
3120 
cmdBondorder()3121   private void cmdBondorder() throws ScriptException {
3122     checkLength(-3);
3123     int order = 0;
3124     switch (getToken(1).tok) {
3125     case T.integer:
3126     case T.decimal:
3127       if ((order = Edge.getBondOrderFromFloat(floatParameter(1))) == Edge.BOND_ORDER_NULL)
3128         invArg();
3129       break;
3130     default:
3131       if ((order = getBondOrderFromString(paramAsStr(1))) == Edge.BOND_ORDER_NULL)
3132         invArg();
3133       // generic partial can be indicated by "partial n.m"
3134       if (order == Edge.BOND_PARTIAL01 && tokAt(2) == T.decimal) {
3135         order = getPartialBondOrderFromFloatEncodedInt(st[2].intValue);
3136       }
3137     }
3138     setShapeProperty(JC.SHAPE_STICKS, "bondOrder", Integer.valueOf(order));
3139   }
3140 
cmdBoundbox(int index)3141   private void cmdBoundbox(int index) throws ScriptException {
3142     TickInfo tickInfo = tickParamAsStr(index, false, true, false);
3143     index = iToken + 1;
3144     float scale = 1;
3145     if (tokAt(index) == T.scale) {
3146       scale = floatParameter(++index);
3147       if (!chk && scale == 0)
3148         invArg();
3149       index++;
3150       if (index == slen) {
3151         if (!chk)
3152           vwr.ms.setBoundBox(null, null, true, scale);
3153         return;
3154       }
3155     }
3156     boolean byCorner = (tokAt(index) == T.corners);
3157     if (byCorner)
3158       index++;
3159     if (isCenterParameter(index)) {
3160       Object[] ret = new Object[1];
3161       int index0 = index;
3162       P3 pt1 = centerParameter(index, ret);
3163       index = iToken + 1;
3164       if (byCorner || isCenterParameter(index)) {
3165         // boundbox CORNERS {expressionOrPoint1} {expressionOrPoint2}
3166         // boundbox {expressionOrPoint1} {vector}
3167         P3 pt2 = (byCorner ? centerParameter(index, ret) : getPoint3f(index, true, true));
3168         index = iToken + 1;
3169         if (!chk)
3170           vwr.ms.setBoundBox(pt1, pt2, byCorner, scale);
3171       } else if (ret[0] != null && ret[0] instanceof BS) {
3172         // boundbox {expression}
3173         if (!chk)
3174           vwr.calcBoundBoxDimensions((BS) ret[0], scale);
3175       } else if (ret[0] == null && tokAt(index0) == T.dollarsign) {
3176         if (chk)
3177           return;
3178         P3[] bbox = getObjectBoundingBox(objectNameParameter(++index0));
3179         if (bbox == null)
3180           invArg();
3181         vwr.ms.setBoundBox(bbox[0], bbox[1], true, scale);
3182         index = iToken + 1;
3183       } else {
3184         invArg();
3185       }
3186       if (index == slen)
3187         return;
3188     }
3189     int mad10 = getSetAxesTypeMad10(index);
3190     if (chk || mad10 == Integer.MAX_VALUE)
3191       return;
3192     if (tickInfo != null)
3193       setShapeProperty(JC.SHAPE_BBCAGE, "tickInfo", tickInfo);
3194     setObjectMad10(JC.SHAPE_BBCAGE, "boundbox", mad10);
3195   }
3196 
cmdCD()3197   private void cmdCD() throws ScriptException {
3198     if (chk)
3199       return;
3200     String dir = (slen == 1 ? null : paramAsStr(1));
3201     showString(vwr.cd(dir));
3202   }
3203 
cmdCenter(int i)3204   private void cmdCenter(int i) throws ScriptException {
3205     // from center (atom) or from zoomTo under conditions of not
3206     // windowCentered()
3207     if (slen == 1) {
3208       vwr.setNewRotationCenter(null);
3209       return;
3210     }
3211     P3 center = centerParameter(i, null);
3212     if (center == null)
3213       invArg();
3214     if (!chk)
3215       vwr.setNewRotationCenter(center);
3216     }
3217 
cmdColor()3218   private void cmdColor() throws ScriptException {
3219     int i = 1;
3220     String strColor = (tokAt(1) == T.string ? stringParameter(1) : null);
3221     if (isColorParam(1)) {
3222       theTok = T.atoms;
3223     } else {
3224       int argb = 0;
3225       i = 2;
3226       int tok = getToken(1).tok;
3227       if (tok == T.string) {
3228         tok = T.getTokFromName(strColor);
3229         if (tok == T.nada)
3230           tok = T.string;
3231       }
3232       switch (tok) {
3233       case T.dollarsign:
3234         setObjectProperty();
3235         return;
3236       case T.altloc:
3237       case T.amino:
3238       case T.nucleic:
3239       case T.chain:
3240       case T.fixedtemp:
3241       case T.formalcharge:
3242       case T.group:
3243       case T.hydrophobicity:
3244       case T.insertion:
3245       case T.jmol:
3246       case T.molecule:
3247       case T.monomer:
3248       case T.none:
3249       case T.opaque:
3250       case T.partialcharge:
3251       case T.polymer:
3252       case T.property:
3253       case T.rasmol:
3254       case T.pymol:
3255       case T.spacefill:
3256       case T.shapely:
3257       case T.straightness:
3258       case T.structure:
3259       case T.surfacedistance:
3260       case T.temperature:
3261       case T.translucent:
3262       case T.user:
3263       case T.vanderwaals:
3264         theTok = T.atoms;
3265         i = 1;
3266         break;
3267       case T.string:
3268         i = 2;
3269         if (isArrayParameter(i)) {
3270           strColor = strColor += "="
3271               + SV.sValue(SV.getVariableAS(stringParameterSet(i))).replace(
3272                   '\n', ' ');
3273           i = iToken + 1;
3274         }
3275         boolean isTranslucent = (tokAt(i) == T.translucent);
3276         if (!chk)
3277           vwr.setPropertyColorScheme(strColor, isTranslucent, true);
3278         if (isTranslucent)
3279           ++i;
3280         if (tokAt(i) == T.range || tokAt(i) == T.absolute) {
3281           float min = floatParameter(++i);
3282           float max = floatParameter(++i);
3283           if (!chk)
3284             vwr.cm.setPropertyColorRange(min, max);
3285         }
3286         return;
3287       case T.range:
3288       case T.absolute:
3289         float min = floatParameter(2);
3290         float max = floatParameter(checkLast(3));
3291         if (!chk)
3292           vwr.cm.setPropertyColorRange(min, max);
3293         return;
3294       case T.background:
3295         argb = getArgbParamLast(2, true);
3296         if (!chk)
3297           setObjectArgb("background", argb);
3298         return;
3299       case T.define:
3300       case T.bitset:
3301       case T.expressionBegin:
3302         i = -1;
3303         theTok = T.atoms;
3304         break;
3305       case T.rubberband:
3306         argb = getArgbParamLast(2, false);
3307         if (!chk)
3308           vwr.cm.setRubberbandArgb(argb);
3309         return;
3310       case T.highlight:
3311       case T.selectionhalos:
3312         i = 2;
3313         if (tokAt(2) == T.opaque)
3314           i++;
3315         argb = getArgbParamLast(i, true);
3316         if (chk)
3317           return;
3318         sm.loadShape(JC.SHAPE_HALOS);
3319         setShapeProperty(JC.SHAPE_HALOS,
3320             (tok == T.selectionhalos ? "argbSelection" : "argbHighlight"),
3321             Integer.valueOf(argb));
3322         return;
3323       case T.axes:
3324       case T.boundbox:
3325       case T.unitcell:
3326       case T.identifier:
3327       case T.hydrogen:
3328         // color element
3329         String str = paramAsStr(1);
3330         if (checkToken(2)) {
3331           argb = getToken(2).tok;
3332           switch (argb) {
3333           case T.none:
3334             argb = T.jmol;
3335             break;
3336           case T.jmol:
3337           case T.rasmol:
3338           case T.pymol:
3339             break;
3340           default:
3341             argb = getArgbParam(2);
3342           }
3343         }
3344         if (argb == 0)
3345           error(ERROR_colorOrPaletteRequired);
3346         checkLast(iToken);
3347         if (str.equalsIgnoreCase("axes")
3348             || StateManager.getObjectIdFromName(str) >= 0) {
3349           setObjectArgb(str, argb);
3350           return;
3351         }
3352         if (setElementColor(str, argb))
3353           return;
3354         invArg();
3355         break;
3356       case T.isosurface:
3357       case T.contact:
3358         setShapeProperty(JC.shapeTokenIndex(tok), "thisID",
3359             MeshCollection.PREVIOUS_MESH_ID);
3360         break;
3361       }
3362     }
3363     colorShape(getShapeType(theTok), i, false);
3364   }
3365 
cmdDefine()3366   private void cmdDefine() throws ScriptException {
3367     // note that the standard definition depends upon the
3368     // current state. Once defined, a setName is the set
3369     // of atoms that matches the definition at that time.
3370     // adding DYMAMIC_ to the beginning of the definition
3371     // allows one to create definitions that are recalculated
3372     // whenever they are used. When used, "DYNAMIC_" is dropped
3373     // so, for example:
3374     // define DYNAMIC_what selected and visible
3375     // and then
3376     // select what
3377     // will return different things at different times depending
3378     // upon what is selected and what is visible
3379     // but
3380     // define what selected and visible
3381     // will evaluate the moment it is defined and then represent
3382     // that set of atoms forever.
3383 
3384     if (slen < 3 || !(getToken(1).value instanceof String))
3385       invArg();
3386     String setName = ((String) getToken(1).value).toLowerCase();
3387     if (PT.parseInt(setName) != Integer.MIN_VALUE)
3388       invArg();
3389     if (chk)
3390       return;
3391     boolean isSite = setName.startsWith("site_");
3392     boolean isDynamic = (setName.indexOf("dynamic_") == 0);
3393     if (isDynamic || isSite) {
3394       T[] code = new T[slen];
3395       for (int i = slen; --i >= 0;)
3396         code[i] = st[i];
3397       vwr.definedAtomSets
3398           .put("!" + (isSite ? setName : setName.substring(8)), code);
3399       //if (!isSite)
3400       //vwr.addStateScript(thisCommand, false, true); removed for 12.1.16
3401     } else {
3402       BS bs = atomExpressionAt(2);
3403       vwr.definedAtomSets.put(setName, bs);
3404       if (!chk)
3405         vwr.g.setUserVariable("@" + setName, SV.newV(T.bitset, bs));
3406     }
3407   }
3408 
cmdDelay()3409   private void cmdDelay() throws ScriptException {
3410     int millis = 0;
3411     switch (getToken(1).tok) {
3412     case T.on: // this is auto-provided as a default
3413       millis = 1;
3414       break;
3415     case T.integer:
3416       millis = intParameter(1) * 1000;
3417       break;
3418     case T.decimal:
3419       millis = (int) (floatParameter(1) * 1000);
3420       break;
3421     default:
3422       error(ERROR_numberExpected);
3423     }
3424     refresh(false);
3425     doDelay(Math.abs(millis));
3426   }
3427 
cmdDelete()3428   private void cmdDelete() throws ScriptException {
3429     if (tokAt(1) == T.dollarsign) {
3430       if (slen == 4 && optParameterAsString(2).equals("saved") && slen == 4) {
3431         vwr.stm.deleteSaved(optParameterAsString(3));
3432         if (doReport())
3433           report(GT.o(GT.$("show saved: {0}"), vwr.stm.listSavedStates()), false);
3434         return;
3435       }
3436       setObjectProperty();
3437       return;
3438     }
3439     BS bs = (slen == 1 ? null : atomExpression(st, 1, 0, true, false, null,
3440         false));
3441     if (chk)
3442       return;
3443     if (bs == null)
3444       bs = vwr.getAllAtoms();
3445     int nDeleted = vwr.deleteAtoms(bs, false);
3446     if (doReport())
3447       report(GT.i(GT.$("{0} atoms deleted"), nDeleted), false);
3448   }
3449 
cmdDisplay(boolean isDisplay)3450   private void cmdDisplay(boolean isDisplay) throws ScriptException {
3451     BS bs = null;
3452     int addRemove = 0;
3453     int i = 1;
3454     int tok;
3455     switch (tok = tokAt(1)) {
3456     case T.add:
3457     case T.remove:
3458       addRemove = tok;
3459       tok = tokAt(++i);
3460       break;
3461     }
3462     boolean isGroup = (tok == T.group);
3463     if (isGroup)
3464       tok = tokAt(++i);
3465     switch (tok) {
3466     case T.dollarsign:
3467       setObjectProperty();
3468       return;
3469     case T.nada:
3470       break;
3471     default:
3472       if (slen == 4 && tokAt(2) == T.bonds)
3473         bs = BondSet.newBS(BSUtil.newBitSet2(0, vwr.ms.bondCount), null);
3474       else
3475         bs = atomExpressionAt(i);
3476     }
3477     if (chk)
3478       return;
3479     if (bs instanceof BondSet) {
3480       vwr.ms.displayBonds((BondSet) bs, isDisplay);
3481       return;
3482     }
3483     vwr.displayAtoms(bs, isDisplay, isGroup, addRemove, tQuiet);
3484   }
3485 
cmdDots(int iShape)3486   private void cmdDots(int iShape) throws ScriptException {
3487     if (!chk)
3488       sm.loadShape(iShape);
3489     setShapeProperty(iShape, "init", null);
3490     float value = Float.NaN;
3491     EnumType type = EnumType.ABSOLUTE;
3492     int ipt = 1;
3493     boolean isOnly = false;
3494     while (true) {
3495       switch (getToken(ipt).tok) {
3496       case T.only:
3497         isOnly = true;
3498         //$FALL-THROUGH$
3499       case T.on:
3500         value = 1;
3501         type = EnumType.FACTOR;
3502         break;
3503       case T.off:
3504         value = 0;
3505         break;
3506       case T.ignore:
3507         setShapeProperty(iShape, "ignore", atomExpressionAt(ipt + 1));
3508         ipt = iToken + 1;
3509         continue;
3510       case T.decimal:
3511         isOnly = (tokAt(ipt + 1) == T.only || floatParameter(ipt) < 0);
3512         break;
3513       case T.integer:
3514         int dotsParam = intParameter(ipt);
3515         if (tokAt(ipt + 1) == T.radius) {
3516           ipt++;
3517           setShapeProperty(iShape, "atom", Integer.valueOf(dotsParam));
3518           setShapeProperty(iShape, "radius",
3519               Float.valueOf(floatParameter(++ipt)));
3520           if (tokAt(++ipt) == T.color) {
3521             setShapeProperty(iShape, "colorRGB",
3522                 Integer.valueOf(getArgbParam(++ipt)));
3523             ipt++;
3524           }
3525           if (getToken(ipt).tok != T.bitset)
3526             invArg();
3527           setShapeProperty(iShape, "dots", st[ipt].value);
3528           return;
3529         }
3530         break;
3531       }
3532       break;
3533     }
3534     RadiusData rd = (Float.isNaN(value) ? encodeRadiusParameter(ipt, isOnly,
3535         true) : new RadiusData(null, value, type, VDW.AUTO));
3536     if (rd == null)
3537       return;
3538     if (Float.isNaN(rd.value))
3539       invArg();
3540     if (isOnly) {
3541       restrictSelected(false, false);
3542     }
3543     setShapeSize(iShape, rd);
3544   }
3545 
cmdEcho(int index)3546   private void cmdEcho(int index)
3547       throws ScriptException {
3548     if (chk)
3549       return;
3550     String text = optParameterAsString(index);
3551     boolean doRefresh = true;
3552     if (vwr.ms.getEchoStateActive()) {
3553       if (text.startsWith("\1")) {
3554         // no reporting, just screen echo, from mouseManager key press
3555         text = text.substring(1);
3556         doRefresh = false;
3557       }
3558       if (text != null)
3559         setShapeProperty(JC.SHAPE_ECHO, "text", text);
3560     }
3561     if (doRefresh && vwr.getRefreshing())
3562       showString(Txt.formatText(vwr, text));
3563   }
3564 
cmdFile()3565   private void cmdFile() throws ScriptException {
3566     int file = intParameter(checkLast(1));
3567     if (chk)
3568       return;
3569     int modelIndex = vwr.ms.getModelNumberIndex(file * 1000000 + 1, false,
3570         false);
3571     int modelIndex2 = -1;
3572     if (modelIndex >= 0) {
3573       modelIndex2 = vwr.ms.getModelNumberIndex((file + 1) * 1000000 + 1, false,
3574           false);
3575       if (modelIndex2 < 0)
3576         modelIndex2 = vwr.ms.mc;
3577       modelIndex2--;
3578     }
3579     vwr.setAnimationOn(false);
3580     vwr.am.setAnimationDirection(1);
3581     vwr.setAnimationRange(modelIndex, modelIndex2);
3582     vwr.setCurrentModelIndex(-1);
3583   }
3584 
cmdFixed()3585   private void cmdFixed() throws ScriptException {
3586     BS bs = (slen == 1 ? null : atomExpressionAt(1));
3587     if (chk)
3588       return;
3589     vwr.setMotionFixedAtoms(bs);
3590   }
3591 
3592   @SuppressWarnings("unchecked")
cmdFor(int tok, boolean isForCheck)3593   private boolean cmdFor(int tok, boolean isForCheck) throws ScriptException {
3594     ContextToken cmdToken = (ContextToken) theToken;
3595     int pt = st[0].intValue;
3596     SV[] forVars = cmdToken.forVars;
3597     int[] pts = new int[2];
3598     Object bsOrList = null;
3599     SV forVal = null;
3600     SV forVar = null;
3601     int inTok = 0;
3602     boolean isOK = true;
3603     boolean isMinusMinus = false;
3604     int j = 0;
3605     String key = null;
3606     if (isForCheck && forVars != null) {
3607 
3608       // for xx IN [...] or for xx FROM [...]
3609 
3610       tok = T.in;
3611       // i in x, already initialized
3612       forVar = forVars[0];
3613       forVal = forVars[1];
3614       bsOrList = forVars[1].value;
3615       // nth time through
3616       j = ++forVal.intValue;
3617       if (forVal.tok == T.integer) {
3618         // values are stored in value as [i1 i2]
3619         isMinusMinus = (j < 0);
3620         int i1 = ((int[]) bsOrList)[0];
3621         int i2 = ((int[]) bsOrList)[1];
3622         isOK = (i1 != i2 && (i2 < i1) == isMinusMinus);
3623         if (isOK)
3624           forVar.intValue = ((int[]) bsOrList)[0] = i1 + (isMinusMinus ? -1 : 1);
3625         j = -1;
3626       } else if (forVal.tok == T.varray) {
3627         isOK = (j <= ((Lst<SV>) bsOrList).size());
3628         if (isOK)
3629           forVar.setv(SV.selectItemVar(forVal));
3630         j = -1;
3631       } else {
3632         isBondSet = bsOrList instanceof BondSet;
3633         j = ((BS) bsOrList).nextSetBit(j);
3634         isOK = (j >= 0);
3635       }
3636     } else {
3637       // for (i = 1; i < 3; i = i + 1);
3638       // for (i = 1; i < 3; i = i + 1);
3639       // for (var i = 1; i < 3; i = i + 1);
3640       // for (;;;);
3641       // for (var x in {...}) { xxxxx }
3642       // for (var x in y) { xxxx }
3643       boolean isLocal = false;
3644       for (int i = 1, nSkip = 0; i < slen && j < 2; i++) {
3645         switch (tok = tokAt(i)) {
3646         case T.var:
3647           isLocal = true;
3648           break;
3649         case T.semicolon:
3650           if (nSkip > 0)
3651             nSkip--;
3652           else
3653             pts[j++] = i;
3654           break;
3655         case T.in:
3656         case T.from:
3657           key = paramAsStr(i - 1);
3658           nSkip -= 2;
3659           if (isAtomExpression(++i)) {
3660             inTok = T.bitset;
3661             bsOrList = atomExpressionAt(i);
3662             if (isBondSet)
3663               bsOrList = BondSet.newBS((BS) bsOrList, null);
3664             isOK = (((BS) bsOrList).nextSetBit(0) >= 0);
3665           } else {
3666             Lst<SV> what = parameterExpressionList(-i, 1, false);
3667             if (what == null || what.size() < 1)
3668               invArg();
3669             SV vl = what.get(0);
3670             switch (inTok = vl.tok) {
3671             case T.bitset:
3672               bsOrList = vl.value;
3673               isOK = !((BS) bsOrList).isEmpty();
3674               break;
3675             case T.varray:
3676               Lst<SV> v = vl.getList();
3677               j = v.size();
3678               isOK = (j > 0);
3679               if (isOK && tok == T.from) {
3680                 int[] i12 = new int[] {SV.iValue(v.get(0)), SV.iValue(v.get(j - 1)) };
3681                 isMinusMinus = (i12[1] < i12[0]);
3682                 bsOrList = i12;
3683                 tok = T.in;
3684                 inTok = T.integer;
3685               } else {
3686                 bsOrList = v;
3687               }
3688               break;
3689             case T.hash:
3690               Map<String, SV> m = vl.getMap();
3691               int n = m.keySet().size();
3692               isOK = (n > 0);
3693               if (isOK) {
3694                 String[] keys = new String[n];
3695                 m.keySet().toArray(keys);
3696                 Arrays.sort(keys);
3697                 bsOrList = keys;
3698               }
3699               break;
3700             default:
3701               invArg();
3702             }
3703           }
3704           i = iToken;
3705           break;
3706         case T.select:
3707           nSkip += 2;
3708           break;
3709         }
3710       }
3711       if (!isForCheck) {
3712         pushContext(cmdToken, "FOR");
3713         thisContext.forVars = forVars;
3714         forVars = null;
3715       }
3716       if (key == null) {
3717         if (isForCheck) {
3718           j = (bsOrList == null ? pts[1] + 1 : 2);
3719         } else {
3720           j = 2;
3721         }
3722         if (tokAt(j) == T.var)
3723           j++;
3724         key = paramAsStr(j);
3725         isMinusMinus = key.equals("--") || key.equals("++");
3726         if (isMinusMinus)
3727           key = paramAsStr(++j);
3728       }
3729       if (isOK)
3730         if (tok == T.in) {
3731           // start of FOR (i in x) block or FOR (i from x)
3732           forVar = getContextVariableAsVariable(key, isLocal);
3733           if (forVar == null && !isLocal)
3734             forVar = vwr.g.getAndSetNewVariable(key, false);
3735           if (forVar == null || forVar.myName == null) {
3736             if (key.startsWith("_"))
3737               invArg();
3738             if (isLocal)
3739               contextVariables.put(key.toLowerCase(), forVar = SV.newI(0));
3740             else
3741               forVar = vwr.g.getAndSetNewVariable(key, true);
3742           }
3743           if (inTok == T.integer) {
3744             // for (i from [0 31])
3745             forVar.tok = T.integer;
3746             forVar.intValue = ((int[]) bsOrList)[0];
3747             forVal = SV.newV(T.integer, bsOrList);
3748             forVal.intValue = (isMinusMinus ? Integer.MIN_VALUE : 0);
3749             j = -1;
3750           } else {
3751             forVal = SV.getVariable(bsOrList);
3752             if (inTok == T.bitset) {
3753               j = ((BS) bsOrList).nextSetBit(0);
3754               forVal.intValue = 0;
3755             } else {
3756               forVal.intValue = 1;
3757               forVar.setv(SV.selectItemVar(forVal));
3758               j = -1;
3759             }
3760           }
3761           if (forVars == null)
3762             forVars = cmdToken.forVars = new SV[2];
3763           forVars[0] = forVar;
3764           forVars[1] = forVal;
3765         } else {
3766           int vtok = tokAt(j);
3767           if (vtok != T.semicolon && (T.tokAttr(vtok, T.misc)
3768               || (forVal = getContextVariableAsVariable(key, false)) != null)) {
3769             if (!isMinusMinus && getToken(++j).tok != T.opEQ)
3770               invArg();
3771             if (isMinusMinus)
3772               j -= 2;
3773             setVariable(++j, slen - 1, key, false);
3774           }
3775           isOK = (pts[0] + 1 == pts[1] || parameterExpressionBoolean(pts[0] + 1, pts[1]));
3776         }
3777     }
3778     if (isOK && tok == T.in && j >= 0) {
3779       forVal.intValue = j;
3780       forVar.tok = T.bitset;
3781       if (isBondSet) {
3782         forVar.value = new BondSet();
3783         ((BondSet) forVar.value).set(j);
3784       } else {
3785         forVar.value = BSUtil.newAndSetBit(j);
3786       }
3787     }
3788     pt++;
3789     if (!isOK) {
3790       cmdToken.forVars = thisContext.forVars;
3791       popContext(true, false);
3792     }
3793     isForCheck = false;
3794     if (!isOK && !chk)
3795       pc = Math.abs(pt) - 1;
3796     return isForCheck;
3797   }
3798 
cmdFlow(int tok, boolean isForCheck, Lst<T[]> vProcess)3799   private boolean cmdFlow(int tok, boolean isForCheck, Lst<T[]> vProcess)
3800       throws ScriptException {
3801     ContextToken ct;
3802     int pt = st[0].intValue;
3803     boolean isDone = (pt < 0 && !chk);
3804     boolean continuing = true;
3805     int ptNext = 0;
3806     switch (tok) {
3807     case T.function:
3808     case T.parallel:
3809       cmdFunc(); // when a function is a command
3810       return isForCheck;
3811     case T.trycmd:
3812       return isForCheck;
3813     case T.catchcmd:
3814       ct = (ContextToken) theToken;
3815       pushContext(ct, "CATCH");
3816       if (!isDone && ct.name0 != null)
3817         contextVariables.put(ct.name0, ct.contextVariables.get(ct.name0));
3818       continuing = !isDone;
3819       st[0].intValue = -Math.abs(pt);
3820       break;
3821     case T.switchcmd:
3822     case T.defaultcmd:
3823     case T.casecmd:
3824       ptNext = Math.abs(aatoken[Math.abs(pt)][0].intValue);
3825       switch (isDone ? 0 : cmdFlowSwitch((ContextToken) theToken, tok)) {
3826       case 0:
3827         // done
3828         ptNext = -ptNext;
3829         continuing = false;
3830         break;
3831       case -1:
3832         // skip this case
3833         continuing = false;
3834         break;
3835       case 1:
3836         // do this one
3837       }
3838       aatoken[pc][0].intValue = Math.abs(pt);
3839       theToken = aatoken[Math.abs(pt)][0];
3840       if (theToken.tok != T.end)
3841         theToken.intValue = ptNext;
3842       break;
3843     case T.ifcmd:
3844     case T.elseif:
3845       continuing = (!isDone && parameterExpressionBoolean(1, 0));
3846       if (chk)
3847         break;
3848       ptNext = Math.abs(aatoken[Math.abs(pt)][0].intValue);
3849       ptNext = (isDone || continuing ? -ptNext : ptNext);
3850       aatoken[Math.abs(pt)][0].intValue = ptNext;
3851       if (tok == T.catchcmd)
3852         aatoken[pc][0].intValue = -pt; // reset to "done" state
3853       break;
3854     case T.elsecmd:
3855       checkLength(1);
3856       if (pt < 0 && !chk)
3857         pc = -pt - 1;
3858       break;
3859     case T.endifcmd:
3860       checkLength(1);
3861       break;
3862     case T.whilecmd:
3863       if (!isForCheck)
3864         pushContext((ContextToken) theToken, "WHILE");
3865       isForCheck = false;
3866       if (!parameterExpressionBoolean(1, 0) && !chk) {
3867         pc = pt;
3868         popContext(true, false);
3869       }
3870       break;
3871     case T.breakcmd:
3872       if (!chk) {
3873         breakAt(pt);
3874         break;
3875       }
3876       if (slen == 1)
3877         break;
3878       int n = intParameter(checkLast(1));
3879       if (chk)
3880         break;
3881       for (int i = 0; i < n; i++)
3882         popContext(true, false);
3883       break;
3884     case T.continuecmd:
3885       isForCheck = true;
3886       if (!chk)
3887         pc = pt - 1;
3888       if (slen > 1)
3889         intParameter(checkLast(1));
3890       break;
3891     case T.end: // function, if, for, while, catch, switch
3892       switch (getToken(checkLast(1)).tok) {
3893       case T.trycmd:
3894         ScriptFunction trycmd = (ScriptFunction) getToken(1).value;
3895         if (chk)
3896           return false;
3897         runFunctionAndRet(trycmd, "try", null, null, true, true, true);
3898         return false;
3899       case T.function:
3900       case T.parallel:
3901         addFunction((ScriptFunction) theToken.value);
3902         return isForCheck;
3903       case T.catchcmd:
3904         popContext(true, false);
3905         break;
3906       case T.process:
3907         addProcess(vProcess, pt, pc);
3908         popContext(true, false);
3909         break;
3910       case T.switchcmd:
3911         if (pt > 0 && cmdFlowSwitch((ContextToken) aatoken[pt][0], 0) == -1) {
3912           // check for the default position
3913           for (; pt < pc; pt++)
3914             if ((tok = aatoken[pt][0].tok) != T.defaultcmd && tok != T.casecmd)
3915               break;
3916           continuing = (pc == pt);
3917         }
3918         break;
3919       case T.ifcmd:
3920         break;
3921       case T.forcmd:
3922       case T.whilecmd:
3923         continuing = false;
3924         isForCheck = true;
3925         break;
3926       }
3927       break;
3928     }
3929     if (!continuing && !chk)
3930       pc = Math.abs(pt) - 1;
3931     return isForCheck;
3932   }
3933 
cmdFlowSwitch(ContextToken c, int tok)3934   private int cmdFlowSwitch(ContextToken c, int tok) throws ScriptException {
3935     if (tok == T.switchcmd)
3936       c.addName("_var");
3937     SV var = c.contextVariables.get("_var");
3938     if (var == null)
3939       return 1; // OK, case found -- no more testing
3940     if (tok == 0) {
3941       // end: remove variable and do default
3942       //      this causes all other cases to
3943       //      skip
3944       c.contextVariables.remove("_var");
3945       return -1;
3946     }
3947     if (tok == T.defaultcmd) // never do the default one directly
3948       return -1;
3949     SV v = parameterExpressionToken(1);
3950     if (tok == T.casecmd) {
3951       boolean isOK = SV.areEqual(var, v);
3952       if (isOK)
3953         c.contextVariables.remove("_var");
3954       return isOK ? 1 : -1;
3955     }
3956     c.contextVariables.put("_var", v);
3957     return 1;
3958   }
3959 
cmdFont(int shapeType, float fontsize)3960   private void cmdFont(int shapeType, float fontsize) throws ScriptException {
3961     String fontface = "SansSerif";
3962     String fontstyle = "Plain";
3963     int sizeAdjust = 0;
3964     float scaleAngstromsPerPixel = -1;
3965     switch (iToken = slen) {
3966     case 6:
3967       scaleAngstromsPerPixel = floatParameter(5);
3968       if (scaleAngstromsPerPixel >= 5) // actually a zoom value
3969         scaleAngstromsPerPixel = vwr.tm.getZoomSetting()
3970             / scaleAngstromsPerPixel / vwr.getScalePixelsPerAngstrom(false);
3971       //$FALL-THROUGH$
3972     case 5:
3973       if (getToken(4).tok != T.identifier)
3974         invArg();
3975       fontstyle = paramAsStr(4);
3976       //$FALL-THROUGH$
3977     case 4:
3978       if (getToken(3).tok != T.identifier)
3979         invArg();
3980       fontface = paramAsStr(3);
3981       if (!isFloatParameter(2))
3982         error(ERROR_numberExpected);
3983       fontsize = floatParameter(2);
3984       shapeType = getShapeType(getToken(1).tok);
3985       break;
3986     case 3:
3987       if (!isFloatParameter(2))
3988         error(ERROR_numberExpected);
3989       if (shapeType == -1) {
3990         shapeType = getShapeType(getToken(1).tok);
3991         fontsize = floatParameter(2);
3992       } else {// labels --- old set fontsize N
3993         if (fontsize >= 1)
3994           fontsize += (sizeAdjust = 5);
3995       }
3996       break;
3997     case 2:
3998     default:
3999       if (shapeType == JC.SHAPE_LABELS) {
4000         // set fontsize
4001         fontsize = JC.LABEL_DEFAULT_FONTSIZE;
4002         break;
4003       }
4004       bad();
4005     }
4006     if (shapeType == JC.SHAPE_LABELS) {
4007       if (fontsize < 0
4008           || fontsize >= 1
4009           && (fontsize < JC.LABEL_MINIMUM_FONTSIZE || fontsize > JC.LABEL_MAXIMUM_FONTSIZE)) {
4010         integerOutOfRange(JC.LABEL_MINIMUM_FONTSIZE - sizeAdjust,
4011             JC.LABEL_MAXIMUM_FONTSIZE - sizeAdjust);
4012         return;
4013       }
4014       setShapeProperty(JC.SHAPE_LABELS, "setDefaults", vwr.slm.noneSelected);
4015     }
4016     if (chk)
4017       return;
4018     if (Font.getFontStyleID(fontface) >= 0) {
4019       fontstyle = fontface;
4020       fontface = "SansSerif";
4021     }
4022     Font font3d = vwr.getFont3D(fontface, fontstyle, fontsize);
4023     sm.loadShape(shapeType);
4024     setShapeProperty(shapeType, "font", font3d);
4025     if (scaleAngstromsPerPixel >= 0)
4026       setShapeProperty(shapeType, "scalereference",
4027           Float.valueOf(scaleAngstromsPerPixel));
4028   }
4029 
cmdFrank(int i)4030   private void cmdFrank(int i) throws ScriptException {
4031     boolean b = true;
4032     if (slen > i)
4033       switch (getToken(checkLast(i)).tok) {
4034       case T.on:
4035         break;
4036       case T.off:
4037         b = false;
4038         break;
4039       default:
4040         error(ERROR_booleanExpected);
4041       }
4042     setBooleanProperty("frank", b);
4043   }
4044 
cmdFunc()4045   private void cmdFunc() throws ScriptException {
4046     if (chk && !isCmdLine_c_or_C_Option)
4047       return;
4048     String name = ((String) getToken(0).value).toLowerCase();
4049     if (tokAt(1) == T.opEQ && tokAt(2) == T.none) {
4050       vwr.removeFunction(name);
4051       return;
4052     }
4053     if (!isFunction(name))
4054       error(ERROR_commandExpected);
4055     Lst<SV> params = (slen == 1 || slen == 3 && tokAt(1) == T.leftparen
4056         && tokAt(2) == T.rightparen ? null : parameterExpressionList(1, -1,
4057         false));
4058     if (chk)
4059       return;
4060     runFunctionAndRet(null, name, params, null, false, true, true);
4061   }
4062 
cmdGetProperty()4063   private void cmdGetProperty() throws ScriptException {
4064     if (chk)
4065       return;
4066     String retValue = "";
4067     String property = optParameterAsString(1);
4068     String name = property;
4069     if (name.indexOf(".") >= 0)
4070       name = name.substring(0, name.indexOf("."));
4071     if (name.indexOf("[") >= 0)
4072       name = name.substring(0, name.indexOf("["));
4073     int propertyID = vwr.getPropertyNumber(name);
4074     Object param = "";
4075     switch (tokAt(2)) {
4076     default:
4077       param = optParameterAsString(2);
4078       break;
4079     case T.define:
4080     case T.expressionBegin:
4081     case T.bitset:
4082       param = atomExpressionAt(2);
4083       if (property.equalsIgnoreCase("bondInfo") && isAtomExpression(++iToken))
4084           param = new BS[] { (BS) param, atomExpressionAt(iToken) };
4085       break;
4086     }
4087     if (property.length() > 0 && propertyID < 0) {
4088       // no such property
4089       property = ""; // produces a list from Property Manager
4090       param = "";
4091     } else if (propertyID >= 0 && slen < 3) {
4092       if ((param = vwr.getDefaultPropertyParam(propertyID)).equals("(visible)"))
4093         param = vwr.ms.getVisibleSet(true);
4094     } else if (propertyID == vwr.getPropertyNumber("fileContents")) {
4095       String s = param.toString();
4096       for (int i = 3; i < slen; i++)
4097         s += paramAsStr(i);
4098       param = s;
4099     }
4100     retValue = (String) vwr.getProperty("readable", property, param);
4101     showString(retValue);
4102   }
4103 
cmdGoto(boolean isCmd)4104   private void cmdGoto(boolean isCmd) throws ScriptException {
4105     String strTo = (isCmd ? paramAsStr(checkLast(1)) : null);
4106     int pcTo = (strTo == null ? aatoken.length - 1 : -1);
4107     String s = null;
4108     for (int i = pcTo + 1; i < aatoken.length; i++) {
4109       T[] tokens = aatoken[i];
4110       int tok = tokens[0].tok;
4111       switch (tok) {
4112       case T.message:
4113       case T.nada:
4114         s = (String) tokens[tokens.length - 1].value;
4115         if (tok == T.nada)
4116           s = s.substring(s.startsWith("#") ? 1 : 2);
4117         break;
4118       default:
4119         continue;
4120       }
4121       if (s.equalsIgnoreCase(strTo)) {
4122         pcTo = i;
4123         break;
4124       }
4125     }
4126     if (pcTo < 0)
4127       invArg();
4128     if (strTo == null)
4129       pcTo = 0;
4130     int di = (pcTo < pc ? 1 : -1);
4131     int nPush = 0;
4132     for (int i = pcTo; i != pc; i += di) {
4133       switch (aatoken[i][0].tok) {
4134       case T.push:
4135       case T.process:
4136       case T.forcmd:
4137       case T.catchcmd:
4138       case T.whilecmd:
4139         nPush++;
4140         break;
4141       case T.pop:
4142         nPush--;
4143         break;
4144       case T.end:
4145         switch (aatoken[i][1].tok) {
4146         case T.process:
4147         case T.forcmd:
4148         case T.catchcmd:
4149         case T.whilecmd:
4150           nPush--;
4151         }
4152         break;
4153       }
4154     }
4155     if (strTo == null) {
4156       pcTo = Integer.MAX_VALUE;
4157       for (; nPush > 0; --nPush)
4158         popContext(false, false);
4159     }
4160     if (nPush != 0)
4161       invArg();
4162     if (!chk)
4163       pc = pcTo - 1; // ... resetting the program counter
4164   }
4165 
cmdHbond()4166   private void cmdHbond() throws ScriptException {
4167     if (slen == 2 && getToken(1).tok == T.calculate) {
4168       if (chk)
4169         return;
4170       int n = vwr.autoHbond(null, null, false);
4171       report(GT.i(GT.$("{0} hydrogen bonds"), Math.abs(n)), false);
4172       return;
4173     }
4174     if (slen == 2 && getToken(1).tok == T.delete) {
4175       if (chk)
4176         return;
4177       checkExtension(T.hbond);
4178       return;
4179     }
4180     int mad = getMadParameter();
4181     if (mad == Integer.MAX_VALUE)
4182       return;
4183     setShapeProperty(JC.SHAPE_STICKS, "type",
4184         Integer.valueOf(Edge.BOND_HYDROGEN_MASK));
4185     setShapeSizeBs(JC.SHAPE_STICKS, mad, null);
4186     setShapeProperty(JC.SHAPE_STICKS, "type",
4187         Integer.valueOf(Edge.BOND_COVALENT_MASK));
4188   }
4189 
cmdHelp()4190   private void cmdHelp() throws ScriptException {
4191     if (chk)
4192       return;
4193     String what = optParameterAsString(1).toLowerCase();
4194     int pt = 0;
4195     if (what.startsWith("mouse") && (pt = what.indexOf(" ")) >= 0
4196         && pt == what.lastIndexOf(" ")) {
4197       showString(vwr.getBindingInfo(what.substring(pt + 1)));
4198       return;
4199     }
4200     if (T.tokAttr(T.getTokFromName(what), T.scriptCommand))
4201       what = "?command=" + what;
4202     vwr.getHelp(what);
4203   }
4204 
cmdHistory(int pt)4205   private void cmdHistory(int pt) throws ScriptException {
4206     // history or set history
4207     if (slen == 1) {
4208       // show it
4209       showString(vwr.getSetHistory(Integer.MAX_VALUE));
4210       return;
4211     }
4212     if (pt == 2) {
4213       // set history n; n' = -2 - n; if n=0, then set history OFF
4214       int n = intParameter(checkLast(2));
4215       if (n < 0)
4216         invArg();
4217       if (!chk)
4218         vwr.getSetHistory(n == 0 ? 0 : -2 - n);
4219       return;
4220     }
4221     switch (getToken(checkLast(1)).tok) {
4222     // pt = 1 history ON/OFF/CLEAR
4223     case T.on:
4224     case T.clear:
4225       if (!chk)
4226         vwr.getSetHistory(Integer.MIN_VALUE);
4227       return;
4228     case T.off:
4229       if (!chk)
4230         vwr.getSetHistory(0);
4231       break;
4232     default:
4233       errorStr(ERROR_keywordExpected, "ON, OFF, CLEAR");
4234     }
4235   }
4236 
cmdHover()4237   private void cmdHover() throws ScriptException {
4238     if (chk)
4239       return;
4240     String strLabel = (slen == 1 ? "on" : paramAsStr(1));
4241     if (strLabel.equalsIgnoreCase("on"))
4242       strLabel = "%U";
4243     else if (strLabel.equalsIgnoreCase("off"))
4244       strLabel = null;
4245     vwr.setHoverLabel(strLabel);
4246   }
4247 
cmdLabel(int index, BS bs)4248   private void cmdLabel(int index, BS bs) throws ScriptException {
4249     if (chk)
4250       return;
4251     sm.loadShape(JC.SHAPE_LABELS);
4252     Object strLabel = null;
4253     switch (getToken(index).tok) {
4254     case T.on:
4255       strLabel = vwr.getStandardLabelFormat(0);
4256       break;
4257     case T.off:
4258       break;
4259     case T.hide:
4260     case T.display:
4261       setShapeProperty(JC.SHAPE_LABELS, "display",
4262           theTok == T.display ? Boolean.TRUE : Boolean.FALSE);
4263       return;
4264     case T.varray:
4265       strLabel = theToken.value;
4266       break;
4267     default:
4268       strLabel = paramAsStr(index);
4269     }
4270     sm.setLabel(strLabel, bs == null ? vwr.bsA() : bs);
4271   }
4272 
cmdLoad()4273   public void cmdLoad() throws ScriptException {
4274     boolean doLoadFiles = (!chk || isCmdLine_C_Option);
4275     boolean isAppend = false;
4276     boolean isInline = false;
4277     boolean isSmiles = false;
4278     boolean isMutate = false;
4279     boolean isData = false;
4280     boolean isAsync = vwr.async;
4281     boolean isConcat = false;
4282     boolean doOrient = false;
4283     boolean appendNew = vwr.getBoolean(T.appendnew);
4284     boolean isAudio = false;
4285     String filename = null;
4286     BS bsModels;
4287     int i = (tokAt(0) == T.data ? 0 : 1);
4288     String filter = null;
4289     int modelCount0 = vwr.ms.mc
4290         - (vwr.fm.getFileName().equals(JC.ZAP_TITLE) ? 1 : 0);
4291     int ac0 = vwr.ms.ac;
4292     SB loadScript = new SB().append("load");
4293     int nFiles = 1;
4294     Map<String, Object> htParams = new Hashtable<String, Object>();
4295     // ignore optional file format
4296     if (isStateScript) {
4297       htParams.put("isStateScript", Boolean.TRUE);
4298       if (forceNoAddHydrogens)
4299         htParams.put("doNotAddHydrogens", Boolean.TRUE);
4300     }
4301     String modelName = null;
4302     String[] filenames = null;
4303     String[] tempFileInfo = null;
4304     String errMsg = null;
4305     SB sOptions = new SB();
4306     int tokType = 0;
4307     int tok;
4308 
4309     // check for special parameters
4310 
4311     if (slen == 1) {
4312       i = 0;
4313     } else {
4314       modelName = paramAsStr(i);
4315       if (slen == 2 && !chk) {
4316         // spt, png, and pngj files may be
4317         // run using the LOAD command, but
4318         // we transfer them to the script command
4319         // if it is just LOAD "xxxx.xxx"
4320         // so as to avoid the ZAP in case these
4321         // do not contain a full state script
4322         if (modelName.endsWith(".spt") || modelName.endsWith(".png")
4323             || modelName.endsWith(".pngj")) {
4324           cmdScript(0, modelName, null);
4325           return;
4326         }
4327       }
4328 
4329       tok = tokAt(i);
4330       // load MENU
4331       // load DATA "xxx" ...(data here)...END "xxx"
4332       // load DATA "append_and/or_orientation xxx" ...(data here)...END "append_and/or_orientation xxx"
4333       // load DATA "@varName"
4334       // load APPEND (moves pointer forward)
4335       // load XYZ
4336       // load VXYZ
4337       // load VIBRATION
4338       // load TEMPERATURE
4339       // load OCCUPANCY
4340       // load PARTIALCHARGE
4341       // load HISTORY
4342       // load NBO
4343       switch (tok) {
4344       case T.var:
4345         String var = paramAsStr(++i);
4346         filename = "@" + var;
4347         Object o = getVarParameter(var, false);
4348         if (o instanceof Map<?, ?>) {
4349           checkLength(3);
4350           loadPNGJVar(filename, o, htParams);
4351           return;
4352         }
4353         break;
4354       case T.nbo:
4355       case T.history:
4356       case T.menu:
4357         String m = paramAsStr(checkLast(2));
4358         if (!chk) {
4359           switch (tok) {
4360           case T.nbo:
4361             htParams.put("service", "nbo");
4362             htParams.put("mode", Integer.valueOf(1)); // MODEL
4363             htParams.put("action", "load");
4364             htParams.put("value", m);
4365             htParams.put("sync", Boolean.TRUE);
4366             vwr.sm.processService(htParams);
4367             runScript((String) htParams.get("ret"));
4368             break;
4369           case T.history:
4370             vwr.setHistory(m);
4371             break;
4372           case T.menu:
4373             vwr.setMenu(m, true);
4374             break;
4375           }
4376         }
4377         return;
4378       case T.mutate:
4379         isMutate = isAppend = true;
4380         appendNew = false;
4381         loadScript.append(" mutate");
4382         modelName = optParameterAsString(++i);
4383         tok = T.getTokFromName(modelName);
4384         htParams.put("appendToModelIndex", Integer.valueOf(vwr.am.cmi));
4385         break;
4386       case T.append:
4387         // we are looking out for state scripts after model 1.1 deletion.
4388         modelName = optParameterAsString(++i);
4389         int ami = PT.parseInt(modelName);
4390         isAppend = (!isStateScript || vwr.ms.mc > 0);
4391         if (isAppend)
4392           loadScript.append(" append");
4393         if (ami >= 0) {
4394           modelName = optParameterAsString(++i);
4395           if (isAppend) {
4396             loadScript.append(" " + ami);
4397             appendNew = false;
4398             htParams.put("appendToModelIndex", Integer.valueOf(ami));
4399           }
4400         }
4401         tok = T.getTokFromName(modelName);
4402         break;
4403       case T.orientation:
4404         doOrient = true;
4405         loadScript.append(" orientation");
4406         vwr.stm.saveOrientation("preload", null);
4407         modelName = optParameterAsString(++i);
4408         tok = T.getTokFromName(modelName);
4409         break;
4410       case T.audio:
4411         isAudio = true;
4412         i++;
4413         break;
4414       case T.identifier:
4415         i++;
4416         loadScript.append(" " + modelName);
4417         tokType = (tok == T.identifier
4418             && PT.isOneOf(modelName.toLowerCase(), JC.LOAD_ATOM_DATA_TYPES) ? T
4419             .getTokFromName(modelName) : T.nada);
4420         if (tokType != T.nada) {
4421           // loading just some data here
4422           // xyz vxyz vibration temperature occupancy partialcharge
4423           htParams.put("atomDataOnly", Boolean.TRUE);
4424           htParams.put("modelNumber", Integer.valueOf(1));
4425           if (tokType == T.vibration)
4426             tokType = T.vibxyz;
4427           tempFileInfo = vwr.fm.getFileInfo();
4428           isAppend = true;
4429         }
4430       }
4431       // LOAD [[APPEND]] FILE
4432       // LOAD [[APPEND]] INLINE
4433       // LOAD [[APPEND]] SMILES
4434       // LOAD [[APPEND]] TRAJECTORY
4435       // LOAD [[APPEND]] MODEL
4436       // LOAD ASYNC  (asynchronous -- flag for RecentFileDialog)
4437       // LOAD [[APPEND]] "fileNameInQuotes"
4438 
4439       switch (tok) {
4440       case T.file:
4441         i++;
4442         loadScript.append(" " + modelName);
4443         if (optParameterAsString(i).equals("+")) {
4444           isConcat = true;
4445           i++;
4446           loadScript.append(" +");
4447         }
4448         if (optParameterAsString(i).equals("-")) {
4449           isConcat = true;
4450           i++;
4451           loadScript.append(" -");
4452         }
4453         if (tokAt(i) == T.varray) {
4454           filenames = stringParameterSet(i);
4455           i = iToken;
4456           if (i + 1 != slen)
4457             invArg();
4458           if (filenames != null)
4459             nFiles = filenames.length;
4460         }
4461         break;
4462       case T.inline:
4463         isInline = true;
4464         i++;
4465         loadScript.append(" " + modelName);
4466         break;
4467       case T.smiles:
4468         isSmiles = true;
4469         i++;
4470         break;
4471       case T.async:
4472         isAsync = true;
4473         htParams.put("async", Boolean.TRUE);
4474         i++;
4475         break;
4476       case T.trajectory:
4477       case T.model:
4478         i++;
4479         loadScript.append(" " + modelName);
4480         if (tok == T.trajectory)
4481           htParams.put("isTrajectory", Boolean.TRUE);
4482         if (isPoint3f(i)) {
4483           P3 pt = getPoint3f(i, false, true);
4484           i = iToken + 1;
4485           // first last stride
4486           htParams.put("firstLastStep", new int[] { (int) pt.x, (int) pt.y,
4487               (int) pt.z });
4488           loadScript.append(" " + Escape.eP(pt));
4489         } else {
4490           switch (tokAt(i)) {
4491           case T.bitset:
4492             bsModels = (BS) getToken(i++).value;
4493             htParams.put("bsModels", bsModels);
4494             loadScript.append(" " + Escape.eBS(bsModels));
4495             break;
4496           default:
4497             htParams.put("firstLastStep", new int[] { 0, -1, 1 });
4498           }
4499         }
4500         break;
4501       case T.identifier:
4502         // i has been incremented; continue...
4503         break;
4504       case T.data:
4505         String key = stringParameter(++i).toLowerCase();
4506         modelName = optParameterAsString(i + 1);
4507         isAppend = key.startsWith("append");
4508         if (isAppend && key.startsWith("append modelindex=")) {
4509            int ami = PT.parseInt(key.substring(18));
4510            if (ami >= 0) {
4511              appendNew = false;
4512              htParams.put("appendToModelIndex", Integer.valueOf(ami));
4513            }
4514         }
4515         doOrient = (key.indexOf("orientation") >= 0);
4516         i = addLoadData(loadScript, key, htParams, i);
4517         isData = true;
4518         break;
4519       default:
4520         modelName = "fileset";
4521       }
4522       if (filename == null && filenames == null && getToken(i).tok != T.string)
4523         error(ERROR_filenameExpected);
4524     }
4525     // long timeBegin = System.currentTimeMillis();
4526 
4527     // file name is next
4528 
4529     // LOAD ... "xxxx"
4530     // LOAD ... "xxxx" AS "yyyy"
4531 
4532     int filePt = i;
4533     int ptAs = i + 1;
4534     String localName = null;
4535     //    String annotation = null;
4536     //    if (tokAt(filePt + 1) == T.divide) {
4537     //      annotation = optParameterAsString(filePt + 2);
4538     //      ptAs += 2;
4539     //      i += 2;
4540     //    }
4541     if (tokAt(ptAs) == T.as) {
4542       localName = stringParameter(i = ptAs + 1);
4543       if (vwr.fm.getPathForAllFiles() != "") {
4544         // we use the LOCAL name when reading from a local path only (in the case of JMOL files)
4545         localName = null;
4546         filePt = i;
4547       }
4548     }
4549 
4550     String appendedData = null;
4551     String appendedKey = null;
4552 
4553     if (slen == i + 1) {
4554       // end-of-command options:
4555       // LOAD SMILES "xxxx" --> load "$xxxx"
4556 
4557       if (filename == null
4558           && (i == 0 || filenames == null
4559               && (filename = paramAsStr(filePt)).length() == 0))
4560         filename = getFullPathName();
4561       if (filename == null && filenames == null) {
4562         cmdZap(false);
4563         return;
4564       }
4565       if (filenames == null && !isInline) {
4566         if (isSmiles) {
4567           filename = "$" + filename;
4568         } else {
4569           if (filename.equals("String[]"))
4570             return;
4571           if (filename.indexOf("[") == 0) {
4572             filenames = Escape.unescapeStringArray(filename);
4573             if (filenames != null) {
4574               if (i == 1)
4575                 loadScript.append(" files");
4576               nFiles = filenames.length;
4577             }
4578           }
4579         }
4580       }
4581       if (filenames != null)
4582         for (int j = 0; j < nFiles; j++)
4583           loadScript.append(" /*file*/").append(PT.esc(filenames[j]));
4584     } else if (isLoadOption(getToken(i + 1).tok)) {
4585 
4586       // more complicated command options, in order
4587       // (checking the tokens after "....")
4588 
4589       // LOAD "" --> prevous file
4590 
4591       if (filename == null && (filename = paramAsStr(filePt)).length() == 0
4592           && (filename = getFullPathName()) == null) {
4593         // no previously loaded file
4594         cmdZap(false);
4595         return;
4596       }
4597       if (filePt == i || localName != null)
4598         i++;
4599 
4600       // for whatever reason, we don't allow a filename with [] in it.
4601       if (filename.equals("String[]"))
4602         return;
4603       // MANIFEST "..."
4604       if ((tok = tokAt(i)) == T.manifest) {
4605         String manifest = stringParameter(++i);
4606         htParams.put("manifest", manifest);
4607         sOptions.append(" MANIFEST " + PT.esc(manifest));
4608         tok = tokAt(++i);
4609       }
4610       // n >= 0: model number
4611       // n < 0: vibration number
4612       // [index1, index2, index3,...]
4613 
4614       switch (tok) {
4615       case T.integer:
4616       case T.varray:
4617       case T.leftsquare:
4618       case T.spacebeforesquare:
4619         i = getLoadModelIndex(i, sOptions, htParams);
4620         break;
4621       }
4622       i = getCmdExt().getLoadSymmetryParams(i, sOptions, htParams);
4623 
4624       // .... APPEND DATA "appendedData" .... end "appendedData"
4625       // option here to designate other than "appendedData"
4626       // .... APPEND "appendedData" @x ....
4627 
4628       if (tokAt(i) == T.append) {
4629         // for CIF reader -- experimental
4630         if (tokAt(++i) == T.data) {
4631           i += 2;
4632           appendedData = (String) getToken(i++).value;
4633           appendedKey = stringParameter(++i);
4634           ++i;
4635         } else {
4636           appendedKey = stringParameter(i++);
4637           appendedData = stringParameter(i++);
4638         }
4639         htParams.put(appendedKey, appendedData);
4640       }
4641       if (tokAt(i) == T.filter)
4642         filter = stringParameter(++i);
4643     } else {
4644       Lst<String> fNames = new Lst<String>();
4645       if (i == 1) {
4646         if (tokAt(i + 1) == T.plus || tokAt(i + 1) == T.minus) {
4647           modelName = "files";
4648         } else {
4649           i++;
4650         }
4651         loadScript.append(" " + modelName);
4652       }
4653       if (tokAt(i + 1) == T.minus) // state from /val
4654         isConcat = true;
4655       filter = getLoadFilesList(i, loadScript, sOptions, htParams, fNames);
4656       filenames = fNames.toArray(new String[nFiles = fNames.size()]);
4657       if (!isConcat && loadScript.indexOf("/*concat*/") >= 0)
4658         isConcat = true;
4659     }
4660 
4661     // end of parsing
4662 
4663     if (!doLoadFiles)
4664       return;
4665 
4666     if (filenames != null)
4667       filename = "fileSet";
4668 
4669     // get default filter if necessary
4670 
4671     if (appendedData != null) {
4672       sOptions.append(" APPEND data \"" + appendedKey + "\"\n" + appendedData
4673           + (appendedData.endsWith("\n") ? "" : "\n") + "end \"" + appendedKey
4674           + "\"");
4675     }
4676     if (filter == null)
4677       filter = vwr.g.defaultLoadFilter;
4678     if (filter.length() > 0) {
4679       if (filter.toUpperCase().indexOf("DOCACHE") >= 0) {
4680         if (!isStateScript && !isAppend)
4681           vwr.cacheClear();
4682       }
4683       htParams.put("filter", filter);
4684       if (filter.equalsIgnoreCase("2d")) // MOL file hack
4685         filter = "2D-noMin";
4686       sOptions.append(" FILTER " + PT.esc(filter));
4687     }
4688 
4689     // store inline data or variable data in htParams
4690 
4691     boolean isVariable = false;
4692     if (filenames == null) {
4693       if (filename.equals("string") && vwr.am.cmi >= 0) {
4694         filename = vwr.getCurrentFileAsString(null);
4695         loadScript = new SB().append("load inline ");
4696         isInline = true;
4697       }
4698       if (isInline) {
4699         htParams.put("fileData", filename);
4700       } else if (filename.startsWith("@") && filename.length() > 1) {
4701         Object o = getVarParameter(filename.substring(1), false);
4702         if (o instanceof Map<?, ?>) {
4703           checkLength(i + 1);
4704           loadPNGJVar(filename, o, htParams);
4705           return;
4706         }
4707         isVariable = true;
4708         o = "" + o;
4709         loadScript = new SB().append("{\n    var ")
4710             .append(filename.substring(1)).append(" = ")
4711             .append(PT.esc((String) o)).append(";\n    ").appendSB(loadScript);
4712         htParams.put("fileData", o);
4713       } else if (!isData && !((filename.startsWith("=") || filename.startsWith("*")) && filename.indexOf("/") > 0)) {
4714         // only for cases that can get filename changed to actual reference
4715         String type = "";
4716         int pt = filename.indexOf("::");
4717         if (pt > 0 && pt < 20) { // trying to avoid conflict with some sort of URL that has "::" in it.
4718           type = filename.substring(0, pt + 2);
4719           filename = filename.substring(pt + 2);
4720         }
4721         filename = type + checkFileExists("LOAD" + (isAppend ? "_APPEND_" : "_"),
4722             isAsync, filename, filePt, !isAppend && pc != pcResume);
4723 
4724         if (filename.startsWith("cache://"))
4725           localName = null;
4726         // on first pass, a ScriptInterruption will be thrown;
4727         // on the second pass, we will have the file name, which will be cache://localLoad_n__m
4728       }
4729     }
4730 
4731     // set up the output stream from AS keyword
4732 
4733     OC out = null;
4734     String filecat = null;
4735     if (localName != null) {
4736       if (localName.equals("."))
4737         localName = vwr.fm.getFilePath(filename, false, true);
4738       if (localName.length() == 0
4739           || vwr.fm.getFilePath(localName, false, false).equalsIgnoreCase(
4740               vwr.fm.getFilePath(filename, false, false)))
4741         invArg();
4742       String[] fullPath = new String[] { localName };
4743       out = vwr.getOutputChannel(localName, fullPath);
4744       if (out == null)
4745         Logger.error("Could not create output stream for " + fullPath[0]);
4746       else
4747         htParams.put("outputChannel", out);
4748     }
4749 
4750     // check for single file or string
4751 
4752     if (filenames == null && tokType == 0) {
4753 
4754       // finalize the loadScript
4755 
4756       loadScript.append(" ");
4757       if (isVariable || isInline) {
4758         loadScript.append(filename.indexOf('\n') >= 0 || isVariable ? PT
4759             .esc(filename) : filename);
4760       } else if (!isData) {
4761         if (localName != null)
4762           localName = vwr.fm.getFilePath(localName, false, false);
4763         if (!filename.equals("String[]"))
4764           loadScript.append("/*file*/").append(
4765               (localName != null ? PT.esc(localName) : "$FILENAME$"));
4766       }
4767       if (!isConcat && (filename.startsWith("=") || filename.startsWith("*"))
4768           && filename.indexOf("/") > 0) {
4769 
4770         // EBI domains and validations, also rna3 and dssr
4771 
4772         // load *1cbs/dom/xx/xx  -->  load *1cbs - *dom/xx/xx/1cbs
4773         // load *1cbs/val/xx/xx  -->  load *1cbs - *val/xx/xx/1cbs
4774         // load *1cbs/rna3d/loops  -->  load *1cbs - *rna3d/loops/downloads/1cbs
4775         // TODO load *1cbs/map/xx/xx  -->  load *1cbs - *map/xx/xx/1cbs (unimplemented electron density?)
4776         // load =1mys/dssr  -->  load =1mys + *dssr/1mys
4777 
4778         isConcat = true;
4779         int pt = filename.indexOf("/");
4780         String id;
4781         if (pt == 1 && (id = vwr.getPdbID()) != null) {
4782           filename = filename.substring(0, 1) + id + filename.substring(1);
4783           pt = filename.indexOf("/");
4784         } else {
4785           id = filename.substring(1, pt);
4786         }
4787         String ext = filename.substring(pt + 1);
4788         filename = filename.substring(0, pt);
4789         if ((pt = filename.indexOf(".")) >= 0)
4790           filename = filename.substring(0, pt);
4791         if (JC.PDB_ANNOTATIONS.indexOf(";" + ext + ";") >= 0
4792             || ext.startsWith("dssr--")) {
4793           if (filename.startsWith("="))
4794             filename += ".cif";
4795           filenames = (ext.equals("all") ? new String[] { filename,
4796               "*dom/" + id, "*val/" + id } : new String[] { filename,
4797               "*" + ext + "/" + id });
4798           filename = "fileSet";
4799           loadScript = null;
4800           isVariable = false;
4801           filecat = "-";
4802           //sOptions.setLength(0);
4803         } else {
4804           filename += "/" + ext;
4805         }
4806       }
4807       if (loadScript != null) {
4808         if (sOptions.length() > 0)
4809           loadScript.append(" /*options*/ ").append(sOptions.toString());
4810         if (isVariable)
4811           loadScript.append("\n  }");
4812         htParams.put("loadScript", loadScript);
4813       }
4814     }
4815 
4816     if (isAudio) {
4817       if (filename != null)
4818         htParams.put("audioFile", filename);
4819       addFilterAttribute(htParams, filter, "id");
4820       addFilterAttribute(htParams, filter, "pause");
4821       addFilterAttribute(htParams, filter, "play");
4822       addFilterAttribute(htParams, filter, "ended");
4823       addFilterAttribute(htParams, filter, "action");
4824       vwr.sm.playAudio(htParams);
4825       return;
4826     }
4827 
4828     // load model
4829 
4830     setCursorWait(true);
4831     boolean timeMsg = vwr.getBoolean(T.showtiming);
4832     if (timeMsg)
4833       Logger.startTimer("load");
4834     if (!isStateScript && !isAppend)
4835       vwr.setBooleanProperty("legacyJavaFloat", false);
4836     if (isMutate)
4837       htParams.put("isMutate", Boolean.TRUE);
4838     htParams.put("eval", this);
4839     errMsg = vwr.loadModelFromFile(null, filename, filenames, null, isAppend,
4840         htParams, loadScript, sOptions, tokType, filecat != null ? filecat
4841             : isConcat ? "+" : " ");
4842     if (timeMsg)
4843       showString(Logger.getTimerMsg("load", 0));
4844 
4845     // close output channel
4846 
4847     if (out != null) {
4848       vwr.fm.setFileInfo(new String[] { localName });
4849       Logger.info(GT.o(GT.$("file {0} created"), localName));
4850       showString(vwr.fm.getFilePath(localName, false, false) + " created");
4851       out.closeChannel();
4852     }
4853 
4854     // check for just loading an atom property
4855 
4856     if (tokType > 0) {
4857       // reset the file info in FileManager, check for errors, and return
4858       vwr.fm.setFileInfo(tempFileInfo);
4859       if (errMsg != null && !isCmdLine_c_or_C_Option)
4860         evalError(errMsg, null);
4861       return;
4862     }
4863 
4864     // check for an error
4865 
4866     if (errMsg != null && !isCmdLine_c_or_C_Option) {
4867       // note that as ZAP will already have been issued here.
4868       if (errMsg.indexOf(JC.NOTE_SCRIPT_FILE) == 0) {
4869         filename = errMsg.substring(JC.NOTE_SCRIPT_FILE.length()).trim();
4870         if (filename.indexOf("png|") >= 0 && filename.endsWith("pdb|state.spt")) {
4871           // fix for earlier version in JavaScript saving the full state instead of just the PDB file.
4872           filename = filename.substring(0, filename.lastIndexOf("|"));
4873           filename += filename.substring(filename.lastIndexOf("|"));
4874           runScript("load \"" + filename + "\"");
4875           return;
4876         }
4877         cmdScript(0, filename, null);
4878         return;
4879       }
4880       if (vwr.async && errMsg.startsWith(JC.READER_NOT_FOUND)) {
4881         //TODO: other errors can occur due to missing files
4882         //String rdrName = errMsg.substring(JC.READER_NOT_FOUND.length());
4883         throw new ScriptInterruption(this, "async", 1);
4884         //errMsg = "asynchronous load for " + rdrName + " initiated";
4885       }
4886       evalError(errMsg, null);
4887     }
4888 
4889     if (debugHigh)
4890       report(
4891           "Successfully loaded:"
4892               + (filenames == null ? htParams.get("fullPathName") : modelName),
4893           false);
4894 
4895     finalizeLoad(isAppend, appendNew, isConcat, doOrient, nFiles, ac0,
4896         modelCount0, isData);
4897 
4898   }
4899 
checkFileExists(String prefix, boolean isAsync, String filename, int i, boolean doClear)4900   public String checkFileExists(String prefix, boolean isAsync, String filename, int i, boolean doClear) throws ScriptException {
4901     if (chk || filename.startsWith("cache://"))
4902        return filename;
4903     if ((vwr.testAsync || Viewer.isJS)
4904         && (isAsync || filename.startsWith("?"))
4905         || vwr.apiPlatform.forceAsyncLoad(filename)) {
4906       filename = loadFileAsync(prefix, filename, i, doClear);
4907       // on first pass, a ScriptInterruption will be thrown;
4908       // on the second pass, we will have the file name, which will be cache://localLoad_n__m
4909     }
4910 
4911     String[] fullPathNameOrError = vwr.getFullPathNameOrError(filename);
4912     filename = fullPathNameOrError[0];
4913     if (fullPathNameOrError[1] != null)
4914       errorStr(ScriptError.ERROR_fileNotFoundException, filename
4915           + ":" + fullPathNameOrError[1]);
4916     return filename;
4917   }
4918 
addFilterAttribute(Map<String, Object> htParams, String filter, String key)4919   private void addFilterAttribute(Map<String, Object> htParams, String filter,
4920                                   String key) {
4921     String val = PT.getQuotedOrUnquotedAttribute(filter, key);
4922     if (val != null && val.length() > 0)
4923       htParams.put(key, val);
4924   }
4925 
addLoadData(SB loadScript, String key, Map<String, Object> htParams, int i)4926   private int addLoadData(SB loadScript, String key, Map<String, Object> htParams, int i) throws ScriptException {
4927     loadScript.append(" /*data*/ data");
4928     int ptVar = key.indexOf("@");
4929     if (ptVar >= 0)
4930       key = key.replace('@', '_');
4931     loadScript.append(" ").append(PT.esc(key));
4932     String strModel = (ptVar >= 0 ? ""
4933         + getParameter(key.substring(ptVar + 1), T.string, true)
4934         : paramAsStr(++i));
4935     strModel = Viewer.fixInlineString(strModel, vwr.getInlineChar());
4936     htParams.put("fileData", strModel);
4937     htParams.put("isData", Boolean.TRUE);
4938     //note: ScriptCompiler will remove an initial \n if present
4939     loadScript.appendC('\n').append(strModel).append(" end ")
4940         .append(PT.esc(key));
4941     if (ptVar < 0)
4942       i += 2; // skip END "key"
4943     return i;
4944   }
4945 
loadPNGJVar(String varName, Object o, Map<String, Object> htParams)4946   private void loadPNGJVar(String varName, Object o, Map<String, Object> htParams) throws ScriptException {
4947     SV[] av = new SV[] {SV.newV(T.hash, o)};
4948     getCmdExt().dispatch(T.binary, false, av);
4949     htParams.put("imageData", av[0].value);
4950     OC out = vwr.getOutputChannel(null, null);
4951     htParams.put("outputChannel", out);
4952     vwr.createZip("", "BINARY", htParams);
4953     String modelName = "cache://VAR_" + varName;
4954     vwr.cacheFileByName("cache://VAR_*",false);
4955     vwr.cachePut(modelName, out.toByteArray());
4956     cmdScript(0, modelName, null);
4957   }
4958 
getLoadFilesList(int i, SB loadScript, SB sOptions, Map<String, Object> htParams, Lst<String> fNames)4959   private String getLoadFilesList(int i, SB loadScript, SB sOptions,
4960                                 Map<String, Object> htParams, Lst<String> fNames) throws ScriptException {
4961     // list of file names
4962     // or COORD {i j k} "fileName"
4963     // or COORD ({bitset}) "fileName"
4964     // or FILTER "xxxx"
4965 
4966     Lst<Object> firstLastSteps = null;
4967     String filter = null;
4968     P3 pt = null;
4969     BS bs = null;
4970     while (i < slen) {
4971       switch (tokAt(i)) {
4972       case T.plus:
4973         loadScript.append("/*concat*/ +");
4974         ++i;
4975         continue;
4976       case T.minus:
4977         // =xxxx/val split into two
4978         loadScript.append(" -");
4979         ++i;
4980         continue;
4981       case T.integer:
4982       case T.varray:
4983       case T.leftsquare:
4984       case T.spacebeforesquare:
4985         i = getLoadModelIndex(i, sOptions, htParams);
4986         continue;
4987       case T.filter:
4988         filter = stringParameter(++i);
4989         ++i;
4990         continue;
4991       case T.coord:
4992         htParams.remove("isTrajectory");
4993         if (firstLastSteps == null) {
4994           firstLastSteps = new Lst<Object>();
4995           pt = P3.new3(0, -1, 1);
4996         }
4997         if (isPoint3f(++i)) {
4998           pt = getPoint3f(i, false, true);
4999           i = iToken + 1;
5000         } else if (tokAt(i) == T.bitset) {
5001           bs = (BS) getToken(i).value;
5002           pt = null;
5003           i = iToken + 1;
5004         }
5005         break;
5006       case T.identifier:
5007         invArg();
5008       }
5009       fNames.addLast(paramAsStr(i++));
5010       if (pt != null) {
5011         firstLastSteps
5012             .addLast(new int[] { (int) pt.x, (int) pt.y, (int) pt.z });
5013         loadScript.append(" COORD " + Escape.eP(pt));
5014       } else if (bs != null) {
5015         firstLastSteps.addLast(bs);
5016         loadScript.append(" COORD " + Escape.eBS(bs));
5017       }
5018       loadScript.append(" /*file*/$FILENAME" + fNames.size() + "$");
5019     }
5020     if (firstLastSteps != null)
5021       htParams.put("firstLastSteps", firstLastSteps);
5022     return filter;
5023   }
5024 
isLoadOption(int tok)5025   private boolean isLoadOption(int tok) {
5026     switch (tok) {
5027     case T.manifest:
5028       // model/vibration index or list of model indices
5029     case T.integer:
5030     case T.varray:
5031     case T.leftsquare:
5032     case T.spacebeforesquare:
5033     // {i j k} (lattice)
5034     case T.leftbrace:
5035     case T.point3f:
5036     // PACKED/CENTROID, either order
5037     case T.packed:
5038     case T.centroid:
5039     // SUPERCELL {i j k}
5040     case T.supercell:
5041     // RANGE x.x or RANGE -x.x
5042     case T.fill:  // new in Jmol 14.3.14
5043     // FILL BOUNDBOX
5044     // FILL UNITCELL
5045     case T.range:
5046     // SPACEGROUP "nameOrNumber"
5047     // or SPACEGROUP "IGNOREOPERATORS"
5048     // or SPACEGROUP "" (same as current)
5049     case T.spacegroup:
5050     // UNITCELL [a b c alpha beta gamma]
5051     // or UNITCELL [ax ay az bx by bz cx cy cz]
5052     // or UNITCELL "" (same as current)
5053     // UNITCELL "..." or UNITCELL ""
5054     case T.unitcell:
5055     // OFFSET {x y z}
5056     case T.offset:
5057     case T.data:
5058     // FILTER "..."
5059     case T.append:
5060       // Jmol 13.1.5 -- APPEND "data..."
5061       return true;
5062     case T.filter:
5063     case T.identifier:
5064       return (tokAt(iToken + 2) != T.coord);
5065     }
5066     return false;
5067   }
5068 
getLoadModelIndex(int i, SB sOptions, Map<String, Object> htParams)5069   private int getLoadModelIndex(int i, SB sOptions,
5070                                  Map<String, Object> htParams)
5071       throws ScriptException {
5072     int n;
5073     switch (tokAt(i)) {
5074     case T.integer:
5075       htParams.remove("firstLastStep");
5076       htParams.remove("bsModel");
5077       htParams.put("useFileModelNumbers", Boolean.TRUE);
5078       n = intParameter(i);
5079       sOptions.append(" ").appendI(n);
5080       if (n < 0)
5081         htParams.put("vibrationNumber", Integer.valueOf(-n));
5082       else
5083         htParams.put("modelNumber", Integer.valueOf(n));
5084       break;
5085     case T.varray:
5086     case T.leftsquare:
5087     case T.spacebeforesquare:
5088       htParams.remove("firstLastStep");
5089       float[] data = floatParameterSet(i, 1, Integer.MAX_VALUE);
5090       BS bs = new BS();
5091       int[] iArray = new int[data.length];
5092       for (int j = 0; j < data.length; j++) {
5093         n = (int) data[j];
5094         if (data[j] >= 1 && data[j] == n)
5095           bs.set(n - 1);
5096         else
5097           invArg();
5098         iArray[j] = n;
5099       }
5100       sOptions.append(" " + Escape.eAI(iArray));
5101       htParams.put("bsModels", bs);
5102       htParams.put("useFileModelNumbers", Boolean.TRUE);
5103       break;
5104     }
5105     return iToken + 1;
5106   }
5107 
finalizeLoad(boolean isAppend, boolean appendNew, boolean isConcat, boolean doOrient, int nFiles, int ac0, int modelCount0, boolean isData)5108   private void finalizeLoad(boolean isAppend, boolean appendNew,
5109                             boolean isConcat, boolean doOrient, int nFiles,
5110                             int ac0, int modelCount0, boolean isData)
5111       throws ScriptException {
5112     if (isAppend && (appendNew || nFiles > 1)) {
5113       vwr.setAnimationRange(-1, -1);
5114       vwr.setCurrentModelIndex(modelCount0);
5115     }
5116     String msg;
5117     if (scriptLevel == 0 && !isAppend && (isConcat || nFiles < 2)
5118         && (msg = (String) vwr.ms.getInfoM("modelLoadNote")) != null)
5119       vwr.showString(msg, false);
5120     Object centroid = vwr.ms.getInfoM("centroidMinMax");
5121     if (AU.isAI(centroid) && vwr.ms.ac > 0) {
5122       BS bs = BSUtil.newBitSet2(isAppend ? ac0 : 0, vwr.ms.ac);
5123       vwr.ms.setCentroid(bs, (int[]) centroid);
5124     }
5125     String script = vwr.g.defaultLoadScript;
5126     msg = "";
5127     if (script.length() > 0)
5128       msg += "\nUsing defaultLoadScript: " + script;
5129     String embeddedScript;
5130     Map<String, Object> info = vwr.ms.msInfo;
5131     if (info != null && vwr.allowEmbeddedScripts()
5132         && (embeddedScript = (String) info.remove("jmolscript")) != null
5133         && embeddedScript.length() > 0) {
5134       msg += "\nAdding embedded #jmolscript: " + embeddedScript;
5135       script += ";" + embeddedScript;
5136       setStringProperty("_loadScript", script);
5137       script = "allowEmbeddedScripts = false;try{" + script
5138           + "} allowEmbeddedScripts = true;";
5139       isEmbedded = !isCmdLine_c_or_C_Option;
5140     } else {
5141       setStringProperty("_loadScript", "");
5142     }
5143     logLoadInfo(msg, isData);
5144 
5145     String siteScript = (info == null ? null
5146         : (String) info.remove("sitescript"));
5147     if (siteScript != null)
5148       script = siteScript + ";" + script;
5149     if (doOrient)
5150       script += ";restore orientation preload";
5151     if (script.length() > 0 && !isCmdLine_c_or_C_Option)
5152       // NOT checking embedded scripts in some cases
5153       runScript(script);
5154     isEmbedded = false;
5155   }
5156 
cmdLog()5157   private void cmdLog() throws ScriptException {
5158     if (slen == 1)
5159       bad();
5160     if (chk)
5161       return;
5162     String s = parameterExpressionString(1, 0);
5163     if (tokAt(1) == T.off)
5164       setStringProperty("logFile", "");
5165     else
5166       vwr.log(s);
5167   }
5168 
cmdLoop()5169   private void cmdLoop() throws ScriptException {
5170     if (vwr.headless)
5171       return;
5172     // back to the beginning of this script
5173     if (!chk)
5174       pc = -1;
5175     cmdDelay();
5176     // JavaScript will not get here
5177   }
5178 
cmdMessage()5179   private void cmdMessage() throws ScriptException {
5180     String text = paramAsStr(checkLast(1));
5181     if (chk)
5182       return;
5183     String s = Txt.formatText(vwr, text);
5184     if (outputBuffer == null && !vwr.isPrintOnly)
5185       Logger.warn(s);
5186     if (!s.startsWith("_"))
5187       report(s, false);
5188   }
5189 
5190   /**
5191    * ONE difference between FRAME and MODEL: model 1 sets model NAMED one in the
5192    * case of PDB frame 1 always sets the first model
5193    *
5194    * @param offset
5195    *        will be 2 for "anim frame ..."
5196    * @throws ScriptException
5197    */
cmdModel(int offset)5198   private void cmdModel(int offset) throws ScriptException {
5199     boolean isFrame = (theTok == T.frame || vwr.ms.mc > 1);
5200     int[] frameList = new int[] { -1, -1 };
5201     int nFrames = 0;
5202     boolean useModelNumber = true;
5203     int modelIndex = -1;
5204     if (slen == 1 && offset == 1) {
5205       modelIndex = vwr.am.cmi;
5206       int m;
5207       if (!chk && modelIndex >= 0
5208           && (m = vwr.ms.getJmolDataSourceFrame(modelIndex)) >= 0)
5209         vwr.setCurrentModelIndex(m == modelIndex ? Integer.MIN_VALUE : m);
5210       return;
5211     }
5212     switch (tokAt(1)) {
5213     case T.mo:
5214       if (!chk && isFrame && slen == 2) {
5215         while (++modelIndex < vwr.ms.mc) {
5216           if (!vwr.ms.am[modelIndex].auxiliaryInfo.containsKey("moData"))
5217             continue;
5218           vwr.am.setFrame(modelIndex);
5219           showString("Frame set to " + (modelIndex + 1));
5220           return;
5221         }
5222         showString("No molecular orbitals");
5223       }
5224       return;
5225     case T.integer:
5226       if (isFrame && slen == 2) {
5227         // FRAME n
5228         if (!chk)
5229           vwr.am.setFrame(intParameter(1) - 1);
5230         return;
5231       }
5232       break;
5233     case T.expressionBegin:
5234     case T.bitset:
5235       modelIndex = atomExpressionAt(1).nextSetBit(0);
5236       if (chk || modelIndex < 0 || modelIndex >= vwr.ms.ac)
5237         return;
5238       modelIndex = vwr.ms.at[modelIndex].mi;
5239       if (iToken + 1 == slen) {
5240         vwr.setCurrentModelIndex(modelIndex);
5241         return;
5242       }
5243       frameList[nFrames++] = modelIndex;
5244       offset = iToken + 1;
5245       useModelNumber = false;
5246       break;
5247     case T.create:
5248       iToken = 1;
5249       int n = (tokAt(2) == T.integer ? intParameter(++iToken) : 1);
5250       checkLength(iToken + 1);
5251       if (!chk && n > 0)
5252         vwr.ms.createModels(n);
5253       return;
5254     case T.id:
5255       checkLength(3);
5256       String id = stringParameter(2);
5257       if (!chk)
5258         vwr.setCurrentModelID(id);
5259       return;
5260     case T.delay:
5261       long millis = 0;
5262       checkLength(3);
5263       switch (getToken(2).tok) {
5264       case T.integer:
5265       case T.decimal:
5266         millis = (long) (floatParameter(2) * 1000);
5267         break;
5268       default:
5269         error(ERROR_integerExpected);
5270       }
5271       if (!chk)
5272         vwr.setFrameDelayMs(millis);
5273       return;
5274     case T.title:
5275       if (checkLength23() > 0)
5276         if (!chk)
5277           vwr.setFrameTitleObj(slen == 2 ? "@{_modelName}"
5278               : (tokAt(2) == T.varray ? SV.strListValue(st[2]) : paramAsStr(2)));
5279       return;
5280     case T.orientation:
5281       if (tokAt(2) == T.decimal && tokAt(3) == T.matrix4f) {
5282         modelIndex = vwr.ms.getModelNumberIndex(getToken(2).intValue, false,
5283             false);
5284         M4 mat4 = (M4) getToken(3).value;
5285         if (modelIndex >= 0)
5286           vwr.ms.am[modelIndex].mat4 = mat4;
5287         return;
5288       }
5289       break;
5290     case T.align:
5291       boolean isNone = (tokAt(2) == T.none);
5292       BS bs = (slen == 2 || isNone ? null : atomExpressionAt(2));
5293       if (isNone)
5294         iToken = 2;
5295       boolean isFixed = (tokAt(iToken + 1) == T.fixed);
5296       checkLength(iToken + (isFixed ? 2 : 1));
5297       if (!chk)
5298         vwr.setFrameOffsets(bs, isFixed);
5299       return;
5300     }
5301     if (getToken(offset).tok == T.minus) {
5302       ++offset;
5303       if (getToken(checkLast(offset)).tok != T.integer
5304           || intParameter(offset) != 1)
5305         invArg();
5306       if (!chk)
5307         vwr.setAnimation(T.prev);
5308       return;
5309     }
5310     boolean isPlay = false;
5311     boolean isRange = false;
5312     String propName = null;
5313     Object prop = null;
5314     boolean isAll = false;
5315     boolean isHyphen = false;
5316     float fFrame = 0;
5317     P3 frameAlign = null;
5318     boolean haveFileSet = vwr.haveFileSet();
5319     if (isArrayParameter(1)) {
5320       setFrameSet(1);
5321       isAll = true;
5322     } else {
5323       for (int i = offset; i < slen; i++) {
5324         switch (getToken(i).tok) {
5325         case T.align:
5326           // model 2.3 align {0 0 0}  // from state
5327           if (i != 2)
5328             invArg();
5329           frameAlign = centerParameter(3, null);
5330           checkLength(i = iToken + 1);
5331           break;
5332         case T.all:
5333         case T.times:
5334           checkLength(offset + (isRange ? 2 : 1));
5335           isAll = true;
5336           break;
5337         case T.minus: // ignore
5338           if (nFrames != 1)
5339             invArg();
5340           isHyphen = true;
5341           break;
5342         case T.none:
5343           checkLength(offset + 1);
5344           break;
5345         case T.decimal:
5346           useModelNumber = false;
5347           if ((fFrame = floatParameter(i)) < 0) {
5348             checkLength(i + 1);
5349             if (!chk)
5350               vwr.am.morph(-fFrame);
5351             return;
5352           }
5353           //$FALL-THROUGH$
5354         case T.integer:
5355         case T.string:
5356           if (nFrames == 2)
5357             invArg();
5358           int iFrame = (theTok == T.string ? getFloatEncodedInt((String) theToken.value)
5359               : theToken.intValue);
5360           if (iFrame < 0 && nFrames == 1) {
5361             isHyphen = true;
5362             iFrame = -iFrame;
5363             if (haveFileSet && iFrame < 1000000)
5364               iFrame *= 1000000;
5365           }
5366           if (theTok == T.decimal && haveFileSet && fFrame == (int) fFrame)
5367             iFrame = (int) fFrame * 1000000;
5368           if (iFrame == Integer.MAX_VALUE) {
5369             useModelNumber = false;
5370             frameList[nFrames++] = (chk || i != 1 ? 0 : vwr
5371                 .getModelIndexFromId(theToken.value.toString()));
5372             break;
5373           }
5374           if (iFrame == -1) {
5375             checkLength(offset + 1);
5376             if (!chk)
5377               vwr.setAnimation(T.prev);
5378             return;
5379           }
5380           if (iFrame >= 1000 && iFrame < 1000000 && haveFileSet)
5381             iFrame = (iFrame / 1000) * 1000000 + (iFrame % 1000); // initial way
5382           if (!useModelNumber && iFrame == 0 && nFrames == 0)
5383             isAll = true; // 0.0 means ALL; 0 means "all in this range
5384           if (iFrame >= 1000000)
5385             useModelNumber = false;
5386           frameList[nFrames++] = iFrame;
5387           break;
5388         case T.play:
5389           isPlay = true;
5390           break;
5391         case T.range:
5392           isRange = true;
5393           break;
5394         case T.property:
5395           if (modelIndex < 0 && (modelIndex = vwr.am.cmi) < 0)
5396             return;
5397           propName = paramAsStr(++i);
5398           SV sv = setVariable(++i, -1, "", false);
5399           if (sv != null && !chk) {
5400             if (propName.equalsIgnoreCase("DSSR")) {
5401               loadDssr(modelIndex, (String) sv.value);
5402               return;
5403             }
5404             prop = SV.oValue(sv);
5405           }
5406           if (!chk)
5407             vwr.ms.setInfo(modelIndex, propName, prop);
5408           return;
5409         default:
5410           frameControl(offset);
5411           return;
5412         }
5413       }
5414     }
5415     if (chk)
5416       return;
5417     if (isRange && nFrames == 0)
5418       isAll = true;
5419     if (isAll) {
5420       vwr.setAnimationOn(false);
5421       vwr.setAnimationRange(-1, -1);
5422       if (!isRange)
5423         vwr.setCurrentModelIndex(-1);
5424       return;
5425     }
5426     if (nFrames == 2 && !isRange)
5427       isHyphen = true;
5428     if (haveFileSet)
5429       useModelNumber = false;
5430     else if (useModelNumber)
5431       for (int i = 0; i < nFrames; i++)
5432         if (frameList[i] >= 0)
5433           frameList[i] %= 1000000;
5434     modelIndex = vwr.ms
5435         .getModelNumberIndex(frameList[0], useModelNumber, false);
5436     if (frameAlign != null) {
5437       if (modelIndex >= 0) {
5438         vwr.ms.translateModel(modelIndex, null);
5439         vwr.ms.translateModel(modelIndex, frameAlign);
5440       }
5441       return;
5442     }
5443     int modelIndex2 = -1;
5444     if (haveFileSet && modelIndex < 0 && frameList[0] != 0) {
5445       // may have frame 2.0 or frame 2 meaning the range of models in file 2
5446       // or frame 2.0 - 3.1   or frame 2.0 - 3.0
5447       if (frameList[0] < 1000000)
5448         frameList[0] *= 1000000;
5449       if (nFrames == 2 && frameList[1] < 1000000)
5450         frameList[1] *= 1000000;
5451       if (frameList[0] % 1000000 == 0) {
5452         frameList[0]++;
5453         modelIndex = vwr.ms.getModelNumberIndex(frameList[0], false, false);
5454         if (modelIndex >= 0) {
5455           int i2 = (nFrames == 1 ? frameList[0] + 1000000
5456               : frameList[1] == 0 ? -1
5457                   : frameList[1] % 1000000 == 0 ? frameList[1] + 1000001
5458                       : frameList[1] + 1);
5459           modelIndex2 = vwr.ms.getModelNumberIndex(i2, false, false);
5460           if (modelIndex2 < 0)
5461             modelIndex2 = vwr.ms.mc;
5462           modelIndex2--;
5463           if (isRange)
5464             nFrames = 2;
5465           else if (!isHyphen && modelIndex2 != modelIndex)
5466             isHyphen = true;
5467           isRange = isRange || modelIndex == modelIndex2;// (isRange ||
5468           // !isHyphen &&
5469           // modelIndex2 !=
5470           // modelIndex);
5471         }
5472       } else {
5473         // must have been a bad frame number. Just return.
5474         return;
5475       }
5476     }
5477 
5478     if (!isPlay && !isRange || modelIndex >= 0)
5479       vwr.setCurrentModelIndexClear(modelIndex, false);
5480     if (isPlay && nFrames == 2 || isRange || isHyphen) {
5481       if (modelIndex2 < 0)
5482         modelIndex2 = vwr.ms.getModelNumberIndex(frameList[1], useModelNumber,
5483             false);
5484       vwr.setAnimationOn(false);
5485       vwr.am.setAnimationDirection(1);
5486       vwr.setAnimationRange(modelIndex, modelIndex2);
5487       vwr.setCurrentModelIndexClear(isHyphen && !isRange ? -1
5488           : modelIndex >= 0 ? modelIndex : 0, false);
5489     }
5490     if (isPlay)
5491       vwr.setAnimation(T.resume);
5492   }
5493 
loadDssr(int modelIndex, String data)5494   private void loadDssr(int modelIndex, String data) throws ScriptException {
5495     if (modelIndex < 0 && (modelIndex = vwr.am.cmi) < 0)
5496       errorStr(ScriptError.ERROR_multipleModelsDisplayedNotOK, "load <dssr file>");
5497     if (!data.startsWith("{"))
5498       data = vwr.getFileAsString3(data, true, "script");
5499     clearDefinedVariableAtomSets();
5500     Map<String, Object> map = vwr.parseJSONMap(data);
5501     showString(vwr.getAnnotationParser(true).fixDSSRJSONMap(map));
5502     vwr.ms.setInfo(modelIndex, "dssr", map);
5503   }
5504 
cmdMove()5505   private void cmdMove() throws ScriptException {
5506     checkLength(-11);
5507     // rotx roty rotz zoom transx transy transz slab seconds fps
5508     V3 dRot = V3.new3(floatParameter(1), floatParameter(2), floatParameter(3));
5509     float dZoom = floatParameter(4);
5510     V3 dTrans = V3.new3(intParameter(5), intParameter(6), intParameter(7));
5511     float dSlab = floatParameter(8);
5512     float floatSecondsTotal = floatParameter(9);
5513     int fps = (slen == 11 ? intParameter(10) : 30);
5514     if (chk)
5515       return;
5516     refresh(false);
5517     if (!useThreads())
5518       floatSecondsTotal = 0;
5519     vwr.move(this, dRot, dZoom, dTrans, dSlab, floatSecondsTotal, fps);
5520     if (floatSecondsTotal > 0 && isJS)
5521       throw new ScriptInterruption(this, "move", 1);
5522   }
5523 
cmdMoveto()5524   private void cmdMoveto() throws ScriptException {
5525     // moveto time
5526     // moveto [time] { x y z deg} zoom xTrans yTrans (rotCenter) rotationRadius
5527     // (navCenter) xNav yNav navDepth
5528     // moveto [time] { x y z deg} 0 xTrans yTrans (rotCenter) [zoom factor]
5529     // (navCenter) xNav yNav navDepth
5530     // moveto [time] { x y z deg} (rotCenter) [zoom factor] (navCenter) xNav
5531     // yNav navDepth
5532     // where [zoom factor] is [0|n|+n|-n|*n|/n|IN|OUT]
5533     // moveto [time] front|back|left|right|top|bottom
5534     if (slen == 2 && tokAt(1) == T.stop) {
5535       if (!chk)
5536         vwr.tm.stopMotion();
5537       return;
5538     }
5539     float floatSecondsTotal;
5540     if (slen == 2 && isFloatParameter(1)) {
5541       floatSecondsTotal = floatParameter(1);
5542       if (chk)
5543         return;
5544       if (!useThreads())
5545         floatSecondsTotal = 0;
5546       if (floatSecondsTotal > 0)
5547         refresh(false);
5548       vwr.moveTo(this, floatSecondsTotal, null, JC.axisZ, 0, null, 100, 0, 0,
5549           0, null, Float.NaN, Float.NaN, Float.NaN, Float.NaN, Float.NaN,
5550           Float.NaN);
5551       if (isJS && floatSecondsTotal > 0 && vwr.g.waitForMoveTo)
5552         throw new ScriptInterruption(this, "moveTo", 1);
5553       return;
5554     }
5555     V3 axis = V3.new3(Float.NaN, 0, 0);
5556     P3 center = null;
5557     int i = 1;
5558     floatSecondsTotal = (isFloatParameter(i) ? floatParameter(i++) : 2.0f);
5559     float degrees = 90;
5560     BS bsCenter = null;
5561     boolean isChange = true;
5562     boolean isMolecular = false;
5563     float xTrans = 0;
5564     float yTrans = 0;
5565     float zoom = Float.NaN;
5566     float rotationRadius = Float.NaN;
5567     float zoom0 = vwr.tm.getZoomSetting();
5568     P3 navCenter = null;
5569     float xNav = Float.NaN;
5570     float yNav = Float.NaN;
5571     float navDepth = Float.NaN;
5572     float cameraDepth = Float.NaN;
5573     float cameraX = Float.NaN;
5574     float cameraY = Float.NaN;
5575     float[] pymolView = null;
5576     Quat q = null;
5577     int tok = getToken(i).tok;
5578     switch (tok) {
5579     case T.pymol:
5580       // 18-element standard PyMOL view matrix
5581       // [0-8] are 3x3 rotation matrix (inverted)
5582       // [9,10] are x,y translations (y negative)
5583       // [11] is distance from camera to center (negative)
5584       // [12-14] are rotation center coords
5585       // [15-16] are slab and depth distance from camera (0 to ignore)
5586       // [17] is field of view; positive for orthographic projection
5587       // or 21-element extended matrix (PSE file reading)
5588       // [18,19] are boolean depth_cue and fog settings
5589       // [20] is fogStart (usually 0.45)
5590       pymolView = floatParameterSet(++i, 18, 21);
5591       i = iToken + 1;
5592       if (chk && checkLength(i) > 0)
5593         return;
5594       break;
5595     case T.quaternion:
5596       if (tokAt(++i) == T.molecular) {
5597         // see comment below
5598         isMolecular = true;
5599         i++;
5600       }
5601       if (isAtomExpression(i)) {
5602         isMolecular = true;
5603         Object[] ret = new Object[1];
5604         center = centerParameter(i, ret);
5605         if (!(ret[0] instanceof BS))
5606           invArg();
5607         bsCenter = (BS) ret[0];
5608         q = (chk ? new Quat() : vwr.ms.getQuaternion(bsCenter.nextSetBit(0),
5609             vwr.getQuaternionFrame()));
5610       } else {
5611         q = getQuaternionParameter(i, null, false);
5612       }
5613       i = iToken + 1;
5614       if (q == null)
5615         invArg();
5616       break;
5617     case T.point4f:
5618     case T.point3f:
5619     case T.leftbrace:
5620       // {X, Y, Z} deg or {x y z deg}
5621       if (isPoint3f(i)) {
5622         axis.setT(getPoint3f(i, true, true));
5623         i = iToken + 1;
5624         degrees = floatParameter(i++);
5625       } else {
5626         P4 pt4 = getPoint4f(i);
5627         i = iToken + 1;
5628         axis.set(pt4.x, pt4.y, pt4.z);
5629         degrees = (pt4.x == 0 && pt4.y == 0 && pt4.z == 0 ? Float.NaN : pt4.w);
5630       }
5631       break;
5632     case T.front:
5633       axis.set(1, 0, 0);
5634       degrees = 0f;
5635       checkLength(++i);
5636       break;
5637     case T.back:
5638       axis.set(0, 1, 0);
5639       degrees = 180f;
5640       checkLength(++i);
5641       break;
5642     case T.left:
5643       axis.set(0, 1, 0);
5644       checkLength(++i);
5645       break;
5646     case T.right:
5647       axis.set(0, -1, 0);
5648       checkLength(++i);
5649       break;
5650     case T.top:
5651       axis.set(1, 0, 0);
5652       checkLength(++i);
5653       break;
5654     case T.bottom:
5655       axis.set(-1, 0, 0);
5656       checkLength(++i);
5657       break;
5658     case T.axis:
5659       String abc = paramAsStr(++i);
5660       if (abc.equals("-"))
5661         abc += paramAsStr(++i);
5662       checkLength(++i);
5663       switch ("xyz".indexOf(abc)) {
5664       case 0:
5665         q = Quat.new4(0.5f,0.5f,0.5f,-0.5f);
5666         break;
5667       case 1:
5668         q = Quat.new4(0.5f,0.5f,0.5f,0.5f);
5669         break;
5670       case 2:
5671         q = Quat.new4(0, 0, 0, 1);
5672         break;
5673       default:
5674          // a b c
5675         SymmetryInterface uc;
5676         uc = vwr.getCurrentUnitCell();
5677         if (uc == null) {
5678           uc = vwr.getSymTemp();
5679           uc.setUnitCell(new float[] { 1, 1, 1, 90, 90, 90 }, false);
5680         }
5681         q = uc.getQuaternionRotation(abc);
5682         if (q == null)
5683           invArg();
5684       }
5685       break;
5686     default:
5687       // X Y Z deg
5688       axis = V3.new3(floatParameter(i++), floatParameter(i++),
5689           floatParameter(i++));
5690       degrees = floatParameter(i++);
5691     }
5692     if (q != null) {
5693       A4 aa;
5694       aa = q.toAxisAngle4f();
5695       axis.set(aa.x, aa.y, aa.z);
5696       /*
5697        * The quaternion angle for an atom represents the angle by which the
5698        * reference frame must be rotated to match the frame defined for the
5699        * residue.
5700        *
5701        * However, to "moveTo" this frame as the REFERENCE frame, what we have to
5702        * do is take that quaternion frame and rotate it BACKWARD by that many
5703        * degrees. Then it will match the reference frame, which is ultimately
5704        * our window frame.
5705        *
5706        * We only apply this for molecular-type quaternions, because in general
5707        * the orientation quaternion refers to how the reference plane has been
5708        * changed (the orientation matrix)
5709        */
5710       degrees = (isMolecular ? -1 : 1) * (float) (aa.angle * 180.0 / Math.PI);
5711     }
5712     if (Float.isNaN(axis.x) || Float.isNaN(axis.y) || Float.isNaN(axis.z))
5713       axis.set(0, 0, 0);
5714     else if (axis.length() == 0 && degrees == 0)
5715       degrees = Float.NaN;
5716     isChange = (tok == T.quaternion || !vwr.tm.isInPosition(axis, degrees));
5717     // optional zoom
5718     if (isFloatParameter(i))
5719       zoom = floatParameter(i++);
5720     // optional xTrans yTrans
5721     if (isFloatParameter(i) && !isCenterParameter(i)) {
5722       xTrans = floatParameter(i++);
5723       yTrans = floatParameter(i++);
5724       if (!isChange && Math.abs(xTrans - vwr.tm.getTranslationXPercent()) >= 1)
5725         isChange = true;
5726       if (!isChange && Math.abs(yTrans - vwr.tm.getTranslationYPercent()) >= 1)
5727         isChange = true;
5728     }
5729     if (bsCenter == null && i != slen) {
5730       // if any more, required (center)
5731       Object[] ret = new Object[1];
5732       center = centerParameter(i, ret);
5733       if (ret[0] instanceof BS)
5734         bsCenter = (BS) ret[0];
5735       i = iToken + 1;
5736     }
5737     if (center != null) {
5738       if (!isChange && center.distance(vwr.tm.fixedRotationCenter) >= 0.1)
5739         isChange = true;
5740       // optional {center} rotationRadius
5741       if (isFloatParameter(i))
5742         rotationRadius = floatParameter(i++);
5743       if (!isCenterParameter(i)) {
5744         if ((rotationRadius == 0 || Float.isNaN(rotationRadius))
5745             && (zoom == 0 || Float.isNaN(zoom))) {
5746           // alternative (atom expression) 0 zoomFactor
5747           float newZoom = Math.abs(getZoom(0, i, bsCenter, (zoom == 0 ? 0
5748               : zoom0)));
5749           i = iToken + 1;
5750           zoom = newZoom;
5751         } else {
5752           if (!isChange
5753               && Math.abs(rotationRadius - vwr.getFloat(T.rotationradius)) >= 0.1)
5754             isChange = true;
5755         }
5756       }
5757       if (zoom == 0 || Float.isNaN(zoom))
5758         zoom = 100;
5759       if (Float.isNaN(rotationRadius))
5760         rotationRadius = 0;
5761 
5762       if (!isChange && Math.abs(zoom - zoom0) >= 1)
5763         isChange = true;
5764       // (navCenter) xNav yNav navDepth
5765 
5766       if (i != slen) {
5767         navCenter = centerParameter(i, null);
5768         i = iToken + 1;
5769         if (i != slen) {
5770           xNav = floatParameter(i++);
5771           yNav = floatParameter(i++);
5772         }
5773         if (i != slen)
5774           navDepth = floatParameter(i++);
5775         if (i != slen) {
5776           cameraDepth = floatParameter(i++);
5777           if (!isChange
5778               && Math.abs(cameraDepth - vwr.tm.getCameraDepth()) >= 0.01f)
5779             isChange = true;
5780         }
5781         if (i + 1 < slen) {
5782           cameraX = floatParameter(i++);
5783           cameraY = floatParameter(i++);
5784           if (!isChange && Math.abs(cameraX - vwr.tm.camera.x) >= 0.01f)
5785             isChange = true;
5786           if (!isChange && Math.abs(cameraY - vwr.tm.camera.y) >= 0.01f)
5787             isChange = true;
5788         }
5789       }
5790     }
5791     checkLength(i);
5792     if (chk)
5793       return;
5794     if (!isChange)
5795       floatSecondsTotal = 0;
5796     if (floatSecondsTotal > 0)
5797       refresh(false);
5798     if (!useThreads())
5799       floatSecondsTotal = 0;
5800     if (cameraDepth == 0) {
5801       cameraDepth = cameraX = cameraY = Float.NaN;
5802     }
5803     if (pymolView != null)
5804       vwr.tm.moveToPyMOL(this, floatSecondsTotal, pymolView);
5805     else
5806       vwr.moveTo(this, floatSecondsTotal, center, axis, degrees, null, zoom,
5807           xTrans, yTrans, rotationRadius, navCenter, xNav, yNav, navDepth,
5808           cameraDepth, cameraX, cameraY);
5809     if (isJS && floatSecondsTotal > 0 && vwr.g.waitForMoveTo)
5810       throw new ScriptInterruption(this, "moveTo", 1);
5811   }
5812 
isAtomExpression(int i)5813   public boolean isAtomExpression(int i) {
5814     switch(tokAt(i)) {
5815     case T.define: // added 5/13/17
5816     case T.bitset:
5817     case T.expressionBegin:
5818       return true;
5819     default:
5820       return false;
5821     }
5822   }
5823 
cmdPause()5824   private boolean cmdPause() throws ScriptException {
5825     if (chk || isJS && !allowJSThreads)
5826       return false;
5827     String msg = optParameterAsString(1);
5828     if (!vwr.getBooleanProperty("_useCommandThread")) {
5829       // showString("Cannot pause thread when _useCommandThread = FALSE: " +
5830       // msg);
5831       // return;
5832     }
5833     if (vwr.autoExit || !vwr.haveDisplay && !Viewer.isWebGL)
5834       return false;
5835     if (scriptLevel == 0 && pc == aatoken.length - 1) {
5836       vwr.scriptStatus("nothing to pause: " + msg);
5837       return false;
5838     }
5839     msg = (msg.length() == 0 ? ": RESUME to continue." : ": "
5840         + Txt.formatText(vwr, msg));
5841     pauseExecution(true);
5842     vwr.scriptStatusMsg("script execution paused" + msg,
5843         "script paused for RESUME");
5844     return true;
5845   }
5846 
cmdPrint()5847   private void cmdPrint() throws ScriptException {
5848     if (slen == 1) {
5849       if (!chk)
5850         showStringPrint("\0", true);
5851       return;
5852     }
5853     showStringPrint(parameterExpressionString(1, 0), true);
5854   }
5855 
cmdPrompt()5856   private void cmdPrompt() throws ScriptException {
5857     String msg = null;
5858     if (slen == 1) {
5859       if (!chk)
5860         msg = getContextTrace(vwr, getScriptContext("prompt"), null, true)
5861             .toString();
5862     } else {
5863       msg = parameterExpressionString(1, 0);
5864     }
5865     if (!chk)
5866       vwr.prompt(msg, null, null, true);
5867   }
5868 
cmdReset()5869   private void cmdReset() throws ScriptException {
5870     if (slen == 3 && tokAt(1) == T.function) {
5871       if (!chk)
5872         vwr.removeFunction(stringParameter(2));
5873       return;
5874     }
5875     checkLength(-2);
5876     if (chk)
5877       return;
5878     if (slen == 1) {
5879       vwr.reset(false);
5880       return;
5881     }
5882     // possibly "all"
5883     switch (tokAt(1)) {
5884     case T.print:
5885       if (!chk && outputBuffer != null)
5886         outputBuffer.setLength(0);
5887       return;
5888     case T.cache:
5889       vwr.cacheClear();
5890       return;
5891     case T.error:
5892       resetError();
5893       return;
5894     case T.lighting:
5895       vwr.stm.resetLighting();
5896       return;
5897     case T.shape:
5898       vwr.resetShapes(true);
5899       return;
5900     case T.function:
5901       vwr.clearFunctions();
5902       return;
5903     case T.structure:
5904       BS bsModified = new BS();
5905       runScript(vwr.ms.getDefaultStructure(vwr.bsA(), bsModified));
5906       vwr.shm.resetBioshapes(bsModified);
5907       return;
5908     case T.vanderwaals:
5909       vwr.setData("element_vdw", new Object[] { null, "" }, 0, 0, 0, 0, 0);
5910       return;
5911     case T.aromatic:
5912       vwr.ms.resetAromatic();
5913       return;
5914     case T.spin:
5915       vwr.reset(true);
5916       return;
5917     }
5918     String var = paramAsStr(1);
5919     if (var.charAt(0) == '_')
5920       invArg();
5921     vwr.unsetProperty(var);
5922   }
5923 
resetError()5924   private void resetError() {
5925     vwr.g.removeParam("_errormessage");
5926   }
5927 
cmdRestrict()5928   private void cmdRestrict() throws ScriptException {
5929     boolean isBond = (tokAt(1) == T.bonds);
5930     cmdSelect(isBond ? 2 : 1);
5931     restrictSelected(isBond, true);
5932   }
5933 
cmdReturn(SV tv)5934   private void cmdReturn(SV tv) throws ScriptException {
5935     if (chk)
5936       return;
5937     SV t = getContextVariableAsVariable("_retval", false);
5938     if (t != null) {
5939       SV v = (tv != null || slen == 1 ? null : parameterExpressionToken(1));
5940       if (tv == null)
5941         tv = (v == null ? SV.newI(0) : v);
5942       t.value = tv.value;
5943       t.intValue = tv.intValue;
5944       t.tok = tv.tok;
5945     }
5946     cmdGoto(false);
5947   }
5948 
cmdRotate(boolean isSpin, boolean isSelected)5949   public void cmdRotate(boolean isSpin, boolean isSelected)
5950       throws ScriptException {
5951 
5952     // rotate is a full replacement for spin
5953     // spin is DEPRECATED
5954 
5955     /*
5956      * The Chime spin method:
5957      *
5958      * set spin x 10;set spin y 30; set spin z 10; spin | spin ON spin OFF
5959      *
5960      * Jmol does these "first x, then y, then z" I don't know what Chime does.
5961      *
5962      * spin and rotate are now consolidated here.
5963      *
5964      * far simpler is
5965      *
5966      * spin x 10 spin y 10
5967      *
5968      * these are pure x or y spins or
5969      *
5970      * spin axisangle {1 1 0} 10
5971      *
5972      * this is the same as the old "spin x 10; spin y 10" -- or is it? anyway,
5973      * it's better!
5974      *
5975      * note that there are many defaults
5976      *
5977      * spin # defaults to spin y 10
5978      * spin 10 # defaults to spin y 10
5979      * spin x # defaults to spin x 10
5980      *
5981      * and several new options
5982      *
5983      * spin -x
5984      * spin axisangle {1 1 0} 10
5985      * spin 10 (atomno=1)(atomno=2)
5986      * spin 20 {0 0 0} {1 1 1}
5987      *
5988      * spin MOLECULAR {0 0 0} 20
5989      *
5990      * The MOLECULAR keyword indicates that spins or rotations are to be carried
5991      * out in the internal molecular coordinate frame, not the fixed room frame.
5992      *
5993      * In the case of rotateSelected, all rotations are molecular and the
5994      * absense of the MOLECULAR keyword indicates to rotate about the geometric
5995      * center of the molecule, not {0 0 0}
5996      *
5997      * Fractional coordinates may be indicated:
5998      *
5999      * spin 20 {0 0 0/} {1 1 1/}
6000      *
6001      * In association with this, TransformManager and associated functions are
6002      * TOTALLY REWRITTEN and consolideated. It is VERY clean now - just two
6003      * methods here -- one fixed and one molecular, two in Viewer, and two in
6004      * TransformManager. All the centering stuff has been carefully inspected
6005      * are reorganized as well.
6006      *
6007      * Bob Hanson 5/21/06
6008      */
6009 
6010     if (slen == 2)
6011       switch (getToken(1).tok) {
6012       case T.on:
6013         if (!chk)
6014           vwr.tm.setSpinOn();
6015         return;
6016       case T.off:
6017         if (!chk)
6018           vwr.tm.setSpinOff();
6019         return;
6020       }
6021 
6022     BS bsAtoms = null, bsBest = null;
6023     float degreesPerSecond = PT.FLOAT_MIN_SAFE;
6024     int nPoints = 0;
6025     float endDegrees = Float.MAX_VALUE;
6026     boolean isMolecular = false;
6027     boolean haveRotation = false;
6028     float[] dihedralList = null;
6029     Lst<P3> ptsA = null;
6030     P3[] points = new P3[2];
6031     V3 rotAxis = V3.new3(0, 1, 0);
6032     V3 translation = null;
6033     M4 m4 = null;
6034     M3 m3 = null;
6035     boolean is4x4 = false;
6036     int direction = 1;
6037     int tok;
6038     Quat q = null;
6039     boolean helicalPath = false;
6040     boolean isDegreesPerSecond = false;
6041     boolean isSeconds = false;
6042     Lst<P3> ptsB = null;
6043     BS bsCompare = null;
6044     P3 invPoint = null;
6045     P4 invPlane = null;
6046     boolean axesOrientationRasmol = vwr.getBoolean(T.axesorientationrasmol);
6047     for (int i = 1; i < slen; ++i) {
6048       switch (tok = getToken(i).tok) {
6049       case T.rotate:
6050         // from MODELKIT - ignore
6051         continue;
6052       case T.define:
6053       case T.bitset:
6054       case T.expressionBegin:
6055         bsBest = atomExpressionAt(i);
6056         if (translation != null || q != null || nPoints == 2) {
6057           bsAtoms = bsBest;
6058           ptsB = null;
6059           isSelected = true;
6060           break;
6061         }
6062         //$FALL-THROUGH$
6063       case T.leftbrace:
6064       case T.point3f:
6065       case T.dollarsign:
6066         haveRotation = true;
6067         if (nPoints == 2)
6068           nPoints = 0;
6069         // {X, Y, Z}
6070         // $drawObject[n]
6071         P3 pt1 = centerParameterForModel(i, vwr.am.cmi, null);
6072         if (!chk && tok == T.dollarsign && tokAt(i + 2) != T.leftsquare) {
6073           // rotation about an axis such as $line1
6074           isMolecular = true;
6075           Object[] data = new Object[] { objectNameParameter(++i),
6076               Integer.valueOf(vwr.am.cmi), null };
6077           rotAxis = (getShapePropertyData(JC.SHAPE_DRAW, "getSpinAxis", data) ? (V3) data[2]
6078               : null);
6079         }
6080         points[nPoints++] = pt1;
6081         break;
6082       case T.spin:
6083         isSpin = true;
6084         continue;
6085       case T.internal:
6086       case T.molecular:
6087         isMolecular = true;
6088         continue;
6089       case T.selected:
6090         isSelected = true;
6091         break;
6092       case T.comma:
6093         continue;
6094       case T.integer:
6095       case T.decimal:
6096         if (isSpin) {
6097           // rotate spin ... [degreesPerSecond]
6098           // rotate spin ... [endDegrees] [degreesPerSecond]
6099           // rotate spin BRANCH <DihedralList> [seconds]
6100           if (degreesPerSecond == PT.FLOAT_MIN_SAFE) {
6101             degreesPerSecond = floatParameter(i);
6102           } else if (endDegrees == Float.MAX_VALUE) {
6103             endDegrees = degreesPerSecond;
6104             degreesPerSecond = floatParameter(i);
6105           } else {
6106             invArg();
6107           }
6108         } else {
6109           // rotate ... [endDegrees]
6110           // rotate ... [endDegrees] [degreesPerSecond]
6111           if (endDegrees == Float.MAX_VALUE) {
6112             endDegrees = floatParameter(i);
6113           } else if (degreesPerSecond == PT.FLOAT_MIN_SAFE) {
6114             degreesPerSecond = floatParameter(i);
6115             isSpin = true;
6116           } else {
6117             invArg();
6118           }
6119         }
6120         if (i == slen - 2 && (tokAt(i + 1) == T.misc || tokAt(i + 1) == T.string)) {
6121           String s = paramAsStr(++i).toLowerCase();
6122           if (s.equals("dps")) {
6123             isDegreesPerSecond = true;
6124           } else if (s.equals("sec")) {
6125             isSeconds = true;
6126           }
6127         }
6128         break;
6129       case T.minus:
6130         direction = -1;
6131         continue;
6132       case T.x:
6133         haveRotation = true;
6134         rotAxis.set(direction, 0, 0);
6135         continue;
6136       case T.y:
6137         haveRotation = true;
6138         rotAxis.set(0, direction, 0);
6139         continue;
6140       case T.z:
6141         haveRotation = true;
6142         rotAxis.set(0, 0, (axesOrientationRasmol && !isMolecular ? -direction
6143             : direction));
6144         continue;
6145 
6146         // 11.6 options
6147 
6148       case T.point4f:
6149       case T.quaternion:
6150       case T.best:
6151         if (tok == T.quaternion)
6152           i++;
6153         haveRotation = true;
6154         if ((q = getQuaternionParameter(i, bsBest, tok == T.best)) != null) {
6155           if (q.q0 == 0)
6156             q.q0 = 1e-10f;
6157           rotAxis.setT(q.getNormal());
6158           endDegrees = q.getTheta(); // returns [0-180]
6159 //          System.out.println(q + " " + rotAxis + " " + endDegrees);
6160 //          if (q.q0 < 0) {
6161             // greater than 180 degrees - we go the other way, in case this is a spin
6162          //   endDegrees = -endDegrees;
6163 //          }
6164         }
6165         break;
6166       case T.plane:
6167         // rotate plane @1 @2 @3
6168         // rotate plane picked
6169         P3[] pts;
6170         int n;
6171         if (paramAsStr(i + 1).equalsIgnoreCase("picked")) {
6172           i++;
6173           @SuppressWarnings("unchecked")
6174           Lst<SV> lst = (Lst<SV>) vwr.getPOrNull("pickedList");
6175           n = lst.size();
6176           if (n < 3)
6177             return;
6178           pts = new P3[3];
6179           for (int j = 0; j < 3; j++)
6180             pts[j] = vwr.ms.getAtomSetCenter(SV.getBitSet(lst.get(n - 3 + j),
6181                 false));
6182         } else if (isArrayParameter(i + 1)) {
6183           pts = getPointArray(++i, -1, false);
6184           i = iToken;
6185         } else {
6186           pts = new P3[3];
6187           for (int j = 0; j < 3; j++) {
6188             pts[j] = centerParameter(++i, null);
6189             i = iToken;
6190           }
6191         }
6192         n = pts.length;
6193         if (n < 3)
6194           return;
6195         q = Quat.getQuaternionFrame(pts[n - 3], pts[n - 2], pts[n - 1]);
6196         q = Quat.new4(1, 0, 0, 0).mulQ(q.inv().div(vwr.tm.getRotationQ()));
6197         rotAxis.setT(q.getNormal());
6198         endDegrees = q.getTheta();
6199         break;
6200       case T.axisangle:
6201         haveRotation = true;
6202         if (isPoint3f(++i)) {
6203           rotAxis.setT(centerParameter(i, null));
6204           break;
6205         }
6206         P4 p4 = getPoint4f(i);
6207         rotAxis.set(p4.x, p4.y, p4.z);
6208         endDegrees = p4.w;
6209         q = Quat.newVA(rotAxis, endDegrees);
6210         break;
6211       case T.branch:
6212         isSelected = true;
6213         isMolecular = true;
6214         haveRotation = true;
6215         if (isArrayParameter(++i)) {
6216           dihedralList = floatParameterSet(i, 6, Integer.MAX_VALUE);
6217           i = iToken;
6218         } else {
6219           int iAtom1 = atomExpressionAt(i).nextSetBit(0);
6220           int iAtom2 = atomExpressionAt(++iToken).nextSetBit(0);
6221           if (iAtom1 < 0 || iAtom2 < 0)
6222             return;
6223           bsAtoms = vwr.getBranchBitSet(iAtom2, iAtom1, true);
6224           points[0] = vwr.ms.at[iAtom1];
6225           points[1] = vwr.ms.at[iAtom2];
6226           nPoints = 2;
6227         }
6228         break;
6229 
6230       // 12.0 options
6231 
6232       case T.translate:
6233         translation = V3.newV(centerParameter(++i, null));
6234         isMolecular = isSelected = true;
6235         break;
6236       case T.helix:
6237         // screw motion, for quaternion-based operations
6238         helicalPath = true;
6239         continue;
6240       case T.symop:
6241         int symop = intParameter(++i);
6242         if (chk)
6243           continue;
6244         Map<String, Object> info = vwr.getSymTemp().getSpaceGroupInfo(vwr.ms,
6245             null, -1, false, null);
6246         Object[] op = (info == null ? null : (Object[]) info.get("operations"));
6247         if (symop == 0 || op == null || op.length < Math.abs(symop))
6248           invArg();
6249         op = (Object[]) op[Math.abs(symop) - 1];
6250         translation = (V3) op[5];
6251         invPoint = (P3) op[6];
6252         points[0] = (P3) op[7];
6253         if (op[8] != null)
6254           rotAxis = (V3) op[8];
6255         endDegrees = ((Integer) op[9]).intValue();
6256         if (symop < 0) {
6257           endDegrees = -endDegrees;
6258           if (translation != null)
6259             translation.scale(-1);
6260         }
6261         if (endDegrees == 0 && points[0] != null) {
6262           // glide plane
6263           rotAxis.normalize();
6264           Measure.getPlaneThroughPoint(points[0], rotAxis, invPlane = new P4());
6265         }
6266         q = Quat.newVA(rotAxis, endDegrees);
6267         nPoints = (points[0] == null ? 0 : 1);
6268         isMolecular = true;
6269         haveRotation = true;
6270         isSelected = true;
6271         continue;
6272       case T.compare:
6273         bsCompare = atomExpressionAt(++i);
6274         ptsA = vwr.ms.getAtomPointVector(bsCompare);
6275         if (ptsA == null) {
6276           iToken = i;
6277           invArg();
6278         }
6279         i = iToken;
6280         ptsB = getPointVector(getToken(++i), i);
6281         if (ptsB == null || ptsA.size() != ptsB.size()) {
6282           iToken = i;
6283           invArg();
6284         }
6285         m4 = new M4();
6286         points[0] = new P3();
6287         nPoints = 1;
6288         Interface.getInterface("javajs.util.Eigen", vwr, "script");
6289         float stddev = (chk ? 0 : Measure.getTransformMatrix4(ptsA, ptsB, m4,
6290             points[0]));
6291         // if the standard deviation is very small, we leave ptsB
6292         // because it will be used to set the absolute final positions
6293         if (stddev > 0.001)
6294           ptsB = null;
6295         //$FALL-THROUGH$
6296       case T.matrix4f:
6297       case T.matrix3f:
6298         haveRotation = true;
6299         m3 = new M3();
6300         if (tok == T.matrix4f) {
6301           is4x4 = true;
6302           m4 = (M4) theToken.value;
6303         }
6304         if (m4 != null) {
6305           // translation and rotation are calculated
6306           translation = new V3();
6307           m4.getTranslation(translation);
6308           m4.getRotationScale(m3);
6309         } else {
6310           m3 = (M3) theToken.value;
6311         }
6312         q = (chk ? new Quat() : Quat.newM(m3));
6313         rotAxis.setT(q.getNormal());
6314         endDegrees = q.getTheta();
6315         isMolecular = true;
6316         break;
6317       default:
6318         invArg();
6319       }
6320       i = iToken;
6321     }
6322     if (chk)
6323       return;
6324 
6325     // process
6326     if (dihedralList != null) {
6327       if (endDegrees != Float.MAX_VALUE) {
6328         isSpin = true;
6329         degreesPerSecond = endDegrees;
6330       }
6331     }
6332 
6333     if (isSelected && bsAtoms == null)
6334       bsAtoms = vwr.bsA();
6335     if (bsCompare != null) {
6336       isSelected = true;
6337       if (bsAtoms == null)
6338         bsAtoms = bsCompare;
6339     }
6340     if (q != null && !isSeconds && !isDegreesPerSecond) {
6341       isDegreesPerSecond = (degreesPerSecond > 0);
6342       isSeconds = !isDegreesPerSecond;
6343     }
6344     float rate = (degreesPerSecond == PT.FLOAT_MIN_SAFE ? 10
6345         : endDegrees == Float.MAX_VALUE ? degreesPerSecond
6346             : isDegreesPerSecond ? degreesPerSecond
6347                 : isSeconds ? (endDegrees < 0 ? -1 : 1) * Math.abs(endDegrees / degreesPerSecond)
6348                     : (degreesPerSecond < 0) == (q == null ? endDegrees > 0
6349                         : true) ?
6350                     // -n means number of seconds, not degreesPerSecond
6351                     -endDegrees / degreesPerSecond
6352                         : degreesPerSecond);
6353     if (q == null && endDegrees < 0 && rate > 0)
6354       rate = -rate;
6355     if (dihedralList != null) {
6356       if (!isSpin) {
6357         vwr.setDihedrals(dihedralList, null, 1);
6358         return;
6359       }
6360       translation = null;
6361     }
6362 
6363     if (q != null) {
6364       // only when there is a translation but not a 4x4 matrix)
6365       // do we set the rotation to be the center of the selected atoms or model
6366       if (nPoints == 0 && translation != null && !is4x4)
6367         points[0] = vwr.ms.getAtomSetCenter(bsAtoms != null ? bsAtoms
6368             : isSelected ? vwr.bsA() : vwr.getAllAtoms());
6369       if (helicalPath && translation != null) {
6370         points[1] = P3.newP(points[0]);
6371         points[1].add(translation);
6372         T3[] ret = Measure.computeHelicalAxis(points[0], points[1], q);
6373         //  new T3[] { pt_a_prime, n, r, P3.new3(theta, pitch, residuesPerTurn), pt_b_prime };
6374         points[0] = (P3) ret[0];
6375         float theta = ((P3) ret[3]).x;
6376         if (theta != 0) {
6377           translation = (V3) ret[1];
6378           rotAxis = V3.newV(translation);
6379           if (theta < 0)
6380             rotAxis.scale(-1);
6381         }
6382         m4 = null;
6383       }
6384       if (isSpin && m4 == null)
6385         m4 = ScriptMathProcessor.getMatrix4f(q.getMatrix(), translation);
6386       if (points[0] != null)
6387         nPoints = 1;
6388     }
6389     if (invPoint != null) {
6390       vwr.invertAtomCoordPt(invPoint, bsAtoms);
6391       if (rotAxis == null)
6392         return;
6393     }
6394     if (invPlane != null) {
6395       vwr.invertAtomCoordPlane(invPlane, bsAtoms);
6396       if (rotAxis == null)
6397         return;
6398     }
6399     // a thread will be required if we are spinning
6400     // UNLESS we are headless,
6401     // in which case we just turn off the spin
6402     boolean requiresThread = (isSpin && (!vwr.headless || endDegrees == Float.MAX_VALUE));
6403     // just turn this into a rotation if we cannot spin
6404     if (isSpin && !requiresThread)
6405       isSpin = false;
6406     if (nPoints < 2 && dihedralList == null) {
6407       if (!isMolecular) {
6408         // fixed-frame rotation
6409         // rotate x 10 # Chime-like
6410         // rotate axisangle {0 1 0} 10
6411         // rotate x 10 (atoms) # point-centered
6412         // rotate x 10 $object # point-centered
6413         if (requiresThread && bsAtoms == null && !useThreads()) {
6414           isSpin = false;
6415           if (endDegrees == Float.MAX_VALUE)
6416             return;
6417         }
6418         if (vwr.rotateAxisAngleAtCenter(this, points[0], rotAxis, rate,
6419             endDegrees, isSpin, bsAtoms)) {
6420           // bsAtoms can be non-null if we have a quaternion
6421           // rotate quaternion {2 3 4 4} {atomno<10}
6422           // a fixed quaternion rotation of a set of atoms
6423           // currently rotateAxisAngleAtCenter cannot return true
6424           // if isSpin is true. But that is what we are working on
6425           // TODO: not exactly clear here if this will work
6426           if (isJS && isSpin && bsAtoms == null && vwr.g.waitForMoveTo
6427               && endDegrees != Float.MAX_VALUE)
6428             throw new ScriptInterruption(this, "rotate", 1);
6429         }
6430         return;
6431       }
6432 
6433       // must be an internal rotation
6434 
6435       if (nPoints == 0)
6436         points[0] = new P3();
6437       // rotate MOLECULAR
6438       // rotate MOLECULAR (atom1)
6439       // rotate MOLECULAR x 10 (atom1)
6440       // rotate axisangle MOLECULAR (atom1)
6441       points[1] = P3.newP(points[0]);
6442       points[1].add(rotAxis);
6443       nPoints = 2;
6444     }
6445     if (nPoints == 0)
6446       points[0] = new P3();
6447     if (nPoints < 2 || points[0].distance(points[1]) == 0) {
6448       points[1] = P3.newP(points[0]);
6449       points[1].y += 1.0;
6450     }
6451     if (endDegrees == Float.MAX_VALUE)
6452       endDegrees = 0;
6453     if (endDegrees != 0 && translation != null && !haveRotation)
6454       translation.scale(endDegrees / translation.length());
6455     if (isSpin && translation != null
6456         && (endDegrees == 0 || degreesPerSecond == 0)) {
6457       // need a token rotation
6458       endDegrees = 0.01f;
6459       rate = (degreesPerSecond == PT.FLOAT_MIN_SAFE ? 0.01f
6460           : degreesPerSecond < 0 ?
6461           // -n means number of seconds, not degreesPerSecond
6462           -endDegrees / degreesPerSecond
6463               : degreesPerSecond * 0.01f / translation.length());
6464       degreesPerSecond = 0.01f;
6465     }
6466     if (bsAtoms != null && isSpin && ptsB == null && m4 != null) {
6467       ptsA = vwr.ms.getAtomPointVector(bsAtoms);
6468       // note that this m4 is NOT through
6469       ptsB = Measure.transformPoints(ptsA, m4, points[0]);
6470     }
6471     if (bsAtoms != null && !isSpin && ptsB != null) {
6472       vwr.setAtomCoords(bsAtoms, T.xyz, ptsB);
6473     } else {
6474       if (requiresThread && !useThreads())
6475         return;
6476       if (vwr.rotateAboutPointsInternal(this, points[0], points[1], rate,
6477           endDegrees, isSpin, bsAtoms, translation, ptsB, dihedralList,
6478           is4x4 ? m4 : null)
6479           && isJS && isSpin)
6480         throw new ScriptInterruption(this, "rotate", 1);
6481     }
6482   }
6483 
cmdRestore()6484   private void cmdRestore() throws ScriptException {
6485     // restore orientation name time
6486     if (slen > 1) {
6487       String saveName = optParameterAsString(2);
6488       int tok = tokAt(1);
6489       switch (tok) {
6490       case T.unitcell:
6491         if (!chk)
6492           setModelCagePts(-1, null, null);
6493         return;
6494       case T.orientation:
6495       case T.rotation:
6496       case T.scene:
6497         float floatSecondsTotal = (slen > 3 ? floatParameter(3) : 0);
6498         if (floatSecondsTotal < 0)
6499           invArg();
6500         if (chk)
6501           return;
6502         String type = "";
6503         switch (tok) {
6504         case T.orientation:
6505           type = "Orientation";
6506           vwr.stm.restoreOrientation(saveName, floatSecondsTotal, true);
6507           break;
6508         case T.rotation:
6509           type = "Rotation";
6510           vwr.stm.restoreOrientation(saveName, floatSecondsTotal, false);
6511           break;
6512         case T.scene:
6513           type = "Scene";
6514           vwr.stm.restoreScene(saveName, floatSecondsTotal);
6515           break;
6516         }
6517         if (isJS && floatSecondsTotal > 0 && vwr.g.waitForMoveTo)
6518           throw new ScriptInterruption(this, "restore" + type, 1);
6519         return;
6520       }
6521       checkLength23();
6522       switch (tok) {
6523       case T.bonds:
6524         if (!chk)
6525           vwr.stm.restoreBonds(saveName);
6526         return;
6527       case T.context:
6528         if (chk)
6529           return;
6530         ScriptContext sc = (ScriptContext) vwr.stm.getContext(saveName);
6531         if (sc != null) {
6532           restoreScriptContext(sc, true, false, false);
6533           if (thisContext != null) {
6534             thisContext.setMustResume();
6535             mustResumeEval = true;
6536             tQuiet = true;
6537           }
6538         }
6539         return;
6540       case T.coord:
6541         if (chk)
6542           return;
6543         String script = vwr.stm.getSavedCoordinates(saveName);
6544         if (script == null)
6545           invArg();
6546         runScript(script);
6547         vwr.checkCoordinatesChanged();
6548         return;
6549       case T.selection:
6550         if (!chk)
6551           vwr.stm.restoreSelection(saveName);
6552         return;
6553       case T.state:
6554         if (chk)
6555           return;
6556         String state = vwr.stm.getSavedState(saveName);
6557         if (state == null)
6558           invArg();
6559         runScript(state);
6560         return;
6561       case T.structure:
6562         if (chk)
6563           return;
6564         String shape = vwr.stm.getSavedStructure(saveName);
6565         if (shape == null)
6566           invArg();
6567         runScript(shape);
6568         return;
6569       }
6570     }
6571     errorStr2(ERROR_what, "RESTORE", saveList);
6572   }
6573 
cmdSave()6574   private void cmdSave() throws ScriptException {
6575     if (slen > 1) {
6576       String saveName = optParameterAsString(2);
6577       switch (tokAt(1)) {
6578       case T.bonds:
6579         if (!chk)
6580           vwr.stm.saveBonds(saveName);
6581         return;
6582       case T.context:
6583         if (!chk)
6584           saveContext(saveName);
6585         return;
6586       case T.coord:
6587         if (!chk)
6588           vwr.stm.saveCoordinates(saveName, vwr.bsA());
6589         return;
6590       case T.orientation:
6591       case T.rotation:
6592         if (!chk)
6593           vwr.stm.saveOrientation(saveName, null);
6594         return;
6595       case T.selection:
6596         if (!chk) {
6597           vwr.stm.saveSelection(saveName, vwr.bsA());
6598           vwr.stm.restoreSelection(saveName); // just to register the # of
6599         }
6600         return;
6601       case T.state:
6602         if (!chk)
6603           vwr.stm.saveState(saveName);
6604         return;
6605       case T.structure:
6606         if (!chk)
6607           vwr.stm.saveStructure(saveName);
6608         return;
6609       }
6610     }
6611     errorStr2(ERROR_what, "SAVE", saveList);
6612   }
6613 
cmdScript(int tok, String filename, String theScript)6614   public void cmdScript(int tok, String filename, String theScript)
6615       throws ScriptException {
6616     if (tok == T.javascript) {
6617       checkLength(2);
6618       if (!chk)
6619         vwr.jsEval(paramAsStr(1));
6620       return;
6621     }
6622     boolean loadCheck = true;
6623     boolean isCheck = false;
6624     boolean doStep = false;
6625     boolean isAsync = vwr.async;
6626     int lineNumber = 0;
6627     int pc = 0;
6628     int lineEnd = 0;
6629     int pcEnd = 0;
6630     int i = 1;
6631     String localPath = null;
6632     String remotePath = null;
6633     String scriptPath = null;
6634     Lst<SV> params = null;
6635     if (tok == T.macro) {
6636       i = -2;
6637     }
6638     if (filename == null && theScript == null) {
6639       tok = tokAt(i);
6640       if (tok != T.string)
6641         error(ERROR_filenameExpected);
6642       filename = paramAsStr(i);
6643 
6644       if (filename.equalsIgnoreCase("async")) {
6645         isAsync = true;
6646         filename = paramAsStr(++i);
6647       }
6648       if (filename.equalsIgnoreCase("applet")) {
6649         filename = null;
6650         // script APPLET x "....."
6651         String appID = paramAsStr(++i);
6652         theScript = parameterExpressionString(++i, 0); // had _script variable??
6653         checkLast(iToken);
6654         if (chk)
6655           return;
6656         if (appID.length() == 0 || appID.equals("all"))
6657           appID = "*";
6658         if (!appID.equals(".")) {
6659           vwr.jsEval(appID + "\1" + theScript);
6660           if (!appID.equals("*"))
6661             return;
6662         }
6663       } else {
6664         tok = tokAt(slen - 1);
6665         doStep = (tok == T.step);
6666         if (filename.equalsIgnoreCase("inline")) {
6667           filename = null;
6668           theScript = parameterExpressionString(++i, (doStep ? slen - 1 : 0));
6669           i = iToken;
6670         } else {
6671           while (filename.equalsIgnoreCase("localPath")
6672               || filename.equalsIgnoreCase("remotePath")
6673               || filename.equalsIgnoreCase("scriptPath")) {
6674             if (filename.equalsIgnoreCase("localPath"))
6675               localPath = paramAsStr(++i);
6676             else if (filename.equalsIgnoreCase("scriptPath"))
6677               scriptPath = paramAsStr(++i);
6678             else
6679               remotePath = paramAsStr(++i);
6680             filename = paramAsStr(++i);
6681           }
6682           if (filename.startsWith("spt::"))
6683             filename = filename.substring(5);
6684           filename = checkFileExists("SCRIPT_", isAsync, filename, i, true);
6685         }
6686         if ((tok = tokAt(++i)) == T.check) {
6687           isCheck = true;
6688           tok = tokAt(++i);
6689         }
6690         if (tok == T.noload) {
6691           loadCheck = false;
6692           tok = tokAt(++i);
6693         }
6694         if (tok == T.line || tok == T.lines) {
6695           i++;
6696           lineEnd = lineNumber = Math.max(intParameter(i++), 0);
6697           if (checkToken(i)) {
6698             if (getToken(i).tok == T.minus)
6699               lineEnd = (checkToken(++i) ? intParameter(i++) : 0);
6700             else
6701               lineEnd = -intParameter(i++);
6702             if (lineEnd <= 0)
6703               invArg();
6704           }
6705         } else if (tok == T.command || tok == T.commands) {
6706           i++;
6707           pc = Math.max(intParameter(i++) - 1, 0);
6708           pcEnd = pc + 1;
6709           if (checkToken(i)) {
6710             if (getToken(i).tok == T.minus)
6711               pcEnd = (checkToken(++i) ? intParameter(i++) : 0);
6712             else
6713               pcEnd = -intParameter(i++);
6714             if (pcEnd <= 0)
6715               invArg();
6716           }
6717         }
6718         i = -i;
6719       }
6720     } else if (filename != null && isAsync) {
6721       filename = checkFileExists("SCRIPT_", isAsync, filename, i, true);
6722     }
6723     if (i < 0) {
6724       if (tokAt(i = -i) == T.leftparen) {
6725         params = parameterExpressionList(i, -1, false);
6726         i = iToken + 1;
6727       }
6728       checkLength(doStep ? i + 1 : i);
6729     }
6730 
6731     // processing
6732 
6733     if (chk && !isCmdLine_c_or_C_Option)
6734       return;
6735     if (isCmdLine_c_or_C_Option)
6736       isCheck = true;
6737     boolean wasSyntaxCheck = chk;
6738     boolean wasScriptCheck = isCmdLine_c_or_C_Option;
6739     if (isCheck)
6740       chk = isCmdLine_c_or_C_Option = true;
6741     pushContext(null, "SCRIPT");
6742     contextPath += " >> " + filename;
6743     if (theScript == null)
6744       theScript = getScriptFileInternal(filename, localPath, remotePath, scriptPath);
6745     if (compileScript(filename, theScript, filename != null && debugScript)) {
6746       this.pcEnd = pcEnd;
6747       this.lineEnd = lineEnd;
6748       while (pc < lineNumbers.length && lineNumbers[pc] < lineNumber)
6749         pc++;
6750       this.pc = pc;
6751       boolean saveLoadCheck = isCmdLine_C_Option;
6752       isCmdLine_C_Option &= loadCheck;
6753       executionStepping |= doStep;
6754 
6755       if (contextVariables == null)
6756         contextVariables = new Hashtable<String, SV>();
6757       contextVariables.put("_arguments",
6758           (params == null ? SV.getVariableAI(new int[] {})
6759               : SV.getVariableList(params)));
6760       contextVariables.put("_argcount",
6761           SV.newI(params == null ? 0 : params.size()));
6762 
6763       if (isCheck)
6764         listCommands = true;
6765       boolean timeMsg = vwr.getBoolean(T.showtiming);
6766       if (timeMsg)
6767         Logger.startTimer("script");
6768       privateFuncs = null;
6769       dispatchCommands(false, false, false);
6770       if (isStateScript)
6771         ScriptManager.setStateScriptVersion(vwr, null);
6772       if (timeMsg)
6773         showString(Logger.getTimerMsg("script", 0));
6774       isCmdLine_C_Option = saveLoadCheck;
6775       popContext(false, false);
6776     } else {
6777       Logger.error(GT.$("script ERROR: ") + errorMessage);
6778       popContext(false, false);
6779       if (wasScriptCheck) {
6780         setErrorMessage(null);
6781       } else {
6782         evalError(null, null);
6783       }
6784     }
6785 
6786     chk = wasSyntaxCheck;
6787     isCmdLine_c_or_C_Option = wasScriptCheck;
6788   }
6789 
cmdSelect(int i)6790   private void cmdSelect(int i) throws ScriptException {
6791     // NOTE this is called by restrict()
6792     if (slen == 1) {
6793       vwr.select(null, false, 0, !doReport());
6794       return;
6795     }
6796     if (slen == 2 && tokAt(1) == T.only)
6797       return; // coming from "cartoon only"
6798     // select beginexpr none endexpr
6799     int tok = tokAt(2);
6800     vwr.slm.noneSelected = Boolean.valueOf(slen == 4 && tok == T.none);
6801     BS bs = null;
6802     switch (tok) {
6803     case T.bitset:
6804       // select beginexpr bonds ( {...} ) endex pr
6805       if (getToken(2).value instanceof BondSet || tokAt(2) == T.bonds
6806           && getToken(3).tok == T.bitset) {
6807         if (slen != iToken + 2)
6808           invArg();
6809         if (!chk)
6810           vwr.selectBonds((BS) theToken.value);
6811         return;
6812       }
6813       break;
6814     case T.measure:
6815     case T.bonds:
6816       if (slen == 5 && tokAt(3) == T.bitset) {
6817         bs = (BS) getToken(3).value;
6818         iToken++;
6819       } else if (isArrayParameter(4)) {
6820         bs = new BS();
6821         // 0-based here, to conform with getProperty measurementInfo.index
6822         int[] a = (int[]) expandFloatArray(floatParameterSet(4, 0,
6823             Integer.MAX_VALUE), 0, false);
6824         for (int ii = a.length; --ii >= 0;)
6825           if (a[ii] >= 0)
6826             bs.set(a[ii]);
6827       }
6828       checkLast(iToken);
6829       if (chk)
6830         return;
6831       if (bs == null)
6832         invArg();
6833       if (tok == T.measure)
6834         setShapeProperty(JC.SHAPE_MEASURES, "select", bs);
6835       else
6836         vwr.selectBonds(bs);
6837       return;
6838     }
6839 
6840     int addRemove = 0;
6841     boolean isGroup = false;
6842     if (getToken(1).intValue == 0 && theTok != T.off) {
6843       Object v = parameterExpressionToken(0).value;
6844       if (!(v instanceof BS))
6845         invArg();
6846       checkLast(iToken);
6847       bs = (BS) v;
6848     } else {
6849       tok = tokAt(i);
6850       switch (tok) {
6851       case T.on:
6852       case T.off:
6853         if (!chk)
6854           vwr.setSelectionHalosEnabled(tok == T.on);
6855         tok = tokAt(++i);
6856         if (tok == T.nada)
6857           return;
6858         break;
6859       }
6860       switch (tok) {
6861       case T.add:
6862       case T.remove:
6863         addRemove = tok;
6864         tok = tokAt(++i);
6865       }
6866       isGroup = (tok == T.group);
6867       if (isGroup)
6868         tok = tokAt(++i);
6869       bs = atomExpressionAt(i);
6870     }
6871     if (chk)
6872       return;
6873     if (isBondSet) {
6874       vwr.selectBonds(bs);
6875     } else {
6876       if (bs.length() > vwr.ms.ac) {
6877         BS bs1 = vwr.getAllAtoms();
6878         bs1.and(bs);
6879         bs = bs1;
6880       }
6881       vwr.select(bs, isGroup, addRemove, !doReport());
6882     }
6883   }
6884 
cmdSelectionHalos(int pt)6885   private void cmdSelectionHalos(int pt) throws ScriptException {
6886     boolean showHalo = false;
6887     switch (pt == slen ? T.on : getToken(pt).tok) {
6888     case T.on:
6889     case T.selected:
6890       showHalo = true;
6891       //$FALL-THROUGH$
6892     case T.off:
6893     case T.none:
6894     case T.normal:
6895       setBooleanProperty("selectionHalos", showHalo);
6896       break;
6897     default:
6898       invArg();
6899     }
6900   }
6901 
cmdSet()6902   private void cmdSet() throws ScriptException {
6903     /*
6904      * The SET command now allows only the following:
6905      *
6906      * SET SET xxx? SET [valid Jmol Token.setparam keyword] SET labelxxxx SET
6907      * xxxxCallback
6908      *
6909      * All other variables must be assigned using
6910      *
6911      * x = ....
6912      *
6913      * The processing goes as follows:
6914      *
6915      * check for SET check for SET xx? check for SET xxxx where xxxx is a
6916      * command --- deprecated (all other settings may alternatively start with x
6917      * = y) check for SET xxxx where xxxx requires special checking (all other
6918      * settings may alternatively start with x = (math expression) check for
6919      * context variables var x = ... check for deprecated SET words such as
6920      * "radius"
6921      */
6922     if (slen == 1) {
6923       showString(vwr.getAllSettings(null));
6924       return;
6925     }
6926     boolean isJmolSet = (paramAsStr(0).equals("set"));
6927     String key = optParameterAsString(1);
6928     if (isJmolSet && slen == 2 && key.indexOf("?") >= 0) {
6929       showString(vwr.getAllSettings(key.substring(0, key.indexOf("?"))));
6930       return;
6931     }
6932     int tok = getToken(1).tok;
6933 
6934     int newTok = 0;
6935     String sval;
6936     int ival = Integer.MAX_VALUE;
6937     boolean b;
6938     P3 pt;
6939 
6940     boolean showing = (!chk && doReport()
6941         && !((String) st[0].value).equals("var"));
6942 
6943     // THESE FIRST ARE DEPRECATED AND HAVE THEIR OWN COMMAND
6944     // anything in this block MUST RETURN
6945 
6946     switch (tok) {
6947     case T.historylevel:
6948     case T.iskiosk:
6949     case T.saveproteinstructurestate:
6950     case T.showkeystrokes:
6951     case T.testflag1:
6952     case T.testflag2:
6953     case T.testflag3:
6954     case T.testflag4:
6955     case T.useminimizationthread:
6956       // these might be set in older state scripts, but they should not have been there
6957       if (isStateScript)
6958         return;
6959       break;
6960     case T.axes:
6961       cmdAxes(2);
6962       return;
6963     case T.background:
6964       cmdBackground(2);
6965       return;
6966     case T.boundbox:
6967       cmdBoundbox(2);
6968       return;
6969     case T.frank:
6970       cmdFrank(2);
6971       return;
6972     case T.history:
6973       cmdHistory(2);
6974       return;
6975     case T.label:
6976       cmdLabel(2, null);
6977       return;
6978     case T.unitcell:
6979       cmdUnitcell(2);
6980       return;
6981     case T.highlight:
6982       sm.loadShape(JC.SHAPE_HALOS);
6983       setShapeProperty(JC.SHAPE_HALOS, "highlight",
6984           (tokAt(2) == T.off ? null : atomExpressionAt(2)));
6985       return;
6986     case T.display:// deprecated
6987     case T.selectionhalos:
6988       cmdSelectionHalos(2);
6989       return;
6990     case T.timeout:
6991       cmdTimeout(2);
6992       return;
6993 
6994     // THESE HAVE MULTIPLE CONTEXTS AND
6995     // SO DO NOT ALLOW CALCULATIONS xxx = a + b...
6996     // and are thus "setparam" only
6997     // anything in this block MUST RETURN
6998     case T.window:
6999       Object o = (isArrayParameter(2) ? floatParameterSet(2, 2, 2)
7000           : tokAt(2) == T.integer
7001               ? new float[] { intParameter(2), intParameter(3) }
7002               : stringParameter(2));
7003       checkLast(iToken);
7004       if (chk)
7005         return;
7006       if (o instanceof String) {
7007         if (vwr.fm.loadImage(o, "\0windowImage", !useThreads()))
7008           throw new ScriptInterruption(this, "windowImage", 1);
7009       } else {
7010         vwr.setWindowDimensions((float[]) o);
7011       }
7012       return;
7013     case T.structure:
7014       STR type = STR.getProteinStructureType(paramAsStr(2));
7015       if (type == STR.NOT)
7016         invArg();
7017       float[] data = floatParameterSet(3, 0, Integer.MAX_VALUE);
7018       if (data.length % 4 != 0)
7019         invArg();
7020       vwr.setStructureList(data, type);
7021       checkLast(iToken);
7022       return;
7023     case T.axescolor:
7024       ival = getArgbParam(2);
7025       if (!chk)
7026         setObjectArgb("axes", ival);
7027       return;
7028     case T.bondmode:
7029       b = false;
7030       switch (getToken(checkLast(2)).tok) {
7031       case T.opAnd:
7032         break;
7033       case T.opOr:
7034         b = true;
7035         break;
7036       default:
7037         invArg();
7038       }
7039       setBooleanProperty("bondModeOr", b);
7040       return;
7041     case T.debug:
7042     case T.debughigh:
7043       if (chk)
7044         return;
7045       int iLevel = (tokAt(2) == T.off
7046           || tokAt(2) == T.integer && intParameter(2) == 0 ? 4
7047               : (tok == T.debughigh ? 6 : 5));
7048       Logger.setLogLevel(iLevel);
7049       setIntProperty("logLevel", iLevel);
7050       if (iLevel == 4) {
7051         vwr.setDebugScript(false);
7052         if (showing)
7053           vwr.showParameter("debugScript", true, 80);
7054       }
7055       setDebugging();
7056       if (showing)
7057         vwr.showParameter("logLevel", true, 80);
7058       return;
7059     case T.echo:
7060       cmdSetEcho();
7061       return;
7062     case T.fontsize:
7063       cmdFont(JC.SHAPE_LABELS, checkLength23() == 2 ? 0 : floatParameter(2));
7064       return;
7065     case T.hbond:
7066       boolean bool = false;
7067       switch (tokAt(checkLast(2))) {
7068       case T.backbone:
7069         bool = true;
7070         //$FALL-THROUGH$
7071       case T.sidechain:
7072         setBooleanProperty("hbondsBackbone", bool);
7073         break;
7074       case T.solid:
7075         bool = true;
7076         //$FALL-THROUGH$
7077       case T.dotted:
7078         setBooleanProperty("hbondsSolid", bool);
7079         break;
7080       default:
7081         invArg();
7082       }
7083       return;
7084     case T.measure:
7085     case T.measurements:
7086       // on off here incompatible with "monitor on/off" so this is just a SET
7087       // option.
7088       switch (tok = tokAt(checkLast(2))) {
7089       case T.on:
7090       case T.off:
7091         setBooleanProperty("measurementlabels", tok == T.on);
7092         return;
7093       case T.dotted:
7094       case T.integer:
7095       case T.decimal:
7096         vwr.shm.loadShape(JC.SHAPE_MEASURES);
7097         int mad10 = getSetAxesTypeMad10(2);
7098         if (mad10 != Integer.MAX_VALUE)
7099           setShapeSizeBs(JC.SHAPE_MEASURES,
7100               tok == T.decimal ? mad10 / 10 : mad10, null);
7101         return;
7102       }
7103       setUnits(paramAsStr(2), T.measurementunits);
7104       return;
7105     case T.ssbond: // ssBondsBackbone
7106       b = false;
7107       // shapeManager.loadShape(JmolConstants.SHAPE_SSSTICKS);
7108       switch (tokAt(checkLast(2))) {
7109       case T.backbone:
7110         b = true;
7111         break;
7112       case T.sidechain:
7113         break;
7114       default:
7115         invArg();
7116       }
7117       setBooleanProperty("ssbondsBackbone", b);
7118       return;
7119     case T.togglelabel:
7120       cmdSetLabel("toggle");
7121       return;
7122     case T.usercolorscheme:
7123       Lst<Integer> v = new Lst<Integer>();
7124       for (int i = 2; i < slen; i++) {
7125         int argb = getArgbParam(i);
7126         v.addLast(Integer.valueOf(argb));
7127         i = iToken;
7128       }
7129       if (chk)
7130         return;
7131       int n = v.size();
7132       int[] scale = new int[n];
7133       for (int i = n; --i >= 0;)
7134         scale[i] = v.get(i).intValue();
7135       vwr.cm.ce.setUserScale(scale);
7136       return;
7137     case T.zslab:
7138       // sets zSlab either based on a percent value or an atom position
7139       if (isFloatParameter(2)) {
7140         checkLength(3);
7141         setIntProperty("zSlab", (int) floatParameter(2));
7142         pt = null;
7143       } else {
7144         if (!isCenterParameter(2))
7145           invArg();
7146         pt = centerParameter(2, null);
7147         checkLength(iToken + 1);
7148       }
7149       if (!chk)
7150         vwr.tm.zSlabPoint = (pt == null ? null : P3.newP(pt));
7151       return;
7152     }
7153 
7154     boolean justShow = true;
7155 
7156     // these next may just report a value
7157     // require special checks
7158     // math expressions are allowed in most cases using xxxSetting(...)
7159 
7160     switch (tok) {
7161     case T.backgroundmodel:
7162       if (slen > 2) {
7163         String modelDotted = getSettingStr(2, false);
7164         int modelNumber;
7165         boolean useModelNumber = false;
7166         if (modelDotted.indexOf(".") < 0) {
7167           modelNumber = PT.parseInt(modelDotted);
7168           useModelNumber = true;
7169         } else {
7170           modelNumber = getFloatEncodedInt(modelDotted);
7171         }
7172         if (chk)
7173           return;
7174         int modelIndex = vwr.ms.getModelNumberIndex(modelNumber, useModelNumber,
7175             true);
7176         vwr.setBackgroundModelIndex(modelIndex);
7177         return;
7178       }
7179       break;
7180     case T.vanderwaals:
7181       if (chk)
7182         return;
7183       vwr.setAtomProperty(vwr.getAllAtoms(), T.vanderwaals, -1, Float.NaN, null,
7184           null, null);
7185       if (slen > 2 && "probe".equalsIgnoreCase(getSettingStr(2, false))) {
7186         runScript(Elements.VdwPROBE);
7187         return;
7188       }
7189       newTok = T.defaultvdw;
7190       //$FALL-THROUGH$
7191     case T.defaultvdw:
7192       // allows unquoted string for known vdw type
7193       if (slen > 2) {
7194         sval = paramAsStr(2);
7195         if (slen == 3 && VDW.getVdwType(sval) == null
7196             && VDW.getVdwType(sval = getSettingStr(2, false)) == null)
7197           invArg();
7198         setStringProperty(key, sval);
7199       }
7200       break;
7201     case T.defaultlattice:
7202       // in very early versions of Jmol we might
7203       // have accepted string "{1.0,2.0,3.0}" here,
7204       // but that is at least before 11.8.
7205       // shouldn't be saving this in the state anyway.
7206 
7207       if (slen > 2) {
7208         SV var = parameterExpressionToken(2);
7209         if (var.tok == T.point3f)
7210           pt = (P3) var.value;
7211         else {
7212           pt = new P3();
7213           int ijk = var.asInt();
7214           if (ijk >= 100)
7215             SimpleUnitCell.ijkToPoint3f(ijk, pt, -1, 0);
7216         }
7217         if (!chk)
7218           vwr.setDefaultLattice(pt);
7219       }
7220       break;
7221     case T.defaults:
7222     case T.defaultcolorscheme:
7223       // allows unquoted "jmol" or "rasmol"
7224       if (slen > 2) {
7225         if ((theTok = tokAt(2)) == T.jmol || theTok == T.rasmol) {
7226           sval = paramAsStr(checkLast(2));
7227         } else {
7228           sval = getSettingStr(2, false);
7229         }
7230         setStringProperty(key, sval);
7231       }
7232       break;
7233     case T.formalcharge:
7234       ival = getSettingInt(2);
7235       if (ival == Integer.MIN_VALUE)
7236         invArg();
7237       if (!chk)
7238         vwr.ms.setFormalCharges(vwr.bsA(), ival);
7239       return;
7240     case T.language:
7241       // language can be used without quotes in a SET context
7242       // set language en
7243       if (slen > 2)
7244         setStringProperty(key, getSettingStr(2, isJmolSet));
7245       break;
7246     case T.measurementunits:
7247     case T.energyunits:
7248       if (slen > 2)
7249         setUnits(getSettingStr(2, isJmolSet), tok);
7250       break;
7251     case T.picking:
7252       if (!chk)
7253         vwr.setPicked(-1, false);
7254       if (slen > 2) {
7255         cmdSetPicking();
7256         return;
7257       }
7258       break;
7259     case T.pickingstyle:
7260       if (slen > 2) {
7261         cmdSetPickingStyle();
7262         return;
7263       }
7264       break;
7265     case T.property: // compiler may give different values to this token
7266       // set property_xxxx will be handled in setVariable
7267       break;
7268     case T.specular:
7269       ival = getSettingInt(2);
7270       if (ival == Integer.MIN_VALUE || ival == 0 || ival == 1) {
7271         justShow = false;
7272         break;
7273       }
7274       tok = T.specularpercent;
7275       key = "specularPercent";
7276       setIntProperty(key, ival);
7277       break;
7278     case T.strands:
7279       tok = T.strandcount;
7280       key = "strandCount";
7281       setIntProperty(key, getSettingInt(2));
7282       break;
7283     default:
7284       justShow = false;
7285     }
7286 
7287     if (justShow && !showing)
7288       return;
7289 
7290     // var xxxx = xxx can supercede set xxxx
7291 
7292     boolean isContextVariable = (!justShow && !isJmolSet
7293         && getContextVariableAsVariable(key, false) != null);
7294 
7295     if (!justShow && !isContextVariable) {
7296 
7297       // THESE NEXT are deprecated:
7298 
7299       switch (tok) {
7300       case T.bonds:
7301         newTok = T.showmultiplebonds;
7302         break;
7303       case T.hetero:
7304         newTok = T.selecthetero;
7305         break;
7306       case T.hydrogen:
7307         newTok = T.selecthydrogen;
7308         break;
7309       case T.measurementnumbers:
7310         newTok = T.measurementlabels;
7311         break;
7312       case T.radius:
7313         newTok = T.solventproberadius;
7314         setFloatProperty("solventProbeRadius", getSettingFloat(2));
7315         justShow = true;
7316         break;
7317       case T.scale3d:
7318         newTok = T.scaleangstromsperinch;
7319         break;
7320       case T.solvent:
7321         newTok = T.solventprobe;
7322         break;
7323       case T.color:
7324         newTok = T.defaultcolorscheme;
7325         break;
7326       case T.spin:
7327         sval = paramAsStr(2).toLowerCase();
7328         switch ("x;y;z;fps;".indexOf(sval + ";")) {
7329         case 0:
7330           newTok = T.spinx;
7331           break;
7332         case 2:
7333           newTok = T.spiny;
7334           break;
7335         case 4:
7336           newTok = T.spinz;
7337           break;
7338         case 6:
7339           newTok = T.spinfps;
7340           break;
7341         default:
7342           errorStr2(ERROR_unrecognizedParameter, "set SPIN ", sval);
7343         }
7344         if (!chk)
7345           vwr.setSpin(sval, (int) floatParameter(checkLast(3)));
7346         justShow = true;
7347         break;
7348       }
7349     }
7350 
7351     if (newTok != 0) {
7352       key = T.nameOf(tok = newTok);
7353     } else if (!justShow && !isContextVariable) {
7354       // special cases must be checked
7355       if (key.length() == 0 || key.charAt(0) == '_' && tokAt(2) != T.leftsquare) // these cannot be set by user
7356         error(ERROR_cannotSet);
7357 
7358       // these next are not reported and do not allow calculation xxxx = a + b
7359 
7360       String lckey = key.toLowerCase();
7361       if (lckey.indexOf("label") == 0 && PT.isOneOf(lckey.substring(5),
7362           ";front;group;atom;offset;offsetexact;offsetabsolute;pointer;alignment;toggle;scalereference;for;")) {
7363         if (cmdSetLabel(lckey.substring(5)))
7364           return;
7365       }
7366       if (isJmolSet && lckey.indexOf("shift_") == 0) {
7367         float f = floatParameter(2);
7368         checkLength(3);
7369         if (!chk)
7370           vwr.getNMRCalculation().setChemicalShiftReference(lckey.substring(6),
7371               f);
7372         return;
7373       }
7374       if (lckey.endsWith("callback"))
7375         tok = T.setparam;
7376     }
7377     if (isJmolSet && !T.tokAttr(tok, T.setparam)) {
7378       iToken = 1;
7379       if (!isStateScript)
7380         errorStr2(ERROR_unrecognizedParameter, "SET", key);
7381       warning(ERROR_unrecognizedParameterWarning, "SET", key);
7382     }
7383 
7384     if (!justShow && isJmolSet) {
7385       // simple cases
7386       switch (slen) {
7387       case 2:
7388         // set XXXX;
7389         // too bad we allow this...
7390         setBooleanProperty(key, true);
7391         justShow = true;
7392         break;
7393       case 3:
7394         // set XXXX val;
7395         // check for int and NONE just in case
7396         if (ival != Integer.MAX_VALUE) {
7397           // keep it simple
7398           setIntProperty(key, ival);
7399           justShow = true;
7400         }
7401         break;
7402       }
7403     }
7404 
7405     if (!justShow && !isJmolSet && tokAt(2) == T.none) {
7406       if (!chk)
7407         vwr.removeUserVariable(key.toLowerCase());
7408       justShow = true;
7409     }
7410     if (!justShow) {
7411       setVariable(1, 0, key, true);
7412       if (!isJmolSet)
7413         return;
7414     }
7415     if (showing)
7416       vwr.showParameter(key, true, 80);
7417   }
7418 
cmdSetEcho()7419   private void cmdSetEcho() throws ScriptException {
7420     String propertyName = null;
7421     Object propertyValue = null;
7422     String id = null;
7423     boolean echoShapeActive = true;
7424     // set echo xxx
7425     int pt = 2;
7426 
7427     // check for ID name or just name
7428     // also check simple OFF, NONE
7429     switch (getToken(2).tok) {
7430     case T.off:
7431       id = propertyName = "allOff";
7432       checkLength(++pt);
7433       break;
7434     case T.none:
7435       echoShapeActive = false;
7436       //$FALL-THROUGH$
7437     case T.all:
7438       // all and none get NO additional parameters;
7439       id = paramAsStr(2);
7440       checkLength(++pt);
7441       break;
7442     case T.left:
7443     case T.center:
7444     case T.right:
7445     case T.top:
7446     case T.middle:
7447     case T.bottom:
7448     case T.identifier:
7449     case T.string:
7450     case T.id:
7451       if (theTok == T.id)
7452         pt++;
7453       id = paramAsStr(pt++);
7454       break;
7455     }
7456 
7457     if (!chk) {
7458       vwr.ms.setEchoStateActive(echoShapeActive);
7459       sm.loadShape(JC.SHAPE_ECHO);
7460       if (id != null)
7461         setShapeProperty(JC.SHAPE_ECHO, propertyName == null ? "target"
7462             : propertyName, id);
7463     }
7464 
7465     if (pt < slen) {
7466       // set echo name xxx
7467       // pt is usually 3, but could be 4 if ID used
7468       switch (getToken(pt++).tok) {
7469       case T.align:
7470         propertyName = "align";
7471         switch (getToken(pt).tok) {
7472         case T.left:
7473         case T.right:
7474         case T.center:
7475           propertyValue = paramAsStr(pt++);
7476           break;
7477         default:
7478           invArg();
7479         }
7480         break;
7481       case T.center:
7482       case T.left:
7483       case T.right:
7484         propertyName = "align";
7485         propertyValue = paramAsStr(pt - 1);
7486         break;
7487       case T.depth:
7488         propertyName = "%zpos";
7489         propertyValue = Integer.valueOf((int) floatParameter(pt++));
7490         break;
7491       case T.display:
7492       case T.displayed:
7493       case T.on:
7494         propertyName = "hidden";
7495         propertyValue = Boolean.FALSE;
7496         break;
7497       case T.hide:
7498       case T.hidden:
7499         propertyName = "hidden";
7500         propertyValue = Boolean.TRUE;
7501         break;
7502       case T.model:
7503         int modelIndex = (chk ? 0 : modelNumberParameter(pt++));
7504         if (modelIndex >= vwr.ms.mc)
7505           invArg();
7506         propertyName = "model";
7507         propertyValue = Integer.valueOf(modelIndex);
7508         break;
7509       case T.leftsquare:
7510       case T.spacebeforesquare:
7511         // [ x y ] with or without %
7512         propertyName = "xypos";
7513         propertyValue = xypParameter(--pt);
7514         if (propertyValue == null)
7515           invArg();
7516         pt = iToken + 1;
7517         break;
7518       case T.integer:
7519         // x y without brackets
7520         int posx = intParameter(pt - 1);
7521         String namex = "xpos";
7522         if (tokAt(pt) == T.percent) {
7523           namex = "%xpos";
7524           pt++;
7525         }
7526         propertyName = "ypos";
7527         propertyValue = Integer.valueOf(intParameter(pt++));
7528         if (tokAt(pt) == T.percent) {
7529           propertyName = "%ypos";
7530           pt++;
7531         }
7532         checkLength(pt);
7533         setShapeProperty(JC.SHAPE_ECHO, namex, Integer.valueOf(posx));
7534         break;
7535       case T.offset:
7536         propertyName = "offset";
7537         if (isPoint3f(pt)) {
7538           P3 pt3 = getPoint3f(pt, false, true);
7539           // minus 1 here means from Jmol, not from PyMOL
7540           propertyValue = new float[] { -1, pt3.x, pt3.y, pt3.z, 0, 0, 0 };
7541           pt = iToken + 1;
7542         } else if (isArrayParameter(pt)) {
7543           // PyMOL offsets -- [1, scrx, scry, scrz, molx, moly, molz] in angstroms
7544           propertyValue = floatParameterSet(pt, 7, 7);
7545           pt = iToken + 1;
7546         }
7547         break;
7548       case T.off:
7549         propertyName = "off";
7550         break;
7551       case T.scale:
7552         propertyName = "scale";
7553         propertyValue = Float.valueOf(floatParameter(pt++));
7554         break;
7555       case T.script:
7556         propertyName = "script";
7557         propertyValue = paramAsStr(pt++);
7558         break;
7559       case T.image:
7560         pt++;
7561         //$FALL-THROUGH$
7562       case T.string:
7563         boolean isImage = (theTok != T.string);
7564         checkLength(pt--);
7565         if (isImage) {
7566           if (id == null) {
7567             String[] data = new String[1];
7568             getShapePropertyData(JC.SHAPE_ECHO, "currentTarget", data);
7569             id = data[0];
7570           }
7571           if (!chk && vwr.ms.getEchoStateActive()
7572               && vwr.fm.loadImage(getToken(pt).value, id, !useThreads()))
7573             throw new ScriptInterruption(this, "setEchoImage", 1);
7574           return;
7575         }
7576         cmdEcho(pt);
7577         return;
7578       case T.point:
7579         propertyName = "point";
7580         propertyValue = (isCenterParameter(pt) ? centerParameter(pt, null)
7581             : null);
7582         pt = iToken + 1;
7583         break;
7584       default:
7585         if (isCenterParameter(pt - 1)) {
7586           propertyName = "xyz";
7587           propertyValue = centerParameter(pt - 1, null);
7588           pt = iToken + 1;
7589           break;
7590         }
7591         invArg();
7592       }
7593     }
7594     checkLength(pt);
7595     if (!chk && propertyName != null)
7596       setShapeProperty(JC.SHAPE_ECHO, propertyName, propertyValue);
7597   }
7598 
cmdSetLabel(String str)7599   private boolean cmdSetLabel(String str) throws ScriptException {
7600     sm.loadShape(JC.SHAPE_LABELS);
7601     Object propertyValue = null;
7602     setShapeProperty(JC.SHAPE_LABELS, "setDefaults", vwr.slm.noneSelected);
7603     while (true) {
7604       if (str.equals("for")) {
7605         BS bs = atomExpressionAt(2);
7606         cmdLabel(iToken + 1, bs);
7607         return true;
7608       }
7609       if (str.equals("scalereference")) {
7610         float scaleAngstromsPerPixel = floatParameter(2);
7611         if (scaleAngstromsPerPixel >= 5) // actually a zoom value
7612           scaleAngstromsPerPixel = vwr.tm.getZoomSetting()
7613               / scaleAngstromsPerPixel / vwr.getScalePixelsPerAngstrom(false);
7614         propertyValue = Float.valueOf(scaleAngstromsPerPixel);
7615         break;
7616       }
7617       boolean isAbsolute = false;
7618       if (str.equals("offset") || (isAbsolute = (str.equals("offsetabsolute") || str.equals("offsetexact")))) {
7619         str = "offset";
7620         if (isPoint3f(2)) {
7621           // PyMOL offsets -- {x, y, z} in angstroms
7622           P3 pt = getPoint3f(2, false, true);
7623           // minus 1 here means from Jmol, not from PyMOL
7624           propertyValue = new float[] { -1, pt.x, pt.y, pt.z, 0, 0, 0 };
7625         } else if (isArrayParameter(2)) {
7626           // PyMOL offsets -- [1, scrx, scry, scrz, molx, moly, molz] in angstroms
7627           propertyValue = floatParameterSet(2, 7, 7);
7628         } else {
7629           int xOffset = intParameterRange(2, -JC.LABEL_OFFSET_MAX, JC.LABEL_OFFSET_MAX);
7630           int yOffset = intParameterRange(3, -JC.LABEL_OFFSET_MAX, JC.LABEL_OFFSET_MAX);
7631           if (xOffset == Integer.MAX_VALUE || yOffset == Integer.MAX_VALUE)
7632             return true;
7633           propertyValue = Integer.valueOf(JC.getOffset(xOffset, yOffset, isAbsolute));
7634         }
7635         break;
7636       }
7637       if (str.equals("alignment")) {
7638         switch (getToken(2).tok) {
7639         case T.left:
7640         case T.right:
7641         case T.center:
7642           str = "align";
7643           propertyValue = theToken.value;
7644           break;
7645         default:
7646           invArg();
7647         }
7648         break;
7649       }
7650       if (str.equals("pointer")) {
7651         int flags = JC.LABEL_POINTER_NONE;
7652         switch (getToken(2).tok) {
7653         case T.off:
7654         case T.none:
7655           break;
7656         case T.background:
7657           flags |= JC.LABEL_POINTER_BACKGROUND;
7658           //$FALL-THROUGH$
7659         case T.on:
7660           flags |= JC.LABEL_POINTER_ON;
7661           break;
7662         default:
7663           invArg();
7664         }
7665         propertyValue = Integer.valueOf(flags);
7666         break;
7667       }
7668       if (str.equals("toggle")) {
7669         iToken = 1;
7670         BS bs = (slen == 2 ? vwr.bsA() : atomExpressionAt(2));
7671         checkLast(iToken);
7672         if (chk)
7673           return true;
7674         vwr.shm.loadShape(JC.SHAPE_LABELS);
7675         vwr.shm.setShapePropertyBs(JC.SHAPE_LABELS, "toggleLabel", null, bs);
7676         return true;
7677       }
7678       iToken = 1;
7679       boolean TF = (slen == 2 || getToken(2).tok == T.on);
7680       if (str.equals("front") || str.equals("group")) {
7681         if (!TF && tokAt(2) != T.off)
7682           invArg();
7683         if (!TF)
7684           str = "front";
7685         propertyValue = (TF ? Boolean.TRUE : Boolean.FALSE);
7686         break;
7687       }
7688       if (str.equals("atom")) {
7689         if (!TF && tokAt(2) != T.off)
7690           invArg();
7691         str = "front";
7692         propertyValue = (TF ? Boolean.FALSE : Boolean.TRUE);
7693         break;
7694       }
7695       return false;
7696     }
7697     BS bs = (iToken + 1 < slen ? atomExpressionAt(++iToken) : null);
7698     checkLast(iToken);
7699     if (chk)
7700       return true;
7701     if (bs == null)
7702       setShapeProperty(JC.SHAPE_LABELS, str, propertyValue);
7703     else
7704       setShapePropertyBs(JC.SHAPE_LABELS, str, propertyValue, bs);
7705     return true;
7706   }
7707 
cmdSetPicking()7708   private void cmdSetPicking() throws ScriptException {
7709     // set picking
7710     if (slen == 2) {
7711       setStringProperty("picking", "identify");
7712       return;
7713     }
7714     // set picking @{"xxx"} or some large length, ignored
7715     if (slen > 4 || tokAt(2) == T.string) {
7716       setStringProperty("picking", getSettingStr(2, false));
7717       return;
7718     }
7719     int i = 2;
7720     // set picking select ATOM|CHAIN|GROUP|MOLECULE|MODEL|SITE
7721     // set picking measure ANGLE|DISTANCE|TORSION
7722     // set picking spin fps
7723     String type = "SELECT";
7724     switch (getToken(2).tok) {
7725     case T.select:
7726     case T.measure:
7727     case T.spin:
7728       if (checkLength34() == 4) {
7729         type = paramAsStr(2).toUpperCase();
7730         if (type.equals("SPIN"))
7731           setIntProperty("pickingSpinRate", intParameter(3));
7732         else
7733           i = 3;
7734       }
7735       break;
7736     case T.delete:
7737       break;
7738     default:
7739       checkLength(3);
7740     }
7741 
7742     // set picking on
7743     // set picking normal
7744     // set picking identify
7745     // set picking off
7746     // set picking select
7747     // set picking bonds
7748     // set picking dragmolecule
7749     // set picking dragmodel
7750     // set picking dragselected
7751 
7752     String str = paramAsStr(i);
7753     switch (getToken(i).tok) {
7754     case T.on:
7755     case T.normal:
7756       str = "identify";
7757       break;
7758     case T.off:
7759     case T.none:
7760       str = "off";
7761       break;
7762     case T.select:
7763       str = "atom";
7764       break;
7765     case T.label:
7766       str = "label";
7767       break;
7768     case T.bonds: // not implemented
7769       str = "bond";
7770       break;
7771     case T.delete:
7772       checkLength(4);
7773       if (tokAt(3) != T.bonds)
7774         invArg();
7775       str = "deleteBond";
7776       break;
7777     }
7778     int mode = ((mode = str.indexOf("_")) >= 0 ? mode : str.length());
7779     mode = ActionManager.getPickingMode(str.substring(0, mode));
7780     if (mode < 0)
7781       errorStr2(ERROR_unrecognizedParameter, "SET PICKING " + type, str);
7782     setStringProperty("picking", str);
7783   }
7784 
cmdSetPickingStyle()7785   private void cmdSetPickingStyle() throws ScriptException {
7786     if (slen > 4 || tokAt(2) == T.string) {
7787       setStringProperty("pickingStyle", getSettingStr(2, false));
7788       return;
7789     }
7790     int i = 2;
7791     boolean isMeasure = false;
7792     String type = "SELECT";
7793     switch (getToken(2).tok) {
7794     case T.measure:
7795       isMeasure = true;
7796       type = "MEASURE";
7797       //$FALL-THROUGH$
7798     case T.select:
7799       if (checkLength34() == 4)
7800         i = 3;
7801       break;
7802     default:
7803       checkLength(3);
7804     }
7805     String str = paramAsStr(i);
7806     switch (getToken(i).tok) {
7807     case T.none:
7808     case T.off:
7809       str = (isMeasure ? "measureoff" : "toggle");
7810       break;
7811     case T.on:
7812       if (isMeasure)
7813         str = "measure";
7814       break;
7815     }
7816     if (ActionManager.getPickingStyleIndex(str) < 0)
7817       errorStr2(ERROR_unrecognizedParameter, "SET PICKINGSTYLE " + type, str);
7818     setStringProperty("pickingStyle", str);
7819   }
7820 
cmdSlab(boolean isDepth)7821   private void cmdSlab(boolean isDepth) throws ScriptException {
7822     boolean TF = false;
7823     P4 plane = null;
7824     String str;
7825     if (isCenterParameter(1) || tokAt(1) == T.point4f)
7826       plane = planeParameter(1);
7827     else
7828       switch (getToken(1).tok) {
7829       case T.integer:
7830         int percent = intParameter(checkLast(1));
7831         if (!chk)
7832           if (isDepth)
7833             vwr.tm.depthToPercent(percent);
7834           else
7835             vwr.tm.slabToPercent(percent);
7836         return;
7837       case T.on:
7838        TF = true;
7839         //$FALL-THROUGH$
7840       case T.off:
7841         checkLength(2);
7842         setBooleanProperty("slabEnabled", TF);
7843         return;
7844       case T.reset:
7845         checkLength(2);
7846         if (chk)
7847           return;
7848         vwr.tm.slabReset();
7849         setBooleanProperty("slabEnabled", true);
7850         return;
7851       case T.set:
7852         checkLength(2);
7853         if (!chk)
7854           vwr.tm.setSlabDepthInternal(isDepth);
7855         return;
7856       case T.minus:
7857         str = paramAsStr(2);
7858         if (str.equalsIgnoreCase("hkl"))
7859           plane = hklParameter(3, false);
7860         else if (str.equalsIgnoreCase("plane"))
7861           plane = planeParameter(2);
7862         if (plane == null)
7863           invArg();
7864         plane.scale4(-1);
7865         break;
7866       case T.plane:
7867         switch (getToken(2).tok) {
7868         case T.none:
7869           break;
7870         default:
7871           plane = planeParameter(1);
7872         }
7873         break;
7874       case T.hkl:
7875         plane = (getToken(2).tok == T.none ? null : hklParameter(2, false));
7876         break;
7877       case T.reference:
7878         // only in 11.2; deprecated
7879         return;
7880       default:
7881         invArg();
7882       }
7883     if (!chk)
7884       vwr.tm.slabInternal(plane, isDepth);
7885   }
7886 
7887   /*
7888   private void slice() throws ScriptException{
7889     if(!chk && vwr.slicer==null){
7890      vwr.createSlicer();
7891     }
7892     int tok1 = getToken(1).tok;
7893     if(tok1==Token.left||tok1==Token.right){
7894       switch (getToken(2).tok){
7895       case Token.on:
7896         if(chk) return;
7897         vwr.slicer.drawSlicePlane(tok1, true);
7898         return;
7899       case Token.off:
7900         if(chk) return;
7901         vwr.slicer.drawSlicePlane(tok1, false);
7902         return;
7903       default:
7904         invArg();
7905       break;
7906       }
7907     }else{//command to slice object, not show slice planes
7908       String name = (String)getToken(1).value;
7909       //TODO - should accept "all"  for now "all" will fail silently.
7910       // Should check it is a valid  isosurface name
7911       //Should be followed by two angles, and two percents (float values)
7912       float[] param = new float[4];
7913       for (int i=2;i<6;++i){
7914         if(getToken(i).tok == Token.decimal){
7915           param[i-2]=floatParameter(i);
7916         } else{
7917           invArg();
7918         }
7919       }
7920       if(!chk){
7921         vwr.slicer.setSlice(param[0], param[1], param[2], param[3]);
7922         vwr.slicer.sliceObject(name);
7923       }
7924       return;
7925     }
7926   }
7927 
7928   */
7929 
cmdSsbond()7930   private void cmdSsbond() throws ScriptException {
7931     int mad = getMadParameter();
7932     if (mad == Integer.MAX_VALUE)
7933       return;
7934     setShapeProperty(JC.SHAPE_STICKS, "type",
7935         Integer.valueOf(Edge.BOND_SULFUR_MASK));
7936     setShapeSizeBs(JC.SHAPE_STICKS, mad, null);
7937     setShapeProperty(JC.SHAPE_STICKS, "type",
7938         Integer.valueOf(Edge.BOND_COVALENT_MASK));
7939   }
7940 
cmdStructure()7941   private void cmdStructure() throws ScriptException {
7942     STR type = STR
7943         .getProteinStructureType(paramAsStr(1));
7944     if (type == STR.NOT)
7945       invArg();
7946     BS bs = null;
7947     switch (tokAt(2)) {
7948     case T.define:
7949     case T.bitset:
7950     case T.expressionBegin:
7951       bs = atomExpressionAt(2);
7952       checkLast(iToken);
7953       break;
7954     default:
7955       checkLength(2);
7956     }
7957     if (chk)
7958       return;
7959     clearDefinedVariableAtomSets();
7960     vwr.setProteinType(type, bs);
7961   }
7962 
cmdSubset()7963   private void cmdSubset() throws ScriptException {
7964     BS bs = null;
7965     if (!chk)
7966       vwr.slm.setSelectionSubset(null);
7967     // hover none --> subset exprbeg "off" exprend
7968     if (slen != 1 && (slen != 4 || !getToken(2).value.equals("off")))
7969       bs = atomExpressionAt(1);
7970     if (!chk)
7971       vwr.slm.setSelectionSubset(bs);
7972   }
7973 
cmdSync()7974   private void cmdSync() throws ScriptException {
7975     // new 11.3.9
7976     String text = "";
7977     String applet = "";
7978     int port = PT.parseInt(optParameterAsString(1));
7979     if (port == Integer.MIN_VALUE) {
7980       checkLength(-3);
7981       port = 0;
7982       switch (slen) {
7983       case 1:
7984         // sync
7985         applet = "*";
7986         text = "ON";
7987         break;
7988       case 2:
7989         // sync (*) text
7990         applet = paramAsStr(1);
7991         if (applet.indexOf("jmolApplet") == 0
7992             || PT.isOneOf(applet, ";*;.;^;")) {
7993           text = "ON";
7994           if (!chk)
7995             vwr.syncScript(text, applet, 0);
7996           applet = ".";
7997           break;
7998         }
7999         text = applet;
8000         applet = "*";
8001         break;
8002       case 3:
8003         // sync applet text
8004         // sync applet STEREO
8005         applet = paramAsStr(1);
8006         text = (tokAt(2) == T.stereo ? Viewer.SYNC_GRAPHICS_MESSAGE
8007             : paramAsStr(2));
8008         break;
8009       }
8010     } else {
8011       SV v = null;
8012       if (slen > 2 && (v = setVariable(2, -1, "", false)) == null)
8013         return;
8014       text = (slen == 2 ? null : v.tok == T.hash ? v.toJSON() : v.asString());
8015       applet = null;
8016     }
8017     if (chk)
8018       return;
8019     vwr.syncScript(text, applet, port);
8020   }
8021 
cmdThrow()8022   private void cmdThrow() throws ScriptException {
8023     if (chk)
8024       return;
8025     int pt = (tokAt(1) == T.context ? 2 : 1);
8026     SV v = (pt == 1 ? setVariable(1, slen, "thrown_value", false)
8027         : vwr.g.setUserVariable("thrown_value", SV.newS(optParameterAsString(2))));
8028     String info = v.asString();
8029     if (info.length() == 0 && (info = optParameterAsString(1)).length() == 0)
8030       info = "context";
8031     if (pt == 2) {
8032       saveContext(info);
8033       if (doReport())
8034         report(GT.o(GT.$("to resume, enter: &{0}"), info), false);
8035       throw new ScriptInterruption(this, info, Integer.MIN_VALUE);
8036     }
8037     evalError(info, null);
8038   }
8039 
saveContext(String saveName)8040   private ScriptContext saveContext(String saveName) {
8041     ScriptContext sc = getScriptContext("Context_" + saveName);
8042     vwr.stm.saveContext(saveName, sc);
8043     vwr.g.setUserVariable(saveName, SV.newV(T.context, sc));
8044     return sc;
8045   }
8046 
cmdTimeout(int index)8047   private void cmdTimeout(int index) throws ScriptException {
8048     // timeout ID "mytimeout" mSec "script"
8049     // msec < 0 --> repeat indefinitely
8050     // timeout ID "mytimeout" 1000 // milliseconds
8051     // timeout ID "mytimeout" 0.1 // seconds
8052     // timeout ID "mytimeout" OFF
8053     // timeout ID "mytimeout" // flag to trigger waiting timeout repeat
8054     // timeout OFF
8055     String name = null;
8056     String script = null;
8057     int mSec = 0;
8058     if (slen == index) {
8059       showString(vwr.showTimeout(null));
8060       return;
8061     }
8062     for (int i = index; i < slen; i++)
8063       switch (getToken(i).tok) {
8064       case T.id:
8065         name = paramAsStr(++i);
8066         if (slen == 3) {
8067           if (!chk)
8068             vwr.triggerTimeout(name);
8069           return;
8070         }
8071         break;
8072       case T.off:
8073         break;
8074       case T.integer:
8075         mSec = intParameter(i);
8076         break;
8077       case T.decimal:
8078         mSec = Math.round(floatParameter(i) * 1000);
8079         break;
8080       default:
8081         if (name == null)
8082           name = paramAsStr(i);
8083         else if (script == null)
8084           script = paramAsStr(i);
8085         else
8086           invArg();
8087         break;
8088       }
8089     if (!chk)
8090       vwr.setTimeout(name, mSec, script);
8091   }
8092 
cmdTranslate(boolean isSelected)8093   private void cmdTranslate(boolean isSelected) throws ScriptException {
8094     // translate [selected] X|Y|Z x.x [NM|ANGSTROMS]
8095     // translate [selected] X|Y x.x%
8096     // translate [selected] {x y z} [{atomExpression}]
8097     BS bs = null;
8098     int i = 1;
8099     int i0 = 0;
8100     if (tokAt(1) == T.selected) {
8101       isSelected = true;
8102       i0 = 1;
8103       i = 2;
8104     }
8105     if (isPoint3f(i)) {
8106       P3 pt = getPoint3f(i, true, true);
8107       bs = (iToken + 1 < slen ? atomExpressionAt(++iToken)
8108           : null);
8109       checkLast(iToken);
8110       if (!chk)
8111         vwr.setAtomCoordsRelative(pt, bs);
8112       return;
8113     }
8114     char xyz = (paramAsStr(i).toLowerCase() + " ").charAt(0);
8115     if ("xyz".indexOf(xyz) < 0)
8116       error(ERROR_axisExpected);
8117     float amount = floatParameter(++i);
8118     char type;
8119     switch (tokAt(++i)) {
8120     case T.nada:
8121     case T.define:
8122     case T.bitset:
8123     case T.expressionBegin:
8124       type = '\0';
8125       break;
8126     default:
8127       type = (optParameterAsString(i).toLowerCase() + '\0').charAt(0);
8128     }
8129     if (amount == 0 && type != '\0')
8130       return;
8131     iToken = i0 + (type == '\0' ? 2 : 3);
8132     bs = (isSelected ? vwr.bsA()
8133         : iToken + 1 < slen ? atomExpressionAt(++iToken) : null);
8134     checkLast(iToken);
8135     if (!chk) {
8136       vwr.translate(xyz, amount, type, bs);
8137       refresh(false);
8138     }
8139   }
8140 
cmdUnbind()8141   private void cmdUnbind() throws ScriptException {
8142     /*
8143      * unbind "MOUSE-ACTION"|all ["...script..."|actionName|all]
8144      */
8145     if (slen != 1)
8146       checkLength23();
8147     String mouseAction = optParameterAsString(1);
8148     String name = optParameterAsString(2);
8149     if (mouseAction.length() == 0 || tokAt(1) == T.all)
8150       mouseAction = null;
8151     if (name.length() == 0 || tokAt(2) == T.all)
8152       name = null;
8153     if (name == null && mouseAction != null
8154         && ActionManager.getActionFromName(mouseAction) >= 0) {
8155       name = mouseAction;
8156       mouseAction = null;
8157     }
8158     if (!chk)
8159       vwr.unBindAction(mouseAction, name);
8160   }
8161 
cmdUndoRedoMove()8162   private void cmdUndoRedoMove() throws ScriptException {
8163     // Jmol 12.1.46
8164     int n = 1;
8165     int len = 2;
8166     switch (tokAt(1)) {
8167     case T.nada:
8168       len = 1;
8169       break;
8170     case T.all:
8171       n = 0;
8172       break;
8173     case T.integer:
8174       n = intParameter(1);
8175       break;
8176     default:
8177       invArg();
8178     }
8179     checkLength(len);
8180     if (!chk)
8181       vwr.undoMoveAction(tokAt(0), n);
8182   }
8183 
setModelCagePts(int iModel, T3[] originABC, String name)8184   public void setModelCagePts(int iModel, T3[] originABC, String name) {
8185     if (iModel < 0)
8186       iModel = vwr.am.cmi;
8187     SymmetryInterface sym = Interface.getSymmetry(vwr, "eval");
8188     if (sym == null && vwr.async)
8189       throw new NullPointerException();
8190     try {
8191       vwr.ms.setModelCage(iModel,
8192           originABC == null ? null : sym.getUnitCell(originABC, false, name));
8193     } catch (Exception e) {
8194       //
8195     }
8196   }
8197 
cmdUnitcell(int i)8198   private void cmdUnitcell(int i) throws ScriptException {
8199     getCmdExt().dispatch(T.unitcell, i == 2, null);
8200   }
8201 
cmdVector()8202   private void cmdVector() throws ScriptException {
8203     EnumType type = EnumType.SCREEN;
8204     float value = 1;
8205     checkLength(-3);
8206     switch (iToken = slen) {
8207     case 1:
8208       break;
8209     case 2:
8210       switch (getToken(1).tok) {
8211       case T.on:
8212         break;
8213       case T.off:
8214         value = 0;
8215         break;
8216       case T.integer:
8217         // diameter Pixels
8218         int d = intParameterRange(1, 0, 19);
8219         if (d == Integer.MAX_VALUE)
8220           return;
8221         value = d;
8222         break;
8223       case T.decimal:
8224         // radius angstroms
8225         type = EnumType.ABSOLUTE;
8226         if (Float.isNaN(value = floatParameterRange(1, 0, 3)))
8227           return;
8228         break;
8229       default:
8230         error(ERROR_booleanOrNumberExpected);
8231       }
8232       break;
8233     case 3:
8234       switch (tokAt(1)) {
8235       case T.trace:
8236           setIntProperty("vectorTrace", intParameterRange(2, 0, 20));
8237         return;
8238       case T.scale:
8239         if (!Float.isNaN(value = floatParameterRange(2, -100, 100)))
8240           setFloatProperty("vectorScale", value);
8241         return;
8242       case T.max:
8243         float max = floatParameter(2);
8244         if (!chk)
8245           vwr.ms.scaleVectorsToMax(max);
8246         return;
8247       }
8248       break;
8249     }
8250     setShapeSize(JC.SHAPE_VECTORS, new RadiusData(null, value, type, null));
8251   }
8252 
cmdVibration()8253   private void cmdVibration() throws ScriptException {
8254     checkLength(-3);
8255     float period = 0;
8256     switch (getToken(1).tok) {
8257     case T.on:
8258       checkLength(2);
8259       period = vwr.getFloat(T.vibrationperiod);
8260       break;
8261     case T.off:
8262       checkLength(2);
8263       period = 0;
8264       break;
8265     case T.integer:
8266     case T.decimal:
8267       checkLength(2);
8268       period = floatParameter(1);
8269       break;
8270     case T.scale:
8271       if (!Float.isNaN(period = floatParameterRange(2, -100, 100)))
8272         setFloatProperty("vibrationScale", period);
8273       return;
8274     case T.max:
8275         float max = floatParameter(2);
8276         if (!chk)
8277           vwr.ms.scaleVectorsToMax(max);
8278         break;
8279     case T.period:
8280       setFloatProperty("vibrationPeriod", floatParameter(2));
8281       return;
8282     case T.identifier:
8283       invArg();
8284       break;
8285     default:
8286       period = -1;
8287     }
8288     if (period < 0)
8289       invArg();
8290     if (chk)
8291       return;
8292     if (period == 0) {
8293       vwr.tm.setVibrationPeriod(0);
8294       return;
8295     }
8296     vwr.setVibrationPeriod(-period);
8297   }
8298 
cmdWireframe()8299   private void cmdWireframe() throws ScriptException {
8300     int mad = Integer.MIN_VALUE;
8301     if (tokAt(1) == T.reset)
8302       checkLast(1);
8303     else
8304       mad = getMadParameter();
8305     if (chk || mad == Integer.MAX_VALUE)
8306       return;
8307     setShapeProperty(JC.SHAPE_STICKS, "type",
8308         Integer.valueOf(Edge.BOND_COVALENT_MASK));
8309     setShapeSizeBs(JC.SHAPE_STICKS,
8310         mad == Integer.MIN_VALUE ? 2 * JC.DEFAULT_BOND_MILLIANGSTROM_RADIUS
8311             : mad, null);
8312   }
8313 
cmdZap(boolean isZapCommand)8314   private void cmdZap(boolean isZapCommand) throws ScriptException {
8315     if (slen == 1 || !isZapCommand) {
8316       boolean doAll = (isZapCommand && !isStateScript);
8317       if (doAll)
8318         vwr.cacheFileByName(null, false);
8319       vwr.zap(true, doAll, true);
8320       refresh(false);
8321       return;
8322     }
8323     BS bs = atomExpressionAt(1);
8324     if (chk)
8325       return;
8326     if (bs.nextSetBit(0) < 0 && slen == 4 && tokAt(2) == T.spec_model2) {
8327       int iModel = vwr.ms.getModelNumberIndex(getToken(2).intValue, false, true);
8328       if (iModel >= 0)
8329         vwr.deleteModels(iModel, null);
8330       return;
8331     }
8332     int nDeleted = vwr.deleteAtoms(bs, true);
8333     boolean isQuiet = !doReport();
8334     if (!isQuiet)
8335       report(GT.i(GT.$("{0} atoms deleted"), nDeleted), false);
8336     vwr.select(null, false, 0, isQuiet);
8337   }
8338 
cmdZoom(boolean isZoomTo)8339   private void cmdZoom(boolean isZoomTo) throws ScriptException {
8340     if (!isZoomTo) {
8341       // zoom
8342       // zoom on|off
8343       int tok = (slen > 1 ? getToken(1).tok : T.on);
8344       switch (tok) {
8345       case T.in:
8346       case T.out:
8347         break;
8348       case T.on:
8349       case T.off:
8350         if (slen > 2)
8351           bad();
8352         if (!chk)
8353           setBooleanProperty("zoomEnabled", tok == T.on);
8354         return;
8355       }
8356     }
8357     P3 center = null;
8358     //Point3f currentCenter = vwr.getRotationCenter();
8359     int i = 1;
8360     // zoomTo time-sec
8361     float floatSecondsTotal = (isZoomTo ? (isFloatParameter(i) ? floatParameter(i++)
8362         : 1f)
8363         : 0f);
8364     if (floatSecondsTotal < 0) {
8365       // zoom -10
8366       i--;
8367       floatSecondsTotal = 0;
8368     }
8369     // zoom {x y z} or (atomno=3)
8370     int ptCenter = 0;
8371     BS bsCenter = null;
8372     if (isCenterParameter(i)) {
8373       ptCenter = i;
8374       Object[] ret = new Object[1];
8375       center = centerParameter(i, ret);
8376       if (ret[0] instanceof BS)
8377         bsCenter = (BS) ret[0];
8378       i = iToken + 1;
8379     } else if (tokAt(i) == T.integer && getToken(i).intValue == 0) {
8380       bsCenter = vwr.getAtomBitSet("visible");
8381       center = vwr.ms.getAtomSetCenter(bsCenter);
8382     }
8383 
8384     // disabled sameAtom stuff -- just too weird
8385     boolean isSameAtom = false;// && (center != null && currentCenter.distance(center) < 0.1);
8386     // zoom/zoomTo [0|n|+n|-n|*n|/n|IN|OUT]
8387     // zoom/zoomTo percent|-factor|+factor|*factor|/factor | 0
8388     float zoom = vwr.tm.getZoomSetting();
8389 
8390     float newZoom = getZoom(ptCenter, i, bsCenter, zoom);
8391     i = iToken + 1;
8392     float xTrans = Float.NaN;
8393     float yTrans = Float.NaN;
8394     if (i != slen) {
8395       xTrans = floatParameter(i++);
8396       yTrans = floatParameter(i++);
8397     }
8398     if (i != slen)
8399       invArg();
8400     if (newZoom < 0) {
8401       newZoom = -newZoom; // currentFactor
8402       if (isZoomTo) {
8403         // undocumented!
8404         // no factor -- check for no center (zoom out) or same center (zoom in)
8405         if (slen == 1 || isSameAtom)
8406           newZoom *= 2;
8407         else if (center == null)
8408           newZoom /= 2;
8409       }
8410     }
8411     float max = TransformManager.MAXIMUM_ZOOM_PERCENTAGE;
8412     if (newZoom < 5 || newZoom > max)
8413       numberOutOfRange(5, max);
8414     if (!vwr.tm.isWindowCentered()) {
8415       // do a smooth zoom only if not windowCentered
8416       if (center != null) {
8417         BS bs = atomExpressionAt(ptCenter);
8418         if (!chk)
8419           vwr.setCenterBitSet(bs, false);
8420       }
8421       center = vwr.tm.fixedRotationCenter;
8422       if (Float.isNaN(xTrans))
8423         xTrans = vwr.tm.getTranslationXPercent();
8424       if (Float.isNaN(yTrans))
8425         yTrans = vwr.tm.getTranslationYPercent();
8426     }
8427     if (chk)
8428       return;
8429     if (Float.isNaN(xTrans))
8430       xTrans = 0;
8431     if (Float.isNaN(yTrans))
8432       yTrans = 0;
8433     if (isSameAtom && Math.abs(zoom - newZoom) < 1 || !useThreads())
8434       floatSecondsTotal = 0;
8435     vwr.moveTo(this, floatSecondsTotal, center, JC.center, Float.NaN, null,
8436         newZoom, xTrans, yTrans, Float.NaN, null, Float.NaN, Float.NaN,
8437         Float.NaN, Float.NaN, Float.NaN, Float.NaN);
8438     if (isJS && floatSecondsTotal > 0 && vwr.g.waitForMoveTo)
8439       throw new ScriptInterruption(this, "zoomTo", 1);
8440 
8441   }
8442 
8443   /////////////////////////////// methods used just by cmdXXXXX methods
8444 
colorShape(int shapeType, int index, boolean isBackground)8445   private void colorShape(int shapeType, int index, boolean isBackground)
8446       throws ScriptException {
8447     String translucency = null;
8448     Object colorvalue = null;
8449     Object colorvalue1 = null;
8450     BS bs = null;
8451     String prefix = (index == 2 && tokAt(1) == T.balls ? "ball" : "");
8452     boolean isColor = false;
8453     boolean isIsosurface = (shapeType == JC.SHAPE_ISOSURFACE || shapeType == JC.SHAPE_CONTACT);
8454     int typeMask = 0;
8455     boolean doClearBondSet = false;
8456     float translucentLevel = Float.MAX_VALUE;
8457     if (index < 0) {
8458       bs = atomExpressionAt(-index);
8459       index = iToken + 1;
8460       if (isBondSet) {
8461         doClearBondSet = true;
8462         shapeType = JC.SHAPE_STICKS;
8463       }
8464     }
8465     int tok = getToken(index).tok;
8466     if (isBackground)
8467       getToken(index);
8468     else if ((isBackground = (tok == T.background)) == true)
8469       getToken(++index);
8470     if (isBackground)
8471       prefix = "bg";
8472     else if (isIsosurface) {
8473       switch (theTok) {
8474       case T.mesh:
8475         getToken(++index);
8476         prefix = "mesh";
8477         break;
8478       case T.phase:
8479         int argb = getArgbParamOrNone(++index, false);
8480         colorvalue1 = (argb == 0 ? null : Integer.valueOf(argb));
8481         getToken(index = iToken + 1);
8482         break;
8483       case T.define:
8484       case T.bitset:
8485       case T.expressionBegin:
8486         if (theToken.value instanceof BondSet) {
8487           bs = (BondSet) theToken.value;
8488           prefix = "vertex";
8489         } else {
8490           bs = atomExpressionAt(index);
8491           prefix = "atom";
8492         }
8493         // don't allow isosurface partial translucency (yet)
8494         //translucentLevel = Parser.FLOAT_MIN_SAFE;
8495         getToken(index = iToken + 1);
8496         break;
8497       }
8498     }
8499     if (!chk && (shapeType == JC.SHAPE_MO || shapeType == JC.SHAPE_NBO)
8500         && getIsoExt().dispatch(shapeType, true, st) != null)
8501       return;
8502     boolean isTranslucent = (theTok == T.translucent);
8503     if (isTranslucent || theTok == T.opaque) {
8504       if (translucentLevel == PT.FLOAT_MIN_SAFE)
8505         invArg();
8506       translucency = paramAsStr(index++);
8507       if (isTranslucent && isFloatParameter(index))
8508         translucentLevel = getTranslucentLevel(index++);
8509     }
8510     tok = 0;
8511     if (index < slen && tokAt(index) != T.on && tokAt(index) != T.off) {
8512       isColor = true;
8513       tok = getToken(index).tok;
8514       if ((!isIsosurface || tokAt(index + 1) != T.to) && isColorParam(index)) {
8515         int argb = getArgbParamOrNone(index, false);
8516         colorvalue = (argb == 0 ? null : Integer.valueOf(argb));
8517         if (tokAt(index = iToken + 1) != T.nada && translucency == null) {
8518           getToken(index);
8519           isTranslucent = (theTok == T.translucent);
8520           if (isTranslucent || theTok == T.opaque) {
8521             translucency = paramAsStr(index++);
8522             if (isTranslucent && isFloatParameter(index))
8523               translucentLevel = getTranslucentLevel(index++);
8524           }
8525         }
8526         if (isColorParam(index)) {
8527           argb = getArgbParamOrNone(index, false);
8528           colorvalue1 = (argb == 0 ? null : Integer.valueOf(argb));
8529           index = iToken + 1;
8530         }
8531         checkLength(index);
8532       } else if (shapeType == JC.SHAPE_LCAOCARTOON) {
8533         iToken--; // back up one
8534       } else {
8535         // must not be a color, but rather a color SCHEME
8536         // this could be a problem for properties, which can't be
8537         // checked later -- they must be turned into a color NOW.
8538 
8539         // "cpk" value would be "spacefill"
8540         String name = paramAsStr(index).toLowerCase();
8541         boolean isByElement = (name.indexOf(ColorEncoder.BYELEMENT_PREFIX) == 0);
8542         boolean isColorIndex = (isByElement || name
8543             .indexOf(ColorEncoder.BYRESIDUE_PREFIX) == 0);
8544         PAL pal = (isColorIndex || isIsosurface ? PAL.PROPERTY
8545             : tok == T.spacefill ? PAL.CPK : PAL.getPalette(name));
8546         // color atoms "cpkScheme"
8547         if (pal == PAL.UNKNOWN || (pal == PAL.TYPE || pal == PAL.ENERGY)
8548             && shapeType != JC.SHAPE_HSTICKS)
8549           invArg();
8550         Object data = null;
8551         BS bsSelected = (pal != PAL.PROPERTY && pal != PAL.VARIABLE
8552             || !vwr.g.rangeSelected ? null : vwr.bsA());
8553         if (pal == PAL.PROPERTY) {
8554           if (isColorIndex) {
8555             if (!chk) {
8556               data = getCmdExt().getBitsetPropertyFloat(bsSelected, (isByElement ? T.elemno
8557                   : T.groupid) | T.allfloat, null, Float.NaN, Float.NaN);
8558             }
8559           } else {
8560             boolean isPropertyExplicit = name.equals("property");
8561             // problem here with   color $isosurface1 "rwb"
8562             if (isPropertyExplicit
8563                 && T.tokAttr((tok = getToken(++index).tok), T.atomproperty)
8564                 && !T.tokAttr(tok, T.strproperty)) {
8565               tok = getToken(index).tok;
8566               String type = (tok == T.dssr ? getToken(++index).value.toString() : null);
8567               if (!chk) {
8568                 data = getCmdExt().getBitsetPropertyFloat(bsSelected, tok
8569                     | T.allfloat, type, Float.NaN, Float.NaN);
8570               }
8571               index++;
8572             } else if (!isPropertyExplicit && !isIsosurface) {
8573               index++;
8574             }
8575             // index points to item after property
8576           }
8577         } else if (pal == PAL.VARIABLE) {
8578           index++;
8579           name = paramAsStr(index++);
8580           data = new float[vwr.ms.ac];
8581           Parser.parseStringInfestedFloatArray(
8582               "" + getParameter(name, T.string, true), null, (float[]) data);
8583           pal = PAL.PROPERTY;
8584         }
8585         // index here points to NEXT item
8586         if (pal == PAL.PROPERTY) {
8587           String scheme = null;
8588           if (tokAt(index) == T.string) {
8589             scheme = paramAsStr(index++).toLowerCase();
8590             if (isArrayParameter(index)) {
8591               scheme += "="
8592                   + SV.sValue(SV.getVariableAS(stringParameterSet(index)))
8593                       .replace('\n', ' ');
8594               index = iToken + 1;
8595             }
8596           } else if (isIsosurface && isColorParam(index)) {
8597             scheme = getColorRange(index);
8598             index = iToken + 1;
8599           }
8600           if (scheme != null && !isIsosurface) {
8601             setStringProperty("propertyColorScheme", (isTranslucent
8602                 && translucentLevel == Float.MAX_VALUE ? "translucent " : "")
8603                 + scheme);
8604             isColorIndex = (scheme.indexOf(ColorEncoder.BYELEMENT_PREFIX) == 0 || scheme
8605                 .indexOf(ColorEncoder.BYRESIDUE_PREFIX) == 0);
8606           }
8607           float min = 0;
8608           float max = Float.MAX_VALUE;
8609           if (!isColorIndex
8610               && (tokAt(index) == T.absolute || tokAt(index) == T.range)) {
8611             min = floatParameter(index + 1);
8612             max = floatParameter(index + 2);
8613             index += 3;
8614             if (min == max && isIsosurface) {
8615               float[] range = (float[]) getShapeProperty(shapeType, "dataRange");
8616               if (range != null) {
8617                 min = range[0];
8618                 max = range[1];
8619               }
8620             } else if (min == max) {
8621               max = Float.MAX_VALUE;
8622             }
8623           }
8624           if (isIsosurface) {
8625           } else if (data == null) {
8626             if (!chk)
8627               vwr.setCurrentColorRange(name);
8628           } else {
8629             if (!chk)
8630               vwr.cm.setPropertyColorRangeData((float[]) data, bsSelected);
8631           }
8632           if (isIsosurface) {
8633             checkLength(index);
8634             if (chk)
8635               return;
8636             isColor = false;
8637             ColorEncoder ce = (scheme == null ? (ColorEncoder) getShapeProperty(shapeType, "colorEncoder") : null);
8638             if (ce == null && (ce = vwr.cm.getColorEncoder(scheme)) == null)
8639               return;
8640             ce.isTranslucent = (isTranslucent && translucentLevel == Float.MAX_VALUE);
8641             ce.setRange(min, max, min > max);
8642             if (max == Float.MAX_VALUE)
8643               ce.hi = max;
8644             setShapeProperty(shapeType, "remapColor", ce);
8645             showString(((String) getShapeProperty(shapeType, "dataRangeStr"))
8646                 .replace('\n', ' '));
8647             if (translucentLevel == Float.MAX_VALUE)
8648               return;
8649           } else if (max != Float.MAX_VALUE) {
8650             vwr.cm.setPropertyColorRange(min, max);
8651           }
8652         } else {
8653           index++;
8654         }
8655         checkLength(index);
8656         colorvalue = pal;
8657       }
8658     }
8659     if (chk || shapeType < 0)
8660       return;
8661     switch (shapeType) {
8662     case JC.SHAPE_STRUTS:
8663       typeMask = Edge.BOND_STRUT;
8664       break;
8665     case JC.SHAPE_HSTICKS:
8666       typeMask = Edge.BOND_HYDROGEN_MASK;
8667       break;
8668     case JC.SHAPE_SSSTICKS:
8669       typeMask = Edge.BOND_SULFUR_MASK;
8670       break;
8671     case JC.SHAPE_STICKS:
8672       typeMask = Edge.BOND_COVALENT_MASK;
8673       break;
8674     default:
8675       typeMask = 0;
8676     }
8677     if (typeMask == 0) {
8678       sm.loadShape(shapeType);
8679       if (shapeType == JC.SHAPE_LABELS)
8680         setShapeProperty(JC.SHAPE_LABELS, "setDefaults", vwr.slm.noneSelected);
8681     } else {
8682       if (bs != null) {
8683         vwr.selectBonds(bs);
8684         bs = null;
8685       }
8686       shapeType = JC.SHAPE_STICKS;
8687       setShapeProperty(shapeType, "type", Integer.valueOf(typeMask));
8688     }
8689     if (isColor) {
8690       // ok, the following options require precalculation.
8691       // the state must not save them as paletteIDs, only as pure
8692       // color values.
8693       switch (tok) {
8694       case T.partialcharge:
8695         getPartialCharges(bs);
8696         break;
8697       case T.surfacedistance:
8698       case T.straightness:
8699         vwr.autoCalculate(tok, null);
8700         break;
8701       case T.temperature:
8702         if (vwr.g.rangeSelected)
8703           vwr.ms.clearBfactorRange();
8704         break;
8705       case T.group:
8706         vwr.ms.calcSelectedGroupsCount();
8707         break;
8708       case T.polymer:
8709       case T.monomer:
8710         vwr.ms.calcSelectedMonomersCount();
8711         break;
8712       case T.molecule:
8713         vwr.ms.calcSelectedMoleculesCount();
8714         break;
8715       }
8716       if (colorvalue1 != null
8717           && (isIsosurface || shapeType == JC.SHAPE_CARTOON
8718               || shapeType == JC.SHAPE_RIBBONS || shapeType == JC.SHAPE_POLYHEDRA))
8719         setShapeProperty(shapeType, "colorPhase", new Object[] { colorvalue1,
8720             colorvalue });
8721       else if (bs == null)
8722         setShapeProperty(shapeType, prefix + "color", colorvalue);
8723       else
8724         setShapePropertyBs(shapeType, prefix + "color", colorvalue, bs);
8725     }
8726     if (translucency != null)
8727       setShapeTranslucency(shapeType, prefix, translucency, translucentLevel,
8728           bs);
8729     if (typeMask != 0)
8730       setShapeProperty(JC.SHAPE_STICKS, "type",
8731           Integer.valueOf(Edge.BOND_COVALENT_MASK));
8732     if (doClearBondSet)
8733       vwr.selectBonds(null);
8734     if (shapeType == JC.SHAPE_BALLS)
8735       vwr.shm.checkInheritedShapes();
8736   }
8737 
8738   /*
8739    * Based on the form of the parameters, returns and encoded radius as follows:
8740    *
8741    * script meaning range
8742    *
8743    * +1.2 offset [0 - 10]
8744    * -1.2 offset 0)
8745    * 1.2 absolute (0 - 10]
8746    * -30% 70% (-100 - 0)
8747    * +30% 130% (0
8748    * 80% percent (0
8749    */
8750 
getPartialCharges(BS bs)8751   public void getPartialCharges(BS bs) throws ScriptException  {
8752     try {
8753       vwr.getOrCalcPartialCharges(bs, null);
8754     } catch (Exception e) {
8755       throw new ScriptInterruption(this, "partialcharge", 1);
8756     }
8757   }
8758 
encodeRadiusParameter(int index, boolean isOnly, boolean allowAbsolute)8759   public RadiusData encodeRadiusParameter(int index, boolean isOnly,
8760                                           boolean allowAbsolute)
8761       throws ScriptException {
8762 
8763     float value = Float.NaN;
8764     EnumType factorType = EnumType.ABSOLUTE;
8765     VDW vdwType = null;
8766 
8767     int tok = (index == -1 ? T.vanderwaals : getToken(index).tok);
8768     switch (tok) {
8769     case T.adpmax:
8770     case T.adpmin:
8771     case T.bondingradius:
8772     case T.hydrophobicity:
8773     case T.temperature:
8774     case T.vanderwaals:
8775       value = 1;
8776       factorType = EnumType.FACTOR;
8777       vdwType = (tok == T.vanderwaals ? null : VDW.getVdwType2(T
8778           .nameOf(tok)));
8779       tok = tokAt(++index);
8780       break;
8781     }
8782     switch (tok) {
8783     case T.reset:
8784       return vwr.rd;
8785     case T.auto:
8786     case T.rasmol:
8787     case T.babel:
8788     case T.babel21:
8789     case T.jmol:
8790       value = 1;
8791       factorType = EnumType.FACTOR;
8792       iToken = index - 1;
8793       break;
8794     case T.plus:
8795     case T.integer:
8796     case T.decimal:
8797       if (tok == T.plus) {
8798         index++;
8799       } else if (tokAt(index + 1) == T.percent) {
8800         value = Math.round(floatParameter(index));
8801         iToken = ++index;
8802         factorType = EnumType.FACTOR;
8803         if (value < 0 || value > 200) {
8804           integerOutOfRange(0, 200);
8805           return null;
8806         }
8807         value /= 100;
8808         break;
8809       } else if (tok == T.integer) {
8810         value = intParameter(index);
8811         // rasmol 250-scale if positive or percent (again), if negative
8812         // (deprecated)
8813         if (value > 749 || value < -200) {
8814           integerOutOfRange(-200, 749);
8815             return null;
8816 
8817           }
8818         if (value > 0) {
8819           value /= 250;
8820           factorType = EnumType.ABSOLUTE;
8821         } else {
8822           value /= -100;
8823           factorType = EnumType.FACTOR;
8824         }
8825         break;
8826       }
8827       float max;
8828       if (tok == T.plus || !allowAbsolute) {
8829         factorType = EnumType.OFFSET;
8830         max = Atom.RADIUS_MAX;
8831       } else {
8832         factorType = EnumType.ABSOLUTE;
8833         vdwType = VDW.NADA;
8834         max = 100;
8835       }
8836       value = floatParameterRange(index, (isOnly || !allowAbsolute ? -max : 0),
8837           max);
8838       if (Float.isNaN(value))
8839         return null;
8840       if (isOnly)
8841         value = -value;
8842       if (value > Atom.RADIUS_MAX)
8843         value = Atom.RADIUS_GLOBAL;
8844       break;
8845     default:
8846       if (value == 1)
8847         index--;
8848     }
8849     if (vdwType == null) {
8850       vdwType = VDW.getVdwType(optParameterAsString(++iToken));
8851       if (vdwType == null) {
8852         iToken = index;
8853         vdwType = VDW.AUTO;
8854       }
8855     }
8856     return new RadiusData(null, value, factorType, vdwType);
8857   }
8858 
8859   /**
8860    * Accepts a float array and expands [1 -3] to [1 2 3], for example.
8861    *
8862    * @param a
8863    * @param min
8864    * @param asBS
8865    * @return float[] or BS
8866    * @throws ScriptException
8867    */
expandFloatArray(float[] a, int min, boolean asBS)8868   public Object expandFloatArray(float[] a, int min, boolean asBS) throws ScriptException {
8869     int n = a.length;
8870     boolean haveNeg = false;
8871     BS bs = (asBS ? new BS() : null);
8872     try {
8873       for (int i = 0; i < a.length; i++)
8874         if (a[i] < 0) {
8875           n += Math.abs(a[i - 1] + a[i]) - 1; // 100 - 102 or 11 - 3
8876           haveNeg = true;
8877         }
8878       if (haveNeg) {
8879         float[] b = (asBS ? null : new float[n]);
8880         for (int pt = 0, i = 0; i < a.length; i++) {
8881           n = (int) a[i];
8882           if (n >= 0) {
8883             if (n < min)
8884               invArg();
8885             if (asBS)
8886               bs.set(n - 1);
8887             else
8888               b[pt++] = n;
8889           } else {
8890             int j = (int) a[i - 1];
8891             int dir = (j <= -n ? 1 : -1);
8892             for (int j2 = -n; j != j2; j += dir, pt++)
8893               if (!asBS)
8894                 b[pt] = j + dir;
8895               else
8896                 bs.set(j);
8897           }
8898         }
8899         a = b;
8900         if (!asBS)
8901           n = a.length;
8902       }
8903       if (asBS) {
8904         for (int i = n; --i >= 0;)
8905           bs.set((int) a[i] - 1);
8906         return bs;
8907       }
8908       int[] ia = new int[n];
8909       for (int i = n; --i >= 0;)
8910         ia[i] = (int) a[i];
8911       return ia;
8912     } catch (Exception e) {
8913       invArg();
8914       return null;
8915     }
8916   }
8917 
frameControl(int i)8918   private void frameControl(int i) throws ScriptException {
8919     switch (getToken(checkLast(i)).tok) {
8920     case T.playrev:
8921     case T.play:
8922     case T.resume:
8923     case T.pause:
8924     case T.next:
8925     case T.prev:
8926     case T.rewind:
8927     case T.first:
8928     case T.last:
8929       if (!chk)
8930         vwr.setAnimation(theTok);
8931       return;
8932     }
8933     invArg();
8934   }
8935 
getColorRange(int i)8936   public String getColorRange(int i) throws ScriptException {
8937     int color1 = getArgbParam(i);
8938     if (tokAt(++iToken) != T.to)
8939       invArg();
8940     int color2 = getArgbParam(++iToken);
8941     int nColors = (tokAt(iToken + 1) == T.integer ? intParameter(++iToken) : 0);
8942     return ColorEncoder.getColorSchemeList(ColorEncoder.getPaletteAtoB(color1,
8943         color2, nColors));
8944   }
8945 
getFullPathName()8946   public String getFullPathName() throws ScriptException {
8947     String filename = (!chk || isCmdLine_C_Option ? vwr
8948         .fm.getFullPathName(true) : "test.xyz");
8949     if (filename == null)
8950       invArg();
8951     return filename;
8952   }
8953 
getObjectBoundingBox(String id)8954   private P3[] getObjectBoundingBox(String id) {
8955     Object[] data = new Object[] { id, null, null };
8956     return (getShapePropertyData(JC.SHAPE_ISOSURFACE, "getBoundingBox", data)
8957         || getShapePropertyData(JC.SHAPE_PMESH, "getBoundingBox", data)
8958         || getShapePropertyData(JC.SHAPE_CONTACT, "getBoundingBox", data)
8959         || getShapePropertyData(JC.SHAPE_NBO, "getBoundingBox", data)
8960         || getShapePropertyData(JC.SHAPE_MO, "getBoundingBox", data) ? (P3[]) data[2]
8961         : null);
8962   }
8963 
getObjectCenter(String axisID, int index, int modelIndex)8964   protected P3 getObjectCenter(String axisID, int index, int modelIndex) {
8965 
8966     // called by ScriptParam
8967 
8968     Object[] data = new Object[] { axisID, Integer.valueOf(index),
8969         Integer.valueOf(modelIndex) };
8970     return (getShapePropertyData(JC.SHAPE_DRAW, "getCenter", data)
8971         || getShapePropertyData(JC.SHAPE_ISOSURFACE, "getCenter", data)
8972         || getShapePropertyData(JC.SHAPE_PMESH, "getCenter", data)
8973         || getShapePropertyData(JC.SHAPE_CONTACT, "getCenter", data)
8974         || getShapePropertyData(JC.SHAPE_NBO, "getCenter", data)
8975         || getShapePropertyData(JC.SHAPE_MO, "getCenter", data) ? (P3) data[2]
8976         : null);
8977   }
8978 
getPlaneForObject(String id, V3 vAB)8979   protected P4 getPlaneForObject(String id, V3 vAB) {
8980 
8981     // called by ScriptParam
8982 
8983     int shapeType = sm.getShapeIdFromObjectName(id);
8984     switch (shapeType) {
8985     case JC.SHAPE_DRAW:
8986       setShapeProperty(JC.SHAPE_DRAW, "thisID", id);
8987       T3[] points = (T3[]) getShapeProperty(JC.SHAPE_DRAW, "vertices");
8988       if (points == null || points.length < 3 || points[0] == null
8989           || points[1] == null || points[2] == null)
8990         break;
8991       return Measure.getPlaneThroughPoints(points[0], points[1], points[2],
8992           new V3(), vAB, new P4());
8993     case JC.SHAPE_ISOSURFACE:
8994       setShapeProperty(JC.SHAPE_ISOSURFACE, "thisID", id);
8995       return (P4) getShapeProperty(JC.SHAPE_ISOSURFACE, "plane");
8996     }
8997     return null;
8998   }
8999 
9000   @SuppressWarnings("unchecked")
getQuaternionArray(Object quaternionOrSVData, int itype)9001   public Quat[] getQuaternionArray(Object quaternionOrSVData, int itype) {
9002     Quat[] data;
9003     switch (itype) {
9004     case T.quaternion:
9005       data = (Quat[]) quaternionOrSVData;
9006       break;
9007     case T.point4f:
9008       P4[] pts = (P4[]) quaternionOrSVData;
9009       data = new Quat[pts.length];
9010       for (int i = 0; i < pts.length; i++)
9011         data[i] = Quat.newP4(pts[i]);
9012       break;
9013     case T.list:
9014       Lst<SV> sv = (Lst<SV>) quaternionOrSVData;
9015       data = new Quat[sv.size()];
9016       for (int i = 0; i < sv.size(); i++) {
9017         P4 pt = SV.pt4Value(sv.get(i));
9018         if (pt == null)
9019           return null;
9020         data[i] = Quat.newP4(pt);
9021       }
9022       break;
9023     default:
9024       return null;
9025     }
9026     return data;
9027   }
9028 
getSetAxesTypeMad10(int index)9029   public int getSetAxesTypeMad10(int index) throws ScriptException {
9030     if (index == slen)
9031       return 1;
9032     switch (getToken(checkLast(index)).tok) {
9033     case T.on:
9034       return 1;
9035     case T.off:
9036       return 0;
9037     case T.dotted:
9038       return -1;
9039     case T.integer:
9040       return intParameterRange(index, -1, 19);
9041     case T.decimal:
9042       float angstroms = floatParameterRange(index, 0, 2);
9043       return (Float.isNaN(angstroms) ? Integer.MAX_VALUE : (int) Math.floor(angstroms * 10000 * 2));
9044     }
9045     if (!chk)
9046       errorStr(ERROR_booleanOrWhateverExpected, "\"DOTTED\"");
9047     return 0;
9048   }
9049 
getSettingFloat(int pt)9050   private float getSettingFloat(int pt) throws ScriptException {
9051     return (pt >= slen ? Float.NaN : SV.fValue(parameterExpressionToken(pt)));
9052 }
9053 
getSettingInt(int pt)9054   private int getSettingInt(int pt) throws ScriptException {
9055     return (pt >= slen ? Integer.MIN_VALUE : parameterExpressionToken(pt)
9056         .asInt());
9057   }
9058 
9059   /**
9060    * Accept an unquoted string if there is just one parameter regardless of its
9061    * type. In other words, these commands cannot accept a variable name by
9062    * itself.
9063    *
9064    * @param pt
9065    * @param isJmolSet
9066    * @return string parameter
9067    * @throws ScriptException
9068    */
getSettingStr(int pt, boolean isJmolSet)9069   private String getSettingStr(int pt, boolean isJmolSet)
9070       throws ScriptException {
9071     return (isJmolSet && slen == pt + 1 ? paramAsStr(pt)
9072         : parameterExpressionToken(pt).asString());
9073   }
9074 
getShapeProperty(int shapeType, String propertyName)9075   public Object getShapeProperty(int shapeType, String propertyName) {
9076     return sm.getShapePropertyIndex(shapeType, propertyName, Integer.MIN_VALUE);
9077   }
9078 
getShapePropertyData(int shapeType, String propertyName, Object[] data)9079   public boolean getShapePropertyData(int shapeType, String propertyName,
9080                                       Object[] data) {
9081     return sm.getShapePropertyData(shapeType, propertyName, data);
9082   }
9083 
getShapeType(int tok)9084   private int getShapeType(int tok) throws ScriptException {
9085     int iShape = JC.shapeTokenIndex(tok);
9086     if (iShape < 0)
9087       error(ERROR_unrecognizedObject);
9088     return iShape;
9089   }
9090 
getTranslucentLevel(int i)9091   public float getTranslucentLevel(int i) throws ScriptException {
9092     float f = floatParameter(i);
9093     return (theTok == T.integer && f > 0 && f < 9 ? f + 1 : f);
9094   }
9095 
getZoom(int ptCenter, int i, BS bs, float currentZoom)9096   private float getZoom(int ptCenter, int i, BS bs, float currentZoom)
9097       throws ScriptException {
9098     // where [zoom factor] is [0|n|+n|-n|*n|/n|IN|OUT]
9099 
9100     float zoom = (isFloatParameter(i) ? floatParameter(i++) : Float.NaN);
9101     if (zoom == 0 || currentZoom == 0) {
9102       // moveTo/zoom/zoomTo {center} 0
9103       float r = Float.NaN;
9104       if (bs == null) {
9105         if (tokAt(ptCenter) == T.dollarsign) {
9106           P3[] bbox = getObjectBoundingBox(objectNameParameter(ptCenter + 1));
9107           if (bbox == null || (r = bbox[0].distance(bbox[1]) / 2) == 0)
9108             invArg();
9109         }
9110       } else {
9111         r = vwr.ms.calcRotationRadiusBs(bs);
9112       }
9113       if (Float.isNaN(r))
9114         invArg();
9115       currentZoom = vwr.getFloat(T.rotationradius) / r * 100;
9116       zoom = Float.NaN;
9117     }
9118     if (zoom < 0) {
9119       // moveTo/zoom/zoomTo -factor
9120       zoom += currentZoom;
9121     } else if (Float.isNaN(zoom)) {
9122       // moveTo/zoom/zoomTo [optional {center}] percent|+factor|*factor|/factor
9123       // moveTo/zoom/zoomTo {center} 0 [optional
9124       // -factor|+factor|*factor|/factor]
9125       int tok = tokAt(i);
9126       switch (tok) {
9127       case T.out:
9128       case T.in:
9129         zoom = currentZoom * (tok == T.out ? 0.5f : 2f);
9130         i++;
9131         break;
9132       case T.divide:
9133       case T.times:
9134       case T.plus:
9135         float value = floatParameter(++i);
9136         i++;
9137         switch (tok) {
9138         case T.divide:
9139           zoom = currentZoom / value;
9140           break;
9141         case T.times:
9142           zoom = currentZoom * value;
9143           break;
9144         case T.plus:
9145           zoom = currentZoom + value;
9146           break;
9147         }
9148         break;
9149       default:
9150         // indicate no factor indicated
9151         zoom = (bs == null ? -currentZoom : currentZoom);
9152       }
9153     }
9154     iToken = i - 1;
9155     return zoom;
9156   }
9157 
setElementColor(String str, int argb)9158   private boolean setElementColor(String str, int argb) {
9159     for (int i = Elements.elementNumberMax; --i >= 0;) {
9160       if (str.equalsIgnoreCase(Elements.elementNameFromNumber(i))) {
9161         if (!chk)
9162           vwr.setElementArgb(i, argb);
9163         return true;
9164       }
9165     }
9166     for (int i = Elements.altElementMax; --i >= 0;) {
9167       if (str.equalsIgnoreCase(Elements.altElementNameFromIndex(i))) {
9168         if (!chk)
9169           vwr.setElementArgb(Elements.altElementNumberFromIndex(i), argb);
9170         return true;
9171       }
9172     }
9173     if (str.charAt(0) != '_')
9174       return false;
9175     for (int i = Elements.elementNumberMax; --i >= 0;) {
9176       if (str.equalsIgnoreCase("_" + Elements.elementSymbolFromNumber(i))) {
9177         if (!chk)
9178           vwr.setElementArgb(i, argb);
9179         return true;
9180       }
9181     }
9182     for (int i = Elements.altElementMax; --i >= Elements.firstIsotope;) {
9183       if (str.equalsIgnoreCase("_" + Elements.altElementSymbolFromIndex(i))) {
9184         if (!chk)
9185           vwr.setElementArgb(Elements.altElementNumberFromIndex(i), argb);
9186         return true;
9187       }
9188       if (str.equalsIgnoreCase("_" + Elements.altIsotopeSymbolFromIndex(i))) {
9189         if (!chk)
9190           vwr.setElementArgb(Elements.altElementNumberFromIndex(i), argb);
9191         return true;
9192       }
9193     }
9194     return false;
9195   }
9196 
9197   /**
9198    * @param shape
9199    * @param i
9200    * @param tok
9201    * @return true if successful
9202    * @throws ScriptException
9203    */
setMeshDisplayProperty(int shape, int i, int tok)9204   public boolean setMeshDisplayProperty(int shape, int i, int tok)
9205       throws ScriptException {
9206     String propertyName = null;
9207     Object propertyValue = null;
9208     boolean allowCOLOR = (shape == JC.SHAPE_CONTACT);
9209     boolean checkOnly = (i == 0);
9210     // these properties are all processed in MeshCollection.java
9211     if (!checkOnly)
9212       tok = getToken(i).tok;
9213     switch (tok) {
9214     case T.color:
9215       if (allowCOLOR)
9216         iToken++;
9217       else
9218         break;
9219       //$FALL-THROUGH$
9220     case T.opaque:
9221     case T.translucent:
9222       if (!checkOnly)
9223         colorShape(shape, iToken, false);
9224       return true;
9225     case T.nada:
9226     case T.delete:
9227     case T.on:
9228     case T.off:
9229     case T.hide:
9230     case T.hidden:
9231     case T.display:
9232     case T.displayed:
9233       if (iToken == 1 && shape >= 0 && tokAt(2) == T.nada)
9234         setShapeProperty(shape, "thisID", null);
9235       if (tok == T.nada)
9236         return (iToken == 1);
9237       if (checkOnly)
9238         return true;
9239       switch (tok) {
9240       case T.delete:
9241         setShapeProperty(shape, "delete", null);
9242         return true;
9243       case T.hidden:
9244       case T.hide:
9245         tok = T.off;
9246         break;
9247       case T.displayed:
9248         tok = T.on;
9249         break;
9250       case T.display:
9251         if (i + 1 == slen)
9252           tok = T.on;
9253         break;
9254       }
9255       //$FALL-THROUGH$ for on/off/display
9256     case T.frontlit:
9257     case T.backlit:
9258     case T.fullylit:
9259     case T.contourlines:
9260     case T.nocontourlines:
9261     case T.dots:
9262     case T.nodots:
9263     case T.mesh:
9264     case T.nomesh:
9265     case T.fill:
9266     case T.nofill:
9267     case T.backshell:
9268     case T.nobackshell:
9269     case T.triangles:
9270     case T.notriangles:
9271     case T.frontonly:
9272     case T.notfrontonly:
9273       propertyName = "token";
9274       propertyValue = Integer.valueOf(tok);
9275       break;
9276     }
9277     if (propertyName == null)
9278       return false;
9279     if (checkOnly)
9280       return true;
9281     setShapeProperty(shape, propertyName, propertyValue);
9282     if ((tokAt(iToken + 1)) != T.nada) {
9283       if (!setMeshDisplayProperty(shape, ++iToken, 0))
9284         --iToken;
9285     }
9286     return true;
9287   }
9288 
setObjectArgb(String str, int argb)9289   private void setObjectArgb(String str, int argb) {
9290     if (chk)
9291       return;
9292     vwr.setObjectArgb(str, argb);
9293   }
9294 
setObjectMad10(int iShape, String name, int mad10)9295   public void setObjectMad10(int iShape, String name, int mad10) {
9296     if (!chk)
9297       vwr.setObjectMad10(iShape, name, mad10);
9298   }
9299 
setObjectProp(String id, int tokCommand, int ptColor)9300   private String setObjectProp(String id, int tokCommand, int ptColor)
9301       throws ScriptException {
9302     Object[] data = new Object[] { id, null };
9303     String s = "";
9304     boolean isWild = PT.isWild(id);
9305     for (int iShape = JC.SHAPE_DIPOLES;;) {
9306       if (getShapePropertyData(iShape, "checkID", data)) {
9307         setShapeProperty(iShape, "thisID", id);
9308         switch (tokCommand) {
9309         case T.delete:
9310           setShapeProperty(iShape, "delete", null);
9311           break;
9312         case T.hide:
9313         case T.display:
9314           setShapeProperty(iShape, "hidden",
9315               tokCommand == T.display ? Boolean.FALSE : Boolean.TRUE);
9316           break;
9317         case T.show:
9318           //if (iShape == JmolConstants.SHAPE_ISOSURFACE && !isWild)
9319           //return getIsosurfaceJvxl(false, JmolConstants.SHAPE_ISOSURFACE);
9320           //else if (iShape == JmolConstants.SHAPE_PMESH && !isWild)
9321           //return getIsosurfaceJvxl(true, JmolConstants.SHAPE_PMESH);
9322           s += (String) getShapeProperty(iShape, "command") + "\n";
9323           break;
9324         case T.color:
9325           if (ptColor >= 0)
9326             colorShape(iShape, ptColor + 1, false);
9327           break;
9328         }
9329         if (!isWild)
9330           break;
9331       }
9332       switch (iShape) {
9333       case JC.SHAPE_DIPOLES:
9334         iShape = JC.SHAPE_ELLIPSOIDS;
9335         continue;
9336       case JC.SHAPE_ELLIPSOIDS:
9337         iShape = JC.SHAPE_MAX_HAS_ID;
9338       }
9339       switch (--iShape) {
9340       // skip MO and NBO?
9341       case JC.SHAPE_MO:
9342         iShape--;
9343         break;
9344       case JC.SHAPE_NBO:
9345         iShape -= 2;
9346         break;
9347       }
9348       if (iShape < JC.SHAPE_MIN_HAS_ID)
9349         break;
9350     }
9351     return s;
9352   }
9353 
setObjectProperty()9354   public String setObjectProperty() throws ScriptException {
9355     // also called by show command, in ScriptExt
9356     String id = setShapeNameParameter(2);
9357     return (chk ? "" : setObjectProp(id, tokAt(0), iToken));
9358   }
9359 
setShapeNameParameter(int i)9360   public String setShapeNameParameter(int i) throws ScriptException {
9361     String id = paramAsStr(i);
9362     boolean isWild = id.equals("*");
9363     if (id.length() == 0)
9364       invArg();
9365     if (isWild) {
9366       switch (tokAt(i + 1)) {
9367       case T.nada:
9368       case T.on:
9369       case T.off:
9370       case T.displayed:
9371       case T.hidden:
9372       case T.color:
9373       case T.delete:
9374         break;
9375       default:
9376         if (setMeshDisplayProperty(-1, 0, tokAt(i + 1)))
9377           break;
9378         id += optParameterAsString(++i);
9379       }
9380     }
9381     if (tokAt(i + 1) == T.times)
9382       id += paramAsStr(++i);
9383     iToken = i;
9384     return id;
9385   }
9386 
setShapeProperty(int shapeType, String propertyName, Object propertyValue)9387   public void setShapeProperty(int shapeType, String propertyName,
9388                                Object propertyValue) {
9389     if (!chk)
9390       sm.setShapePropertyBs(shapeType, propertyName, propertyValue, null);
9391   }
9392 
setShapePropertyBs(int iShape, String propertyName, Object propertyValue, BS bs)9393   public void setShapePropertyBs(int iShape, String propertyName,
9394                                  Object propertyValue, BS bs) {
9395     if (!chk)
9396       sm.setShapePropertyBs(iShape, propertyName, propertyValue, bs);
9397   }
9398 
setShapeSize(int shapeType, RadiusData rd)9399   private void setShapeSize(int shapeType, RadiusData rd) {
9400     if (!chk)
9401       sm.setShapeSizeBs(shapeType, 0, rd, null);
9402   }
9403 
setShapeSizeBs(int shapeType, int size, BS bs)9404   public void setShapeSizeBs(int shapeType, int size, BS bs) {
9405     // stars, halos, balls only
9406     if (!chk)
9407       sm.setShapeSizeBs(shapeType, size, null, bs);
9408   }
9409 
setShapeTranslucency(int shapeType, String prefix, String translucency, float translucentLevel, BS bs)9410   public void setShapeTranslucency(int shapeType, String prefix,
9411                                    String translucency, float translucentLevel,
9412                                    BS bs) {
9413     if (translucentLevel == Float.MAX_VALUE)
9414       translucentLevel = vwr.getFloat(T.defaulttranslucent);
9415     setShapeProperty(shapeType, "translucentLevel",
9416         Float.valueOf(translucentLevel));
9417     if (prefix == null)
9418       return;
9419     if (bs == null)
9420       setShapeProperty(shapeType, prefix + "translucency", translucency);
9421     else if (!chk)
9422       setShapePropertyBs(shapeType, prefix + "translucency", translucency, bs);
9423   }
9424 
setSize(int shape, float scale)9425   private void setSize(int shape, float scale) throws ScriptException {
9426     // halo star spacefill
9427     RadiusData rd = null;
9428     int tok = tokAt(1);
9429     boolean isOnly = false;
9430     switch (tok) {
9431     case T.only:
9432       restrictSelected(false, false);
9433       //$FALL-THROUGH$
9434     case T.on:
9435       break;
9436     case T.off:
9437       scale = 0;
9438       break;
9439     case T.decimal:
9440       isOnly = (floatParameter(1) < 0);
9441       //$FALL-THROUGH$
9442     case T.integer:
9443     default:
9444       rd = encodeRadiusParameter(1, isOnly, true);
9445       if (rd == null)
9446         return;
9447       if (Float.isNaN(rd.value))
9448         invArg();
9449     }
9450     if (rd == null)
9451       rd = new RadiusData(null, scale, EnumType.FACTOR, VDW.AUTO);
9452     if (isOnly)
9453       restrictSelected(false, false);
9454     setShapeSize(shape, rd);
9455   }
9456 
setSizeBio(int iShape)9457   private void setSizeBio(int iShape) throws ScriptException {
9458     int mad = 0;
9459     // token has ondefault1
9460     switch (getToken(1).tok) {
9461     case T.only:
9462       restrictSelected(false, false);
9463       //$FALL-THROUGH$
9464     case T.on:
9465       mad = -1; // means take default
9466       break;
9467     case T.off:
9468       break;
9469     case T.structure:
9470       mad = -2;
9471       break;
9472     case T.temperature:
9473     case T.displacement:
9474       mad = -4;
9475       break;
9476     case T.integer:
9477       if ((mad = (intParameterRange(1, 0, 1000) * 8)) == Integer.MAX_VALUE)
9478           return;
9479       break;
9480     case T.decimal:
9481       mad = Math.round(floatParameterRange(1, -Shape.RADIUS_MAX,
9482           Shape.RADIUS_MAX) * 2000);
9483       if (mad == Integer.MAX_VALUE)
9484         return;
9485       if (mad < 0) {
9486         restrictSelected(false, false);
9487         mad = -mad;
9488       }
9489       break;
9490     case T.bitset:
9491       if (!chk)
9492         sm.loadShape(iShape);
9493       setShapeProperty(iShape, "bitset", theToken.value);
9494       return;
9495     default:
9496       error(ERROR_booleanOrNumberExpected);
9497     }
9498     setShapeSizeBs(iShape, mad, null);
9499   }
9500 
setUnits(String units, int tok)9501   private boolean setUnits(String units, int tok) throws ScriptException {
9502     if (tok == T.measurementunits
9503         && (units.toLowerCase().endsWith("hz") || PT.isOneOf(units.toLowerCase(),
9504             ";angstroms;au;bohr;nanometers;nm;picometers;pm;vanderwaals;vdw;"))) {
9505       if (!chk)
9506         vwr.setUnits(units, true);
9507     } else if (tok == T.energyunits
9508         && PT.isOneOf(units.toLowerCase(), ";kcal;kj;")) {
9509       if (!chk)
9510         vwr.setUnits(units, false);
9511     } else {
9512       errorStr2(ERROR_unrecognizedParameter, "set " + T.nameOf(tok), units);
9513     }
9514     return true;
9515   }
9516 
9517 
9518 
9519   @Override
toString()9520   public String toString() {
9521     SB str = new SB();
9522     str.append("Eval\n pc:");
9523     str.appendI(pc);
9524     str.append("\n");
9525     str.appendI(aatoken.length);
9526     str.append(" statements\n");
9527     for (int i = 0; i < aatoken.length; ++i) {
9528       str.append("----\n");
9529       T[] atoken = aatoken[i];
9530       for (int j = 0; j < atoken.length; ++j) {
9531         str.appendO(atoken[j]);
9532         str.appendC('\n');
9533       }
9534       str.appendC('\n');
9535     }
9536     str.append("END\n");
9537     return str.toString();
9538   }
9539 
9540 }
9541