1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2016-07-13 14:51:46 -0500 (Wed, 13 Jul 2016) $
4  * $Revision: 19253 $
5  *
6  * Copyright (C) 2002-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.viewer;
25 
26 import java.io.BufferedInputStream;
27 import java.io.BufferedReader;
28 import java.io.IOException;
29 import java.io.Reader;
30 import java.net.URL;
31 import java.util.Arrays;
32 import java.util.Hashtable;
33 import java.util.Iterator;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 import java.util.Properties;
37 
38 import org.jmol.adapter.readers.quantum.NBOParser;
39 import org.jmol.adapter.smarter.SmarterJmolAdapter;
40 import org.jmol.api.AtomIndexIterator;
41 import org.jmol.api.GenericMenuInterface;
42 import org.jmol.api.GenericMouseInterface;
43 import org.jmol.api.GenericPlatform;
44 import org.jmol.api.Interface;
45 import org.jmol.api.JmolAdapter;
46 import org.jmol.api.JmolAnnotationParser;
47 import org.jmol.api.JmolAppConsoleInterface;
48 import org.jmol.api.JmolCallbackListener;
49 import org.jmol.api.JmolDataManager;
50 import org.jmol.api.JmolInChI;
51 import org.jmol.api.JmolJSpecView;
52 import org.jmol.api.JmolNMRInterface;
53 import org.jmol.api.JmolPropertyManager;
54 import org.jmol.api.JmolRendererInterface;
55 import org.jmol.api.JmolRepaintManager;
56 import org.jmol.api.JmolScriptEditorInterface;
57 import org.jmol.api.JmolScriptEvaluator;
58 import org.jmol.api.JmolScriptFunction;
59 import org.jmol.api.JmolScriptManager;
60 import org.jmol.api.JmolSelectionListener;
61 import org.jmol.api.JmolStatusListener;
62 import org.jmol.api.JmolViewer;
63 import org.jmol.api.PlatformViewer;
64 import org.jmol.api.SmilesMatcherInterface;
65 import org.jmol.api.SymmetryInterface;
66 import org.jmol.api.js.JSmolAppletObject;
67 import org.jmol.api.js.JmolToJSmolInterface;
68 import org.jmol.atomdata.AtomData;
69 import org.jmol.atomdata.AtomDataServer;
70 import org.jmol.atomdata.RadiusData;
71 import org.jmol.atomdata.RadiusData.EnumType;
72 import org.jmol.awtjs.Event;
73 import org.jmol.c.FIL;
74 import org.jmol.c.STER;
75 import org.jmol.c.STR;
76 import org.jmol.c.VDW;
77 import org.jmol.i18n.GT;
78 import org.jmol.minimize.Minimizer;
79 import org.jmol.modelkit.ModelKitPopup;
80 import org.jmol.modelset.Atom;
81 import org.jmol.modelset.AtomCollection;
82 import org.jmol.modelset.Bond;
83 import org.jmol.modelset.LabelToken;
84 import org.jmol.modelset.Measurement;
85 import org.jmol.modelset.MeasurementData;
86 import org.jmol.modelset.MeasurementPending;
87 import org.jmol.modelset.Model;
88 import org.jmol.modelset.ModelSet;
89 import org.jmol.modelset.Orientation;
90 import org.jmol.modelset.StateScript;
91 import org.jmol.modelsetbio.BioResolver;
92 import org.jmol.script.SV;
93 import org.jmol.script.ScriptContext;
94 import org.jmol.script.ScriptEval;
95 import org.jmol.script.T;
96 import org.jmol.thread.TimeoutThread;
97 import org.jmol.util.BSUtil;
98 import org.jmol.util.BoxInfo;
99 import org.jmol.util.C;
100 import org.jmol.util.CommandHistory;
101 import org.jmol.util.Elements;
102 import org.jmol.util.Escape;
103 import org.jmol.util.Font;
104 import org.jmol.util.GData;
105 import org.jmol.util.JmolMolecule;
106 import org.jmol.util.Logger;
107 import org.jmol.util.Node;
108 import org.jmol.util.Parser;
109 import org.jmol.util.Rectangle;
110 import org.jmol.util.TempArray;
111 import org.jmol.util.Triangulator;
112 import org.jmol.viewer.binding.Binding;
113 
114 import javajs.J2SIgnoreImport;
115 import javajs.api.GenericCifDataParser;
116 import javajs.api.GenericZipTools;
117 import javajs.util.AU;
118 import javajs.util.BS;
119 import javajs.util.CU;
120 import javajs.util.DF;
121 import javajs.util.JSJSONParser;
122 import javajs.util.Lst;
123 import javajs.util.M3;
124 import javajs.util.M4;
125 import javajs.util.OC;
126 import javajs.util.P3;
127 import javajs.util.P3i;
128 import javajs.util.P4;
129 import javajs.util.PT;
130 import javajs.util.Quat;
131 import javajs.util.Rdr;
132 import javajs.util.SB;
133 import javajs.util.T3;
134 import javajs.util.V3;
135 
136 /*
137  *
138  * ****************************************************************
139  * The JmolViewer can be used to render client molecules. Clients implement the
140  * JmolAdapter. JmolViewer uses this interface to extract information from the
141  * client data structures and render the molecule to the supplied
142  * java.awt.Component
143  *
144  * The JmolViewer runs on Java 1.5+ virtual machines. The 3d graphics rendering
145  * package is a software implementation of a z-buffer. It does not use Java3D
146  * and does not use Graphics2D from Java 1.2.
147  *
148  * public here is a test for applet-applet and JS-applet communication the idea
149  * being that applet.getProperty("jmolViewer") returns this Viewer object,
150  * allowing direct inter-process access to public methods.
151  *
152  * e.g.
153  *
154  * applet.getProperty("jmolApplet").getFullPathName()
155  *
156  *
157  * This vwr can also be used with JmolData.jar, which is a
158  * frameless version of Jmol that can be used to batch-process
159  * scripts from the command line. No shapes, no labels, no export
160  * to JPG -- just raw data checking and output.
161  *
162  *
163  * NOSCRIPTING option: 2/2013
164  *
165  * This option provides a smaller load footprint for JavaScript JSmol
166  * and disallows:
167  *
168  *   scripting
169  *   modelKitMode
170  *   slabbing of read JVXL files
171  *   calculate hydrogens
172  *
173  *
174  * ****************************************************************
175  */
176 
177 @J2SIgnoreImport({ Runtime.class })
178 public class Viewer extends JmolViewer
179     implements AtomDataServer, PlatformViewer {
180 
181   public final static boolean nullDeletedAtoms = false; // only Jmol 15.2+ for now until better tested
182 
183   public boolean testAsync;// = true; // testing only
184 
185   static {
186     /**
187      * allows customization of Viewer -- not implemented in JSmol.
188      *
189      * @j2sNative
190      *
191      *            self.Jmol && Jmol.extend && Jmol.extend("vwr",
192      *            org.jmol.viewer.Viewer.prototype);
193      *
194      */
195     {
196     }
197   }
198 
199   @Override
finalize()200   protected void finalize() throws Throwable {
201     if (Logger.debugging)
202       Logger.debug("vwr finalize " + this);
203     super.finalize();
204   }
205 
206   // these are all private now so we are certain they are not
207   // being accesed by any other classes
208 
209   public boolean autoExit = false;
210   public boolean haveDisplay = false;
211 
212   static public boolean isJS, isSwingJS, isWebGL;
213   public boolean isJSNoAWT;
214   public boolean isSingleThreaded;
215   public boolean queueOnHold = false;
216 
217   public String fullName = "";
218   public static String appletDocumentBase = "";
219   public static String appletCodeBase = "";
220   public static String appletIdiomaBase;
221 
222   public static String jsDocumentBase = "";
223 
224   public enum ACCESS {
225     NONE, READSPT, ALL
226   }
227 
228   public Object compiler;
229   public Map<String, Object> definedAtomSets;
230   public ModelSet ms;
231   public FileManager fm;
232 
233   public boolean isApplet, isJNLP;
234 
235   public boolean isSyntaxAndFileCheck = false;
236   public boolean isSyntaxCheck = false;
237   public boolean listCommands = false;
238   boolean mustRender = false;
239 
240   public String htmlName = "";
241   public String appletName = "";
242 
243   public int tryPt;
244 
245   private String insertedCommand = "";
246 
setInsertedCommand(String strScript)247   public void setInsertedCommand(String strScript) {
248     insertedCommand = strScript;
249   }
250 
251   public GData gdata;
252   public JSmolAppletObject html5Applet; // j2s only
253   public static JmolToJSmolInterface jmolObject;
254 
255   public ActionManager acm;
256   public AnimationManager am;
257   public ColorManager cm;
258   JmolDataManager dm;
259   public ShapeManager shm;
260   public SelectionManager slm;
261   JmolRepaintManager rm;
262   public GlobalSettings g;
263   public StatusManager sm;
264   public TransformManager tm;
265 
266   public static String strJavaVendor = "Java: "
267       + System.getProperty("java.vendor", "j2s");
268   public static String strOSName = System.getProperty("os.name", "");
269   public static String strJavaVersion = "Java "
270       + System.getProperty("java.version", "");
271 
272   String syncId = "";
273   String logFilePath = "";
274 
275   private boolean allowScripting;
276   public boolean isPrintOnly;
277   public boolean isSignedApplet = false;
278   private boolean isSignedAppletLocal = false;
279   private boolean isSilent;
280   private boolean multiTouch;
281   public boolean noGraphicsAllowed;
282   private boolean useCommandThread = false;
283 
284   private String commandOptions;
285   public Map<String, Object> vwrOptions;
286   public Object display;
287   private JmolAdapter modelAdapter;
288   private ACCESS access;
289   private CommandHistory commandHistory;
290 
291   public ModelManager mm;
292   public StateManager stm;
293   private JmolScriptManager scm;
294   public JmolScriptEvaluator eval;
295   private TempArray tempArray;
296 
297   public boolean allowArrayDotNotation;
298   public boolean async;
299   public Object executor;
300 
301   private static String version_date;
302 
getJmolVersion()303   public static String getJmolVersion() {
304     return (version_date == null ? version_date = JC.version + "  " + JC.date
305         : version_date);
306   }
307 
308   /**
309    * old way...
310    *
311    * @param display
312    * @param modelAdapter
313    * @param fullName
314    * @param documentBase
315    * @param codeBase
316    * @param commandOptions
317    * @param statusListener
318    * @param implementedPlatform
319    * @return JmolViewer object
320    */
allocateViewer(Object display, JmolAdapter modelAdapter, String fullName, URL documentBase, URL codeBase, String commandOptions, JmolStatusListener statusListener, GenericPlatform implementedPlatform)321   protected static JmolViewer allocateViewer(Object display,
322                                              JmolAdapter modelAdapter,
323                                              String fullName, URL documentBase,
324                                              URL codeBase,
325                                              String commandOptions,
326                                              JmolStatusListener statusListener,
327                                              GenericPlatform implementedPlatform) {
328 
329     Map<String, Object> info = new Hashtable<String, Object>();
330     info.put("display", display);
331     info.put("adapter", modelAdapter);
332     info.put("statusListener", statusListener);
333     info.put("platform", implementedPlatform);
334     info.put("options", commandOptions);
335     info.put("fullName", fullName);
336     info.put("documentBase", documentBase);
337     info.put("codeBase", codeBase);
338     return new Viewer(info);
339   }
340 
341   int screenWidth, screenHeight;
342   final Lst<String> actionStates;
343   final Lst<String> actionStatesRedo;
344   VDW defaultVdw;
345 
346   public RadiusData rd;
347   public Map<Object, Object> chainMap;
348   private Lst<String> chainList;
349   private String errorMessage;
350   private String errorMessageUntranslated;
351   private double privateKey;
352   private boolean dataOnly;
353 
354   /**
355    * new way...
356    *
357    * @param info
358    *        "display" "adapter" "statusListener" "platform" "options" "fullName"
359    *        "documentBase" "codeBase" "multiTouch" [options] "noGraphics"
360    *        "printOnly" "previewOnly" "debug" "applet" "signedApplet"
361    *        "appletProxy" "useCommandThread" "platform" [option]
362    *        "backgroundTransparent" "exit" "listCommands" "check" "checkLoad"
363    *        "silent" "access:READSPT" "access:NONE" "menuFile"
364    *        "headlessMaxTimeMs" "headlessImage" "isDataOnly" "async"
365    **/
366 
Viewer(Map<String, Object> info)367   public Viewer(Map<String, Object> info) {
368     commandHistory = new CommandHistory();
369     rd = new RadiusData(null, 0, null, null);
370     defaultVdw = VDW.JMOL;
371     localFunctions = new Hashtable<String, JmolScriptFunction>();
372     privateKey = Math.random();
373     actionStates = new Lst<String>();
374     actionStatesRedo = new Lst<String>();
375     chainMap = new Hashtable<Object, Object>();
376     chainList = new Lst<String>();
377     setOptions(info);
378   }
379 
haveAccess(ACCESS a)380   public boolean haveAccess(ACCESS a) {
381     // disables WRITE, LOAD file:/, set logFile
382     // command line -g and -w options ARE available for final writing of image
383     return access == a;
384   }
385 
386   @Override
getModelAdapter()387   public JmolAdapter getModelAdapter() {
388     return (modelAdapter == null ? modelAdapter = new SmarterJmolAdapter()
389         : modelAdapter);
390   }
391 
392   @Override
getSmartsMatch(String smarts, BS bsSelected)393   public BS getSmartsMatch(String smarts, BS bsSelected) throws Exception {
394     if (bsSelected == null)
395       bsSelected = bsA();
396     return getSmilesMatcher().getSubstructureSet(smarts, ms.at, ms.ac,
397         bsSelected, JC.SMILES_TYPE_SMARTS);
398   }
399 
getSmartsMatchForNodes(String smarts, Node[] atoms)400   public BS getSmartsMatchForNodes(String smarts, Node[] atoms)
401       throws Exception {
402     return getSmilesMatcher().getSubstructureSet(smarts, atoms, atoms.length,
403         null, JC.SMILES_TYPE_SMARTS);
404   }
405 
406   /**
407    *
408    *
409    * @param smilesOrSmarts
410    * @param bsSelected
411    * @param flags
412    *        can be bitwise OR of JC.SMILES_* options, in particular,
413    *
414    *        JC.SMILES_TYPE_SMARTS, JC.SMILES_TYPE_SMILES, and
415    *        JC.SMILES_MAP_UNIQUE
416    *
417    * @return map
418    * @throws Exception
419    */
getSmartsMap(String smilesOrSmarts, BS bsSelected, int flags)420   public int[][] getSmartsMap(String smilesOrSmarts, BS bsSelected, int flags)
421       throws Exception {
422     if (bsSelected == null)
423       bsSelected = bsA();
424     if (flags == 0)
425       flags = JC.SMILES_TYPE_SMARTS;
426     return getSmilesMatcher().getCorrelationMaps(smilesOrSmarts, ms.at, ms.ac,
427         bsSelected, flags);
428   }
429 
430   @SuppressWarnings({ "unchecked", "null", "unused" })
setOptions(Map<String, Object> info)431   public void setOptions(Map<String, Object> info) {
432     // can be deferred
433     vwrOptions = info;
434     // could be a Component, or could be a JavaScript class
435     // use allocateViewer
436     if (Logger.debugging) {
437       Logger.debug("Viewer constructor " + this);
438     }
439     modelAdapter = (JmolAdapter) info.get("adapter");
440     JmolStatusListener statusListener = (JmolStatusListener) info
441         .get("statusListener");
442     fullName = (String) info.get("fullName");
443     if (fullName == null)
444       fullName = "";
445     Object o = info.get("codePath");
446     if (o == null)
447       o = "../java/";
448     appletCodeBase = o.toString();
449     appletIdiomaBase = appletCodeBase.substring(0,
450         appletCodeBase.lastIndexOf("/", appletCodeBase.length() - 2) + 1)
451         + "idioma";
452     o = info.get("documentBase");
453     appletDocumentBase = (o == null ? "" : o.toString());
454     o = info.get("options");
455     commandOptions = (o == null ? "" : o.toString());
456 
457     if (info.containsKey("debug") || commandOptions.indexOf("-debug") >= 0)
458       Logger.setLogLevel(Logger.LEVEL_DEBUG);
459     if (isApplet && info.containsKey("maximumSize"))
460       setMaximumSize(((Integer) info.get("maximumSize")).intValue());
461 
462     isJNLP = checkOption2("isJNLP", "-jnlp");
463     if (isJNLP)
464       Logger.info("setting JNLP mode TRUE");
465 
466     isSignedApplet = isJNLP || checkOption2("signedApplet", "-signed");
467     isApplet = isSignedApplet || checkOption2("applet", "-applet");
468     allowScripting = !checkOption2("noscripting", "-noscripting");
469     int i = fullName.indexOf("__");
470     htmlName = (i < 0 ? fullName : fullName.substring(0, i));
471     appletName = PT.split(htmlName + "_", "_")[0];
472     syncId = (i < 0 ? "" : fullName.substring(i + 2, fullName.length() - 2));
473     access = (checkOption2("access:READSPT", "-r") ? ACCESS.READSPT
474         : checkOption2("access:NONE", "-R") ? ACCESS.NONE : ACCESS.ALL);
475     isPreviewOnly = info.containsKey("previewOnly");
476     if (isPreviewOnly)
477       info.remove("previewOnly"); // see FilePreviewPanel
478     isPrintOnly = checkOption2("printOnly", "-p");
479     dataOnly = checkOption2("isDataOnly", "\0");
480     autoExit = checkOption2("exit", "-x");
481     o = info.get("platform");
482     String platform = "unknown";
483     if (o == null) {
484       o = (commandOptions.contains("platform=")
485           ? commandOptions.substring(commandOptions.indexOf("platform=") + 9)
486           : "org.jmol.awt.Platform");
487       // note that this must be the last option if give in commandOptions
488     }
489     if (o instanceof String) {
490       platform = (String) o;
491       isWebGL = (platform.indexOf(".awtjs.") >= 0);
492       isJS = isJSNoAWT = isWebGL || (platform.indexOf(".awtjs2d.") >= 0);
493       async = !dataOnly && !autoExit
494           && (testAsync || isJS && info.containsKey("async"));
495       JSmolAppletObject applet = null;
496       JmolToJSmolInterface jmol = null;
497       String javaver = "?";
498       /**
499        * @j2sNative
500        *
501        *            if(self.Jmol) { jmol = Jmol; applet =
502        *            Jmol._applets[this.htmlName.split("_object")[0]]; javaver =
503        *            Jmol._version; }
504        *
505        *
506        */
507       {
508         javaver = null;
509       }
510       if (javaver != null) {
511         html5Applet = applet;
512         jmolObject = jmol;
513         strJavaVersion = javaver;
514         strJavaVendor = "Java2Script " + (isWebGL ? "(WebGL)" : "(HTML5)");
515       }
516       o = Interface.getInterface(platform, this, "setOptions");
517     }
518     apiPlatform = (GenericPlatform) o;
519     display = info.get("display");
520 
521     isSingleThreaded = apiPlatform.isSingleThreaded();
522     noGraphicsAllowed = checkOption2("noDisplay", "-n");
523     headless = apiPlatform.isHeadless();
524     haveDisplay = (isWebGL
525         || display != null && !noGraphicsAllowed && !headless && !dataOnly);
526     noGraphicsAllowed &= (display == null);
527     headless |= noGraphicsAllowed;
528     if (haveDisplay) {
529       mustRender = true;
530       multiTouch = checkOption2("multiTouch", "-multitouch");
531       /**
532        * @j2sNative
533        *
534        *            if (!this.isWebGL) this.display =
535        *            document.getElementById(this.display);
536        */
537       {
538       }
539     } else {
540       display = null;
541     }
542     apiPlatform.setViewer(this, display);
543     o = info.get("graphicsAdapter");
544     if (o == null && !isWebGL)
545       o = Interface.getOption("g3d.Graphics3D", this, "setOptions");
546     gdata = (o == null && (isWebGL || !isJS) ? new GData() : (GData) o);
547     // intentionally throw an error here to restart the JavaScript async process
548     gdata.initialize(this, apiPlatform);
549 
550     stm = new StateManager(this);
551     cm = new ColorManager(this, gdata);
552     sm = new StatusManager(this);
553     boolean is4D = info.containsKey("4DMouse");
554     tm = TransformManager.getTransformManager(this, Integer.MAX_VALUE, 0, is4D);
555     slm = new SelectionManager(this);
556     if (haveDisplay) {
557       // must have language by now, as ActionManager uses GT._()
558       acm = (multiTouch
559           ? (ActionManager) Interface.getOption("multitouch.ActionManagerMT",
560               null, null)
561           : new ActionManager());
562       acm.setViewer(this,
563           commandOptions + "-multitouch-" + info.get("multiTouch"));
564       mouse = apiPlatform.getMouseManager(privateKey, display);
565       if (multiTouch && !checkOption2("-simulated", "-simulated"))
566         apiPlatform.setTransparentCursor(display);
567     }
568     mm = new ModelManager(this);
569     shm = new ShapeManager(this);
570     tempArray = new TempArray();
571     am = new AnimationManager(this);
572     o = info.get("repaintManager");
573     if (o == null)
574       o = Interface.getOption("render.RepaintManager", this, "setOptions");
575     if (isJS || o != null && !o.equals(""))
576       (rm = (JmolRepaintManager) o).set(this, shm);
577     // again we through a JS error if in async mode
578     ms = new ModelSet(this, null);
579     initialize(true, false);
580     fm = new FileManager(this);
581 
582     definedAtomSets = new Hashtable<String, Object>();
583     setJmolStatusListener(statusListener);
584     if (isApplet) {
585       Logger.info("vwrOptions: \n" + Escape.escapeMap(vwrOptions));
586       // Java only, because Signed applet can't find correct path when local.
587       String path = (String) vwrOptions.get("documentLocation");
588       if (!isJS && path != null && path.startsWith("file:/")) {
589         path = path.substring(0,
590             path.substring(0, (path + "?").indexOf("?")).lastIndexOf("/"));
591         Logger.info("setting current directory to " + path);
592         cd(path);
593       }
594       path = appletDocumentBase;
595       i = path.indexOf("#");
596       if (i >= 0)
597         path = path.substring(0, i);
598       i = path.lastIndexOf("?");
599       if (i >= 0)
600         path = path.substring(0, i);
601       i = path.lastIndexOf("/");
602       if (i >= 0)
603         path = path.substring(0, i);
604       jsDocumentBase = path;
605       fm.setAppletContext(appletDocumentBase);
606       String appletProxy = (String) info.get("appletProxy");
607       if (appletProxy != null)
608         setStringProperty("appletProxy", appletProxy);
609       if (isSignedApplet) {
610         logFilePath = PT.rep(appletCodeBase, "file://", "");
611         logFilePath = PT.rep(logFilePath, "file:/", "");
612         if (logFilePath.indexOf("//") >= 0)
613           logFilePath = null;
614         else
615           isSignedAppletLocal = true;
616       } else if (!isJS) {
617         logFilePath = null;
618       }
619       new GT(this, (String) info.get("language"));
620       // deferred here so that language is set
621       if (isJS)
622         acm.createActions();
623     } else {
624       // not an applet -- used to pass along command line options
625       gdata.setBackgroundTransparent(
626           checkOption2("backgroundTransparent", "-b"));
627       isSilent = checkOption2("silent", "-i");
628       if (isSilent)
629         Logger.setLogLevel(Logger.LEVEL_WARN); // no info, but warnings and
630       if (headless && !isSilent)
631         Logger.info("Operating headless display=" + display
632             + " nographicsallowed=" + noGraphicsAllowed);
633       // errors
634       isSyntaxAndFileCheck = checkOption2("checkLoad", "-C");
635       isSyntaxCheck = isSyntaxAndFileCheck || checkOption2("check", "-c");
636       listCommands = checkOption2("listCommands", "-l");
637       cd(".");
638       if (headless) {
639         headlessImageParams = (Map<String, Object>) info.get("headlessImage");
640         o = info.get("headlistMaxTimeMs");
641         if (o == null)
642           o = Integer.valueOf(60000);
643         setTimeout("" + Math.random(), ((Integer) o).intValue(), "exitJmol");
644       }
645     }
646     useCommandThread = !headless
647         && checkOption2("useCommandThread", "-threaded");
648     setStartupBooleans();
649     setIntProperty("_nProcessors", nProcessors);
650     /*
651      * Logger.info("jvm11orGreater=" + jvm11orGreater + "\njvm12orGreater=" +
652      * jvm12orGreater + "\njvm14orGreater=" + jvm14orGreater);
653      */
654     if (!isSilent) {
655       Logger.info(JC.copyright + "\nJmol Version: " + getJmolVersion()
656           + "\njava.vendor: " + strJavaVendor + "\njava.version: "
657           + strJavaVersion + "\nos.name: " + strOSName + "\nAccess: " + access
658           + "\nmemory: " + getP("_memory") + "\nprocessors available: "
659           + nProcessors + "\nuseCommandThread: " + useCommandThread
660           + (!isApplet ? ""
661               : "\nappletId:" + htmlName
662                   + (isSignedApplet ? " (signed)" : "")));
663     }
664     zap(false, true, false); // here to allow echos
665     g.setO("language", GT.getLanguage());
666     g.setO("_hoverLabel", hoverLabel);
667     stm.setJmolDefaults();
668     // this code will be shared between Jmol 14.0 and 14.1
669     Elements.covalentVersion = Elements.RAD_COV_BODR_2014_02_22;
670     allowArrayDotNotation = true;
671     if (allowScripting)
672       getScriptManager();
673   }
674 
675   // //////////// screen/image methods ///////////////
676 
677   // final Rectangle rectClip = new Rectangle();
678 
679   private int maximumSize = Integer.MAX_VALUE;
680 
setMaximumSize(int x)681   private void setMaximumSize(int x) {
682     maximumSize = Math.max(x, 100);
683   }
684 
685   /**
686    * A graphics from a "slave" stereo display that has been synchronized with
687    * this this applet.
688    */
689   private Object gRight;
690 
691   /**
692    * A flag to indicate that THIS is the right-side panel of a pair of synced
693    * applets running a left-right stereo display (that would be piped into a
694    * dual-image polarized projector system such as GeoWall).
695    *
696    */
697   private boolean isStereoSlave;
698 
setStereo(boolean isStereoSlave, Object gRight)699   public void setStereo(boolean isStereoSlave, Object gRight) {
700     this.isStereoSlave = isStereoSlave;
701     this.gRight = gRight;
702   }
703 
704   public float imageFontScaling = 1;
705 
getMenu(String type)706   public String getMenu(String type) {
707     getPopupMenu();
708     if (type.equals("\0")) {
709       popupMenu(screenWidth - 120, 0, 'j');
710       return "OK";
711     }
712     return (jmolpopup == null ? ""
713         : jmolpopup.jpiGetMenuAsString(
714             "Jmol version " + getJmolVersion() + "|_GET_MENU|" + type));
715   }
716 
717   @Override
resizeInnerPanel(int width, int height)718   public int[] resizeInnerPanel(int width, int height) {
719     if (!autoExit && haveDisplay)
720       return sm.resizeInnerPanel(width, height);
721     setScreenDimension(width, height);
722     return new int[] { screenWidth, screenHeight };
723   }
724 
725   @Override
setScreenDimension(int width, int height)726   public void setScreenDimension(int width, int height) {
727     // There is a bug in Netscape 4.7*+MacOS 9 when comparing dimension objects
728     // so don't try dim1.equals(dim2)
729     height = Math.min(height, maximumSize);
730     width = Math.min(width, maximumSize);
731     if (tm.stereoDoubleFull)
732       width = (width + 1) / 2;
733     if (screenWidth == width && screenHeight == height)
734       return;
735     resizeImage(width, height, false, false, true);
736   }
737 
resizeImage(int width, int height, boolean isImageWrite, boolean isExport, boolean isReset)738   void resizeImage(int width, int height, boolean isImageWrite,
739                    boolean isExport, boolean isReset) {
740     if (!isImageWrite && creatingImage)
741       return;
742     boolean wasAntialiased = antialiased;
743     antialiased = (isReset
744         ? g.antialiasDisplay && checkMotionRendering(T.antialiasdisplay)
745         : isImageWrite && !isExport ? g.antialiasImages : false);
746     if (!isExport && !isImageWrite
747         && (width > 0 || wasAntialiased != antialiased))
748       setShapeProperty(JC.SHAPE_LABELS, "clearBoxes", null);
749     imageFontScaling = (antialiased ? 2f : 1f)
750         * (isReset || tm.scale3D || width <= 0 ? 1
751             : (g.zoomLarge == (height > width) ? height : width) * 1f
752                 / getScreenDim());
753     if (width > 0) {
754       screenWidth = width;
755       screenHeight = height;
756       if (!isImageWrite) {
757         g.setI("_width", width);
758         g.setI("_height", height);
759         //        setStatusResized(width, height);
760       }
761     } else {
762       width = (screenWidth == 0 ? screenWidth = 500 : screenWidth);
763       height = (screenHeight == 0 ? screenHeight = 500 : screenHeight);
764     }
765     tm.setScreenParameters(width, height,
766         isImageWrite || isReset ? g.zoomLarge : false, antialiased, false,
767         false);
768     gdata.setWindowParameters(width, height, antialiased);
769     if (width > 0 && !isImageWrite)
770       setStatusResized(width, height);
771   }
772 
773   @Override
getScreenWidth()774   public int getScreenWidth() {
775     return screenWidth;
776   }
777 
778   @Override
getScreenHeight()779   public int getScreenHeight() {
780     return screenHeight;
781   }
782 
getScreenDim()783   public int getScreenDim() {
784     return (g.zoomLarge == (screenHeight > screenWidth) ? screenHeight
785         : screenWidth);
786   }
787 
setWidthHeightVar()788   public void setWidthHeightVar() {
789     g.setI("_width", screenWidth);
790     g.setI("_height", screenHeight);
791   }
792 
getBoundBoxCenterX()793   public int getBoundBoxCenterX() {
794     // used by axes renderer
795     return screenWidth / 2;
796   }
797 
getBoundBoxCenterY()798   public int getBoundBoxCenterY() {
799     return screenHeight / 2;
800   }
801 
updateWindow(int width, int height)802   private boolean updateWindow(int width, int height) {
803     if (!refreshing || creatingImage)
804       return (refreshing ? false : !isJS);
805     if (isTainted || tm.slabEnabled)
806       setModelVisibility();
807     isTainted = false;
808     if (rm != null) {
809       if (width != 0)
810         setScreenDimension(width, height);
811     }
812     return true;
813   }
814 
815   /**
816    *
817    * @param isDouble
818    * @param isImageWrite
819    * @return a java.awt.Image in the case of standard Jmol; an int[] in the case
820    *         of Jmol-Android a canvas in the case of JSmol
821    */
getImage(boolean isDouble, boolean isImageWrite)822   private Object getImage(boolean isDouble, boolean isImageWrite) {
823     Object image = null;
824     try {
825       beginRendering(isDouble, isImageWrite);
826       render();
827       gdata.endRendering();
828       image = gdata.getScreenImage(isImageWrite);
829     } catch (Error er) {
830       gdata.getScreenImage(isImageWrite);
831       handleError(er, false);
832       setErrorMessage("Error during rendering: " + er, null);
833     } catch (Exception e) {
834       System.out.println("render error" + e);
835     }
836     return image;
837   }
838 
beginRendering(boolean isDouble, boolean isImageWrite)839   private void beginRendering(boolean isDouble, boolean isImageWrite) {
840     gdata.beginRendering(tm.getStereoRotationMatrix(isDouble), g.translucent,
841         isImageWrite, !checkMotionRendering(T.translucent));
842   }
843 
844   public boolean antialiased;
845 
render()846   private void render() {
847     if (mm.modelSet == null || !mustRender || !refreshing && !creatingImage
848         || rm == null)
849       return;
850     boolean antialias2 = antialiased && g.antialiasTranslucent;
851     int[] navMinMax = shm.finalizeAtoms(tm.bsSelectedAtoms, true);
852     if (isWebGL) {
853       rm.renderExport(gdata, ms, jsParams);
854       notifyViewerRepaintDone();
855       return;
856     }
857     rm.render(gdata, ms, true, navMinMax);
858     if (gdata.setPass2(antialias2)) {
859       tm.setAntialias(antialias2);
860       rm.render(gdata, ms, false, null);
861       tm.setAntialias(antialiased);
862     }
863   }
864 
865   /**
866    *
867    * @param graphic
868    *        In JavaScript/HTML5, a Canvas.Context2d
869    * @param img
870    * @param x
871    * @param y
872    * @param isDTI
873    *        DTI format -- scrunch width by factor of two
874    */
drawImage(Object graphic, Object img, int x, int y, boolean isDTI)875   private void drawImage(Object graphic, Object img, int x, int y,
876                          boolean isDTI) {
877     if (graphic != null && img != null) {
878       apiPlatform.drawImage(graphic, img, x, y, screenWidth, screenHeight,
879           isDTI);
880     }
881     gdata.releaseScreenImage();
882   }
883 
getScreenImage()884   public Object getScreenImage() {
885     return getScreenImageBuffer(null, true);
886   }
887 
888   /**
889    * Image.getJpgImage, ImageCreator.clipImage, getImageBytes,
890    * Viewer.renderScreenImageStereo
891    */
892   @Override
getScreenImageBuffer(Object g, boolean isImageWrite)893   public Object getScreenImageBuffer(Object g, boolean isImageWrite) {
894     if (isWebGL)
895       return (isImageWrite
896           ? apiPlatform.allocateRgbImage(0, 0, null, 0, false, true)
897           : null);
898     boolean isDouble = tm.stereoDoubleFull || tm.stereoDoubleDTI;
899     boolean isBicolor = tm.stereoMode.isBiColor();
900     boolean mergeImages = (g == null && isDouble);
901     Object imageBuffer;
902     if (isBicolor) {
903       beginRendering(true, isImageWrite);
904       render();
905       gdata.endRendering();
906       gdata.snapshotAnaglyphChannelBytes();
907       beginRendering(false, isImageWrite);
908       render();
909       gdata.endRendering();
910       gdata.applyAnaglygh(tm.stereoMode, tm.stereoColors);
911       imageBuffer = gdata.getScreenImage(isImageWrite);
912     } else {
913       imageBuffer = getImage(isDouble, isImageWrite);
914     }
915     Object imageBuffer2 = null;
916     if (mergeImages) {
917       imageBuffer2 = apiPlatform.newBufferedImage(imageBuffer,
918           (tm.stereoDoubleDTI ? screenWidth : screenWidth << 1), screenHeight);
919       g = apiPlatform.getGraphics(imageBuffer2);
920     }
921     if (g != null) {
922       if (isDouble) {
923         if (tm.stereoMode == STER.DTI) {
924           drawImage(g, imageBuffer, screenWidth >> 1, 0, true);
925           imageBuffer = getImage(false, false);
926           drawImage(g, imageBuffer, 0, 0, true);
927           g = null;
928         } else {
929           drawImage(g, imageBuffer, screenWidth, 0, false);
930           imageBuffer = getImage(false, false);
931         }
932       }
933       if (g != null)
934         drawImage(g, imageBuffer, 0, 0, false);
935     }
936     return (mergeImages ? imageBuffer2 : imageBuffer);
937   }
938 
evalStringWaitStatusQueued(String returnType, String strScript, String statusList, boolean isQuiet, boolean isQueued)939   public synchronized Object evalStringWaitStatusQueued(String returnType,
940                                                         String strScript,
941                                                         String statusList,
942                                                         boolean isQuiet,
943                                                         boolean isQueued) {
944     /**
945      * @j2sNative
946      *
947      *            if (strScript.indexOf("JSCONSOLE") == 0) {
948      *            this.html5Applet._showInfo(strScript.indexOf("CLOSE")<0); if
949      *            (strScript.indexOf("CLEAR") >= 0)
950      *            this.html5Applet._clearConsole(); return null; }
951      */
952     {
953     }
954     return (getScriptManager() == null ? null
955         : scm.evalStringWaitStatusQueued(returnType, strScript, statusList,
956             isQuiet, isQueued));
957   }
958 
popupMenu(int x, int y, char type)959   void popupMenu(int x, int y, char type) {
960     if (!haveDisplay || !refreshing || isPreviewOnly || g.disablePopupMenu)
961       return;
962     switch (type) {
963     case 'j':
964       try {
965         getPopupMenu();
966         // can throw error if not present; that's ok
967         jmolpopup.jpiShow(x, y);
968       } catch (Throwable e) {
969         // no Swing -- tough luck!
970         Logger.info(e.toString());
971         g.disablePopupMenu = true;
972       }
973       break;
974     case 'a':
975     case 'b':
976     case 'm':
977       // atom, bond, or main -- ignored
978       if (getModelkit(true) == null) { // bh was false?
979         return;
980       }
981       modelkit.jpiShow(x, y);
982       break;
983     }
984   }
985 
getModelkit(boolean andShow)986   public ModelKitPopup getModelkit(boolean andShow) {
987     if (modelkit == null) {
988       modelkit = (ModelKitPopup) apiPlatform.getMenuPopup(null, 'm');
989     } else if (andShow) {
990       modelkit.jpiUpdateComputedMenus();
991     }
992     return modelkit;
993   }
994 
getPopupMenu()995   Object getPopupMenu() {
996     if (g.disablePopupMenu)
997       return null;
998     if (jmolpopup == null) {
999       jmolpopup = (allowScripting ? apiPlatform.getMenuPopup(menuStructure, 'j')
1000           : null);
1001       if (jmolpopup == null) {
1002         if (!async)
1003           g.disablePopupMenu = true;
1004         return null;
1005       }
1006     }
1007 
1008     if (isJSNoAWT)
1009       checkMenuUpdate();
1010     return jmolpopup.jpiGetMenuAsObject();
1011   }
1012 
1013   @Override
setMenu(String fileOrText, boolean isFile)1014   public void setMenu(String fileOrText, boolean isFile) {
1015     if (isFile)
1016       Logger
1017           .info("Setting menu " + (fileOrText.length() == 0 ? "to Jmol defaults"
1018               : "from file " + fileOrText));
1019     if (fileOrText.length() == 0)
1020       fileOrText = null;
1021     else if (isFile)
1022       fileOrText = getFileAsString3(fileOrText, false, null);
1023     getProperty("DATA_API", "setMenu", fileOrText);
1024     sm.setCallbackFunction("menu", fileOrText);
1025   }
1026 
1027   // // JavaScript callback methods for the applet
1028 
1029   /*
1030   *
1031   * animFrameCallback echoCallback (defaults to messageCallback) errorCallback
1032   * evalCallback hoverCallback loadStructCallback measureCallback (defaults to
1033   * messageCallback) messageCallback (no local version) minimizationCallback
1034   * pickCallback resizeCallback scriptCallback (defaults to messageCallback)
1035   * syncCallback
1036   */
1037 
1038   /*
1039   * aniframeCallback is called:
1040   *
1041   * -- each time a frame is changed -- whenever the animation state is changed
1042   * -- whenever the visible frame range is changed
1043   *
1044   * jmolSetCallback("animFrameCallback", "myAnimFrameCallback") function
1045   * myAnimFrameCallback(frameNo, fileNo, modelNo, firstNo, lastNo) {}
1046   *
1047   * frameNo == the current frame in fileNo == the current file number, starting
1048   * at 1 modelNo == the current model number in the current file, starting at 1
1049   * firstNo == flag1 * (the first frame of the set, in file * 1000000 + model
1050   * notation) lastNo == flag2 * (the last frame of the set, in file * 1000000 +
1051   * model notation)
1052   *
1053   * where flag1 = 1 if animationDirection > 1 or -1 otherwise where flag2 = 1
1054   * if currentDirection > 1 or -1 otherwise
1055   *
1056   * RepaintManager.setStatusFrameChanged RepaintManager.setAnimationOff
1057   * RepaintManager.setCurrentModelIndex RepaintManager.clearAnimation
1058   * RepaintManager.rewindAnimation RepaintManager.setAnimationLast
1059   * RepaintManager.setAnimationRelative RepaintManager.setFrameRangeVisible
1060   * Viewer.setCurrentModelIndex Eval.file Eval.frame Eval.load
1061   * Viewer.createImage (when creating movie frames with the WRITE FRAMES
1062   * command) Viewer.initializeModel
1063   */
1064 
1065   private int prevFrame = Integer.MIN_VALUE;
1066   private float prevMorphModel;
1067 
1068   /**
1069    * @param isVib
1070    * @param doNotify
1071    *        ignored; not implemented
1072    */
setStatusFrameChanged(boolean isVib, boolean doNotify)1073   void setStatusFrameChanged(boolean isVib, boolean doNotify) {
1074     if (isVib) {
1075       // force reset (reading vibrations)
1076       prevFrame = Integer.MIN_VALUE;
1077     }
1078     tm.setVibrationPeriod(Float.NaN);
1079     int firstIndex = am.firstFrameIndex;
1080     int lastIndex = am.lastFrameIndex;
1081 
1082     boolean isMovie = am.isMovie;
1083     int modelIndex = am.cmi;
1084     if (firstIndex == lastIndex && !isMovie)
1085       modelIndex = firstIndex;
1086     int frameID = getModelFileNumber(modelIndex);
1087     int currentFrame = am.cmi;
1088     int fileNo = frameID;
1089     int modelNo = frameID % 1000000;
1090     int firstNo = (isMovie ? firstIndex : getModelFileNumber(firstIndex));
1091     int lastNo = (isMovie ? lastIndex : getModelFileNumber(lastIndex));
1092 
1093     String strModelNo;
1094     if (isMovie) {
1095       strModelNo = "" + (currentFrame + 1);
1096     } else if (fileNo == 0) {
1097       strModelNo = getModelNumberDotted(firstIndex);
1098       if (firstIndex != lastIndex)
1099         strModelNo += " - " + getModelNumberDotted(lastIndex);
1100       if (firstNo / 1000000 == lastNo / 1000000)
1101         fileNo = firstNo;
1102     } else {
1103       strModelNo = getModelNumberDotted(modelIndex);
1104     }
1105     if (fileNo != 0)
1106       fileNo = (fileNo < 1000000 ? 1 : fileNo / 1000000);
1107 
1108     if (!isMovie) {
1109       g.setI("_currentFileNumber", fileNo);
1110       g.setI("_currentModelNumberInFile", modelNo);
1111     }
1112     float currentMorphModel = am.currentMorphModel;
1113     g.setI("_currentFrame", currentFrame);
1114     g.setI("_morphCount", am.morphCount);
1115     g.setF("_currentMorphFrame", currentMorphModel);
1116     g.setI("_frameID", frameID);
1117     g.setI("_modelIndex", modelIndex);
1118     g.setO("_modelNumber", strModelNo);
1119     g.setO("_modelName", (modelIndex < 0 ? "" : getModelName(modelIndex)));
1120     String title = (modelIndex < 0 ? "" : ms.getModelTitle(modelIndex));
1121     g.setO("_modelTitle", title == null ? "" : title);
1122     g.setO("_modelFile",
1123         (modelIndex < 0 ? "" : ms.getModelFileName(modelIndex)));
1124     g.setO("_modelType",
1125         (modelIndex < 0 ? "" : ms.getModelFileType(modelIndex)));
1126 
1127     if (currentFrame == prevFrame && currentMorphModel == prevMorphModel)
1128       return;
1129     prevFrame = currentFrame;
1130     prevMorphModel = currentMorphModel;
1131 
1132     String entryName = getModelName(currentFrame);
1133     if (isMovie) {
1134       entryName = "" + (entryName == "" ? currentFrame + 1 : am.caf + 1) + ": "
1135           + entryName;
1136     } else {
1137       String script = "" + getModelNumberDotted(currentFrame);
1138       if (!entryName.equals(script))
1139         entryName = script + ": " + entryName;
1140     }
1141     // there was a point where I thought frameNo and currentFrame
1142     // might be different.
1143     sm.setStatusFrameChanged(fileNo, modelNo,
1144         (am.animationDirection < 0 ? -firstNo : firstNo),
1145         (am.currentDirection < 0 ? -lastNo : lastNo), currentFrame,
1146         currentMorphModel, entryName);
1147     if (doHaveJDX())
1148       getJSV().setModel(modelIndex);
1149     if (isJS)
1150       updateJSView(modelIndex, -1);
1151   }
1152 
1153   // interaction with JSpecView
1154 
1155   private boolean haveJDX;
1156   private JmolJSpecView jsv;
1157 
doHaveJDX()1158   private boolean doHaveJDX() {
1159     // once-on, never off
1160     return (haveJDX
1161         || (haveJDX = getBooleanProperty("_JSpecView".toLowerCase())));
1162   }
1163 
getJSV()1164   JmolJSpecView getJSV() {
1165     if (jsv == null) {
1166       jsv = (JmolJSpecView) Interface.getOption("jsv.JSpecView", this,
1167           "script");
1168       jsv.setViewer(this);
1169     }
1170     return jsv;
1171   }
1172 
1173   /**
1174    * get the model designated as "baseModel" in a JCamp-MOL file for example,
1175    * the model used for bonding for an XYZVIB file or the model used as the base
1176    * model for a mass spec file. This might then allow pointing off a peak in
1177    * JSpecView to switch to the model that is involved in HNMR or CNMR
1178    *
1179    * @param modelIndex
1180    *
1181    * @return modelIndex
1182    */
1183 
getJDXBaseModelIndex(int modelIndex)1184   public int getJDXBaseModelIndex(int modelIndex) {
1185     if (!doHaveJDX())
1186       return modelIndex;
1187     return getJSV().getBaseModelIndex(modelIndex);
1188   }
1189 
getJspecViewProperties(Object myParam)1190   public Object getJspecViewProperties(Object myParam) {
1191     // from getProperty("JSpecView...")
1192     Object o = sm.getJspecViewProperties("" + myParam);
1193     if (o != null)
1194       haveJDX = true;
1195     return o;
1196   }
1197 
1198   /*
1199   * echoCallback is one of the two main status reporting mechanisms. Along with
1200   * scriptCallback, it outputs to the console. Unlike scriptCallback, it does
1201   * not output to the status bar of the application or applet. If
1202   * messageCallback is enabled but not echoCallback, these messages go to the
1203   * messageCallback function instead.
1204   *
1205   * jmolSetCallback("echoCallback", "myEchoCallback") function
1206   * myEchoCallback(app, message, queueState) {}
1207   *
1208   * queueState = 1 -- queued queueState = 0 -- not queued
1209   *
1210   * serves:
1211   *
1212   * Eval.instructionDispatchLoop when app has -l flag
1213   * ForceField.steepestDescenTakeNSteps for minimization done
1214   * Viewer.setPropertyError Viewer.setBooleanProperty error
1215   * Viewer.setFloatProperty error Viewer.setIntProperty error
1216   * Viewer.setStringProperty error Viewer.showString adds a Logger.warn()
1217   * message Eval.showString calculate, cd, dataFrame, echo, error, getProperty,
1218   * history, isosurface, listIsosurface, pointGroup, print, set, show, write
1219   * ForceField.steepestDescentInitialize for initial energy
1220   * ForceField.steepestDescentTakeNSteps for minimization update
1221   * Viewer.showParameter
1222   */
1223 
scriptEcho(String strEcho)1224   public void scriptEcho(String strEcho) {
1225     if (!Logger.isActiveLevel(Logger.LEVEL_INFO))
1226       return;
1227     if (isJS)
1228       System.out.println(strEcho);
1229     sm.setScriptEcho(strEcho, isScriptQueued());
1230     if (listCommands && strEcho != null && strEcho.indexOf("$[") == 0)
1231       Logger.info(strEcho);
1232   }
1233 
isScriptQueued()1234   private boolean isScriptQueued() {
1235     return scm != null && scm.isScriptQueued();
1236   }
1237 
1238   /*
1239   * errorCallback is a special callback that can be used to identify errors
1240   * during scripting and file i/o, and also indicate out of memory conditions
1241   *
1242   * jmolSetCallback("errorCallback", "myErrorCallback") function
1243   * myErrorCallback(app, errType, errMsg, objectInfo, errMsgUntranslated) {}
1244   *
1245   * errType == "Error" or "ScriptException" errMsg == error message, possibly
1246   * translated, with added information objectInfo == which object (such as an
1247   * isosurface) was involved errMsgUntranslated == just the basic message
1248   *
1249   * Viewer.notifyError Eval.runEval on Error and file loading Exceptions
1250   * Viewer.handleError Eval.runEval on OOM Error Viewer.createModelSet on OOM
1251   * model initialization Error Viewer.getImage on OOM rendering Error
1252   */
notifyError(String errType, String errMsg, String errMsgUntranslated)1253   public void notifyError(String errType, String errMsg,
1254                           String errMsgUntranslated) {
1255     g.setO("_errormessage", errMsgUntranslated);
1256     sm.notifyError(errType, errMsg, errMsgUntranslated);
1257   }
1258 
1259   /*
1260   * evalCallback is a special callback that evaluates expressions in JavaScript
1261   * rather than in Jmol.
1262   *
1263   * Viewer.jsEval Eval.loadScriptFileInternal Eval.Rpn.evaluateScript
1264   * Eval.script
1265   */
1266 
jsEval(String strEval)1267   public String jsEval(String strEval) {
1268     return "" + sm.jsEval(strEval);
1269   }
1270 
jsEvalSV(String strEval)1271   public SV jsEvalSV(String strEval) {
1272     return SV.getVariable(isJS ? sm.jsEval(strEval) : jsEval(strEval));
1273   }
1274 
1275   /*
1276   * loadStructCallback indicates file load status.
1277   *
1278   * jmolSetCallback("loadStructCallback", "myLoadStructCallback") function
1279   * myLoadStructCallback(fullPathName, fileName, modelName, errorMsg, ptLoad)
1280   * {}
1281   *
1282   * ptLoad == JmolConstants.FILE_STATUS_NOT_LOADED == -1 ptLoad == JmolConstants.FILE_STATUS_ZAPPED == 0
1283   * ptLoad == JmolConstants.FILE_STATUS_CREATING_MODELSET == 2 ptLoad ==
1284   * JmolConstants.FILE_STATUS_MODELSET_CREATED == 3 ptLoad == JmolConstants.FILE_STATUS_MODELS_DELETED == 5
1285   *
1286   * Only -1 (error loading), 0 (zapped), and 3 (model set created) messages are
1287   * passed on to the callback function. The others can be detected using
1288   *
1289   * set loadStructCallback "jmolscript:someFunctionName"
1290   *
1291   * At the time of calling of that method, the jmolVariable _loadPoint gives
1292   * the value of ptLoad. These load points are also recorded in the status
1293   * queue under types "fileLoaded" and "fileLoadError".
1294   *
1295   * Viewer.setFileLoadStatus Viewer.createModelSet (2, 3)
1296   * Viewer.createModelSetAndReturnError (-1, 1, 4) Viewer.deleteAtoms (5)
1297   * Viewer.zap (0)
1298   */
setFileLoadStatus(FIL ptLoad, String fullPathName, String fileName, String modelName, String strError, Boolean isAsync)1299   private void setFileLoadStatus(FIL ptLoad, String fullPathName,
1300                                  String fileName, String modelName,
1301                                  String strError, Boolean isAsync) {
1302     setErrorMessage(strError, null);
1303     g.setI("_loadPoint", ptLoad.getCode());
1304     boolean doCallback = (ptLoad != FIL.CREATING_MODELSET);
1305     if (doCallback)
1306       setStatusFrameChanged(false, false);
1307     sm.setFileLoadStatus(fullPathName, fileName, modelName, strError,
1308         ptLoad.getCode(), doCallback, isAsync);
1309     if (doCallback) {
1310       //       setStatusFrameChanged(false, true); // ensures proper title in JmolFrame but then we miss the file name
1311       if (doHaveJDX())
1312         getJSV().setModel(am.cmi);
1313       if (isJS)
1314         updateJSView(am.cmi, -2);
1315     }
1316 
1317   }
1318 
getZapName()1319   public String getZapName() {
1320     return (g.modelKitMode ? JC.MODELKIT_ZAP_TITLE : JC.ZAP_TITLE);
1321   }
1322 
1323   /*
1324   * measureCallback reports completed or pending measurements. Pending
1325   * measurements are measurements that the user has started but has not
1326   * completed -- this call comes when the user hesitates with the mouse over an
1327   * atom and the "rubber band" is showing
1328   *
1329   * jmolSetCallback("measureCallback", "myMeasureCallback") function
1330   * myMeasureCallback(strMeasure, intInfo, status) {}
1331   *
1332   * intInfo == (see below) status == "measurePicked" (intInfo == the number of
1333   * atoms in the measurement) "measureComplete" (intInfo == the current number
1334   * measurements) "measureDeleted" (intInfo == the index of the measurement
1335   * deleted or -1 for all) "measurePending" (intInfo == number of atoms picked
1336   * so far)
1337   *
1338   * strMeasure:
1339   *
1340   * For "set picking MEASURE ..." each time the user clicks an atom, a message
1341   * is sent to the pickCallback function (see below), and if the picking is set
1342   * to measure distance, angle, or torsion, then after the requisite number of
1343   * atoms is picked and the pick callback message is sent, a call is also made
1344   * to measureCallback with a string that indicates the measurement, such as:
1345   *
1346   * Angle O #9 - Si #7 - O #2 : 110.51877
1347   *
1348   * Under default conditions, when picking is not set to MEASURE, then
1349   * measurement reports are sent when the measure is completed, deleted, or
1350   * pending. These reports are in a psuedo array form that can be parsed more
1351   * easily, involving the atoms and measurement with units, for example:
1352   *
1353   * [Si #3, O #8, Si #7, 60.1 <degrees mark>]
1354   *
1355   * Viewer.setStatusMeasuring Measures.clear Measures.define
1356   * Measures.deleteMeasurement Measures.pending actionManager.atomPicked
1357   */
1358 
setStatusMeasuring(String status, int intInfo, String strMeasure, float value)1359   public void setStatusMeasuring(String status, int intInfo, String strMeasure,
1360                                  float value) {
1361 
1362     // status           intInfo
1363 
1364     // measureCompleted index
1365     // measurePicked    atom count
1366     // measurePending   atom count
1367     // measureDeleted   -1 (all) or index
1368     // measureSequence  -2
1369     sm.setStatusMeasuring(status, intInfo, strMeasure, value);
1370   }
1371 
1372   /*
1373   * minimizationCallback reports the status of a currently running
1374   * minimization.
1375   *
1376   * jmolSetCallback("minimizationCallback", "myMinimizationCallback") function
1377   * myMinimizationCallback(app, minStatus, minSteps, minEnergy, minEnergyDiff)
1378   * {}
1379   *
1380   * minStatus is one of "starting", "calculate", "running", "failed", or "done"
1381   *
1382   * Viewer.notifyMinimizationStatus Minimizer.endMinimization
1383   * Minimizer.getEnergyonly Minimizer.startMinimization
1384   * Minimizer.stepMinimization
1385   */
1386 
notifyMinimizationStatus()1387   public void notifyMinimizationStatus() {
1388     Object step = getP("_minimizationStep");
1389     String ff = (String) getP("_minimizationForceField");
1390     sm.notifyMinimizationStatus((String) getP("_minimizationStatus"),
1391         step instanceof String ? Integer.valueOf(0) : (Integer) step,
1392         (Float) getP("_minimizationEnergy"),
1393         (step.toString().equals("0") ? Float.valueOf(0)
1394             : (Float) getP("_minimizationEnergyDiff")),
1395         ff);
1396   }
1397 
1398   /*
1399   * pickCallback returns information about an atom, bond, or DRAW object that
1400   * has been picked by the user.
1401   *
1402   * jmolSetCallback("pickCallback", "myPickCallback") function
1403   * myPickCallback(strInfo, iAtom, map) {}
1404   *
1405   * iAtom == the index of the atom picked or -2 for a draw object or -3 for a
1406   * bond
1407   *
1408   * strInfo depends upon the type of object picked:
1409   *
1410   * atom (iAtom>=0): a string determinied by the PICKLABEL parameter, which if "" delivers
1411   * the atom identity along with its coordinates
1412   *
1413   * bond (iAtom==-3): ["bond", bondIdentityString (quoted), x, y, z] where the coordinates
1414   * are of the midpoint of the bond
1415   *
1416   * draw (iAtom==-2): ["draw", ID(quoted), pickedModel, pickedVertex, x, y, z,
1417   * title(quoted)]
1418   *
1419   * isosurface (iAtom==-4): ["isosurface", ID(quoted), pickedModel, pickedVertex, x, y, z,
1420   * title(quoted)]
1421   *
1422   * map:
1423   *
1424   * atom: null
1425   *
1426   * bond: {pt, index, modelIndex, modelNumberDotted, type, strInfo}
1427   *
1428   * Draw, isosurface: {pt, modelIndex, modelNumberDotted, id, vertex, type}
1429   *
1430   * Viewer.setStatusAtomPicked Draw.checkObjectClicked (set picking DRAW)
1431   * Sticks.checkObjectClicked (set bondPicking TRUE; set picking IDENTIFY)
1432   * actionManager.atomPicked (set atomPicking TRUE; set picking IDENTIFY)
1433   * actionManager.queueAtom (during measurements)
1434   */
1435 
setStatusAtomPicked(int atomIndex, String info, Map<String, Object> map, boolean andSelect)1436   public void setStatusAtomPicked(int atomIndex, String info,
1437                                   Map<String, Object> map, boolean andSelect) {
1438     if (andSelect)
1439       setSelectionSet(BSUtil.newAndSetBit(atomIndex));
1440     if (info == null) {
1441       info = g.pickLabel;
1442       info = (info.length() == 0
1443           ? getAtomInfoXYZ(atomIndex, g.messageStyleChime)
1444           : ms.getAtomInfo(atomIndex, info, ptTemp));
1445     }
1446     setPicked(atomIndex, false);
1447     if (atomIndex < 0) {
1448       Measurement m = getPendingMeasurement();
1449       if (m != null)
1450         info = info.substring(0, info.length() - 1) + ",\"" + m.getString()
1451             + "\"]";
1452     }
1453     g.setO("_pickinfo", info);
1454     sm.setStatusAtomPicked(atomIndex, info, map);
1455     if (atomIndex < 0)
1456       return;
1457     int syncMode = sm.getSyncMode();
1458     if (syncMode == StatusManager.SYNC_DRIVER && doHaveJDX())
1459       getJSV().atomPicked(atomIndex);
1460     if (isJS)
1461       updateJSView(ms.at[atomIndex].mi, atomIndex);
1462   }
1463 
1464   @Override
getProperty(String returnType, String infoType, Object paramInfo)1465   public Object getProperty(String returnType, String infoType,
1466                             Object paramInfo) {
1467     // accepts a BitSet paramInfo
1468     // return types include "JSON", "String", "readable", and anything else
1469     // returns the Java object.
1470     // Jmol 11.7.45 also uses this method as a general API
1471     // for getting and returning script data from the console and editor
1472 
1473     if (!"DATA_API".equals(returnType))
1474       return getPropertyManager().getProperty(returnType, infoType, paramInfo);
1475 
1476     switch (("scriptCheck........." // 0
1477         + "consoleText........." // 20
1478         + "scriptEditor........" // 40
1479         + "scriptEditorState..." // 60
1480         + "getAppConsole......." // 80
1481         + "getScriptEditor....." // 100
1482         + "setMenu............." // 120
1483         + "spaceGroupInfo......" // 140
1484         + "disablePopupMenu...." // 160
1485         + "defaultDirectory...." // 180
1486         + "getPopupMenu........" // 200
1487         + "shapeManager........" // 220
1488         + "getPreference......." // 240
1489     ).indexOf(infoType)) {
1490 
1491     case 0:
1492       return scriptCheckRet((String) paramInfo, true);
1493     case 20:
1494       return (appConsole == null ? "" : appConsole.getText());
1495     case 40:
1496       showEditor((String[]) paramInfo);
1497       return null;
1498     case 60:
1499       scriptEditorVisible = ((Boolean) paramInfo).booleanValue();
1500       return null;
1501     case 80:
1502       if (isKiosk) {
1503         appConsole = null;
1504       } else if (paramInfo instanceof JmolAppConsoleInterface) {
1505         appConsole = (JmolAppConsoleInterface) paramInfo;
1506       } else if (paramInfo != null && !((Boolean) paramInfo).booleanValue()) {
1507         appConsole = null;
1508       } else if (appConsole == null && paramInfo != null
1509           && ((Boolean) paramInfo).booleanValue()) {
1510         if (isJS) {
1511           appConsole = (JmolAppConsoleInterface) Interface
1512               .getOption("consolejs.AppletConsole", this, "script");
1513         }
1514         /**
1515          * @j2sNative
1516          *
1517          *
1518          */
1519         {
1520           for (int i = 0; i < 4 && appConsole == null; i++) {
1521             appConsole = (isApplet
1522                 ? (JmolAppConsoleInterface) Interface
1523                     .getOption("console.AppletConsole", null, null)
1524                 : (JmolAppConsoleInterface) Interface.getInterface(
1525                     "org.openscience.jmol.app.jmolpanel.console.AppConsole",
1526                     null, null));
1527             if (appConsole == null)
1528               try {
1529                 System.out.println("Viewer can't start appConsole");
1530                 Thread.currentThread().wait(100);
1531               } catch (InterruptedException e) {
1532                 //
1533               }
1534           }
1535         }
1536         if (appConsole != null)
1537           appConsole.start(this);
1538       }
1539       scriptEditor = (isJS || appConsole == null ? null
1540           : appConsole.getScriptEditor());
1541       return appConsole;
1542     case 100:
1543       if (appConsole == null && paramInfo != null
1544           && ((Boolean) paramInfo).booleanValue()) {
1545         getProperty("DATA_API", "getAppConsole", Boolean.TRUE);
1546         scriptEditor = (appConsole == null ? null
1547             : appConsole.getScriptEditor());
1548       }
1549       return scriptEditor;
1550     case 120:
1551       if (jmolpopup != null)
1552         jmolpopup.jpiDispose();
1553       jmolpopup = null;
1554       return menuStructure = (String) paramInfo;
1555     case 140:
1556       return getSymTemp().getSpaceGroupInfo(ms, null, -1, false, null);
1557     case 160:
1558       g.disablePopupMenu = true; // no false here, because it's a
1559       // one-time setting
1560       return null;
1561     case 180:
1562       return g.defaultDirectory;
1563     case 200:
1564       if (paramInfo instanceof String)
1565         return getMenu((String) paramInfo);
1566       return getPopupMenu();
1567     case 220:
1568       return shm.getProperty(paramInfo);
1569     case 240:
1570       return sm.syncSend("getPreference", paramInfo, 1);
1571     }
1572     Logger.error("ERROR in getProperty DATA_API: " + infoType);
1573     return null;
1574   }
1575 
notifyMouseClicked(int x, int y, int action, int mode)1576   public int notifyMouseClicked(int x, int y, int action, int mode) {
1577     // change y to 0 at bottom
1578     int modifiers = Binding.getButtonMods(action);
1579     int clickCount = Binding.getClickCount(action);
1580     g.setI("_mouseX", x);
1581     g.setI("_mouseY", screenHeight - y);
1582     g.setI("_mouseAction", action);
1583     g.setI("_mouseModifiers", modifiers);
1584     g.setI("_clickCount", clickCount);
1585     return sm.setStatusClicked(x, screenHeight - y, action, clickCount, mode);
1586   }
1587 
1588   private OutputManager outputManager;
1589 
getOutputManager()1590   private OutputManager getOutputManager() {
1591     if (outputManager != null)
1592       return outputManager;
1593     return (outputManager = (OutputManager) Interface.getInterface(
1594         "org.jmol.viewer.OutputManager" + (isJS ? "JS" : "Awt"), this, "file"))
1595             .setViewer(this, privateKey);
1596   }
1597 
1598   private GenericZipTools jzt;
1599 
getJzt()1600   public GenericZipTools getJzt() {
1601     return (jzt == null
1602         ? jzt = (GenericZipTools) Interface.getInterface("javajs.util.ZipTools",
1603             this, "zip")
1604         : jzt);
1605   }
1606 
readFileAsMap(BufferedInputStream bis, Map<String, Object> map, String name)1607   public void readFileAsMap(BufferedInputStream bis, Map<String, Object> map,
1608                             String name) {
1609     getJzt().readFileAsMap(bis, map, name);
1610   }
1611 
getZipDirectoryAsString(String fileName)1612   public String getZipDirectoryAsString(String fileName) {
1613     Object t = fm.getBufferedInputStreamOrErrorMessageFromName(fileName,
1614         fileName, false, false, null, false, true);
1615     return getJzt().getZipDirectoryAsStringAndClose((BufferedInputStream) t);
1616   }
1617 
1618   /**
1619    * @return byte[] image, or null and an error message
1620    */
1621   @Override
getImageAsBytes(String type, int width, int height, int quality, String[] errMsg)1622   public byte[] getImageAsBytes(String type, int width, int height, int quality,
1623                                 String[] errMsg) {
1624     return getOutputManager().getImageAsBytes(type, width, height, quality,
1625         errMsg);
1626   }
1627 
1628   @Override
releaseScreenImage()1629   public void releaseScreenImage() {
1630     gdata.releaseScreenImage();
1631   }
1632 
setDisplay(Object canvas)1633   public void setDisplay(Object canvas) {
1634     // used by JSmol/HTML5 when a canvas is resized
1635     display = canvas;
1636     apiPlatform.setViewer(this, canvas);
1637   }
1638 
newMeasurementData(String id, Lst<Object> points)1639   public MeasurementData newMeasurementData(String id, Lst<Object> points) {
1640     return ((MeasurementData) Interface
1641         .getInterface("org.jmol.modelset.MeasurementData", this, "script"))
1642             .init(id, this, points);
1643   }
1644 
getDataManager()1645   private JmolDataManager getDataManager() {
1646     return (dm == null
1647         ? (dm = ((JmolDataManager) Interface
1648             .getInterface("org.jmol.viewer.DataManager", this, "script"))
1649                 .set(this))
1650         : dm);
1651   }
1652 
getScriptManager()1653   private JmolScriptManager getScriptManager() {
1654     if (allowScripting && scm == null) {
1655       scm = (JmolScriptManager) Interface
1656           .getInterface("org.jmol.script.ScriptManager", this, "setOptions");
1657       if (isJS && scm == null)
1658         throw new NullPointerException();
1659       if (scm == null) {
1660         allowScripting = false;
1661         return null;
1662       }
1663       eval = scm.setViewer(this);
1664       if (useCommandThread)
1665         scm.startCommandWatcher(true);
1666     }
1667     return scm;
1668   }
1669 
checkOption2(String key1, String key2)1670   private boolean checkOption2(String key1, String key2) {
1671     return (vwrOptions.containsKey(key1)
1672         && !vwrOptions.get(key1).toString().equals("false")
1673         || commandOptions.indexOf(key2) >= 0);
1674   }
1675 
1676   public boolean isPreviewOnly;
1677 
1678   /**
1679    * determined by GraphicsEnvironment.isHeadless() from java
1680    * -Djava.awt.headless=true
1681    *
1682    * disables command threading
1683    *
1684    * disables DELAY, TIMEOUT, PAUSE, LOOP, GOTO, SPIN <rate>, ANIMATION ON
1685    *
1686    * turns SPIN <rate> <end> into just ROTATE <end>
1687    */
1688 
1689   public boolean headless;
1690 
setStartupBooleans()1691   private void setStartupBooleans() {
1692     setBooleanProperty("_applet", isApplet);
1693     setBooleanProperty("_JSpecView".toLowerCase(), false);
1694     setBooleanProperty("_signedApplet", isSignedApplet);
1695     setBooleanProperty("_headless", headless);
1696     setStringProperty("_restrict", "\"" + access + "\"");
1697     setBooleanProperty("_useCommandThread", useCommandThread);
1698   }
1699 
getExportDriverList()1700   public String getExportDriverList() {
1701     return (haveAccess(ACCESS.ALL)
1702         ? (String) g.getParameter("exportDrivers", true)
1703         : "");
1704   }
1705 
1706   /**
1707    * end of life for this viewer
1708    */
1709   @Override
dispose()1710   public void dispose() {
1711     gRight = null;
1712     if (mouse != null) {
1713       acm.dispose();
1714       mouse.dispose();
1715       mouse = null;
1716     }
1717     clearScriptQueue();
1718     clearThreads();
1719     haltScriptExecution();
1720     if (scm != null)
1721       scm.clear(true);
1722     gdata.destroy();
1723     if (jmolpopup != null)
1724       jmolpopup.jpiDispose();
1725     if (modelkit != null)
1726       modelkit.jpiDispose();
1727     try {
1728       if (appConsole != null) {
1729         appConsole.dispose();
1730         appConsole = null;
1731       }
1732       if (scriptEditor != null) {
1733         scriptEditor.dispose();
1734         scriptEditor = null;
1735       }
1736     } catch (Exception e) {
1737       // ignore -- Disposal was interrupted only in Eclipse
1738     }
1739   }
1740 
reset(boolean includingSpin)1741   public void reset(boolean includingSpin) {
1742     // Eval.reset()
1743     // initializeModel
1744     ms.calcBoundBoxDimensions(null, 1);
1745     axesAreTainted = true;
1746     tm.homePosition(includingSpin);
1747     if (ms.setCrystallographicDefaults())
1748       stm.setCrystallographicDefaults();
1749     else
1750       setAxesMode(T.axeswindow);
1751     prevFrame = Integer.MIN_VALUE;
1752     if (!tm.spinOn)
1753       setSync();
1754   }
1755 
1756   @Override
homePosition()1757   public void homePosition() {
1758     evalString("reset spin");
1759   }
1760 
1761   /*
1762    * final Hashtable imageCache = new Hashtable();
1763    *
1764    * void flushCachedImages() { imageCache.clear();
1765    * GData.flushCachedColors(); }
1766    */
1767 
1768   // ///////////////////////////////////////////////////////////////
1769   // delegated to StateManager
1770   // ///////////////////////////////////////////////////////////////
1771 
initialize(boolean clearUserVariables, boolean isPyMOL)1772   public void initialize(boolean clearUserVariables, boolean isPyMOL) {
1773     g = new GlobalSettings(this, g, clearUserVariables);
1774     setStartupBooleans();
1775     setWidthHeightVar();
1776     if (haveDisplay) {
1777       g.setB("_is2D", isJS && !isWebGL);
1778       g.setB("_multiTouchClient", acm.isMTClient());
1779       g.setB("_multiTouchServer", acm.isMTServer());
1780     }
1781     cm.setDefaultColors(false);
1782     setObjectColor("background", "black");
1783     setObjectColor("axis1", "red");
1784     setObjectColor("axis2", "green");
1785     setObjectColor("axis3", "blue");
1786 
1787     // transfer default global settings to managers and g3d
1788 
1789     am.setAnimationOn(false);
1790     am.setAnimationFps(g.animationFps);
1791     sm.playAudio(null);
1792     sm.allowStatusReporting = g.statusReporting;
1793     setBooleanProperty("antialiasDisplay",
1794         (isPyMOL ? true : g.antialiasDisplay));
1795     stm.resetLighting();
1796     tm.setDefaultPerspective();
1797   }
1798 
saveModelOrientation()1799   void saveModelOrientation() {
1800     ms.saveModelOrientation(am.cmi, stm.getOrientation());
1801   }
1802 
restoreModelOrientation(int modelIndex)1803   void restoreModelOrientation(int modelIndex) {
1804     Orientation o = ms.getModelOrientation(modelIndex);
1805     if (o != null)
1806       o.restore(-1, true);
1807   }
1808 
restoreModelRotation(int modelIndex)1809   void restoreModelRotation(int modelIndex) {
1810     Orientation o = ms.getModelOrientation(modelIndex);
1811     if (o != null)
1812       o.restore(-1, false);
1813   }
1814 
1815   // ///////////////////////////////////////////////////////////////
1816   // delegated to TransformManager
1817   // ///////////////////////////////////////////////////////////////
1818 
1819   /**
1820    * This method is only called by JmolGLmol applet._refresh();
1821    *
1822    * @return enough data to update a WebGL view
1823    *
1824    */
1825   @SuppressWarnings("unused")
getGLmolView()1826   public Object getGLmolView() {
1827     TransformManager tm = this.tm;
1828     T3 center = tm.fixedRotationCenter;
1829     Quat q = tm.getRotationQ();
1830     float xtrans = tm.xTranslationFraction;
1831     float ytrans = tm.yTranslationFraction;
1832     float scale = tm.scalePixelsPerAngstrom;
1833     float zoom = tm.zmPctSet;
1834     float cd = tm.cameraDistance;
1835     float pc = tm.screenPixelCount;
1836     boolean pd = tm.perspectiveDepth;
1837     int width = tm.width;
1838     int height = tm.height;
1839 
1840     /**
1841      * @j2sNative
1842      *
1843      *            return { center:center, quaternion:q, xtrans:xtrans,
1844      *            ytrans:ytrans, scale:scale, zoom:zoom, cameraDistance:cd,
1845      *            pixelCount:pc, perspective:pd, width:width, height:height };
1846      */
1847     {
1848       return null;
1849     }
1850   }
1851 
setRotationRadius(float angstroms, boolean doAll)1852   public void setRotationRadius(float angstroms, boolean doAll) {
1853     if (doAll)
1854       angstroms = tm.setRotationRadius(angstroms, false);
1855     // only set the rotationRadius if this is NOT a dataframe
1856     if (ms.setRotationRadius(am.cmi, angstroms))
1857       g.setF("rotationRadius", angstroms);
1858   }
1859 
setCenterBitSet(BS bsCenter, boolean doScale)1860   public void setCenterBitSet(BS bsCenter, boolean doScale) {
1861     // Eval
1862     // setCenterSelected
1863     if (isJmolDataFrame())
1864       return;
1865     tm.setNewRotationCenter(
1866         (BSUtil.cardinalityOf(bsCenter) > 0 ? ms.getAtomSetCenter(bsCenter)
1867             : null),
1868         doScale);
1869   }
1870 
setNewRotationCenter(P3 center)1871   public void setNewRotationCenter(P3 center) {
1872     // eval CENTER command
1873     if (!isJmolDataFrame())
1874       tm.setNewRotationCenter(center, true);
1875   }
1876 
navigate(int keyWhere, int modifiers)1877   void navigate(int keyWhere, int modifiers) {
1878     if (isJmolDataFrame())
1879       return;
1880     tm.navigateKey(keyWhere, modifiers);
1881     if (!tm.vibrationOn && keyWhere != 0)
1882       refresh(REFRESH_REPAINT, "Viewer:navigate()");
1883   }
1884 
move(JmolScriptEvaluator eval, V3 dRot, float dZoom, V3 dTrans, float dSlab, float floatSecondsTotal, int fps)1885   public void move(JmolScriptEvaluator eval, V3 dRot, float dZoom, V3 dTrans,
1886                    float dSlab, float floatSecondsTotal, int fps) {
1887     // from Eval
1888     tm.move(eval, dRot, dZoom, dTrans, dSlab, floatSecondsTotal, fps);
1889     moveUpdate(floatSecondsTotal);
1890   }
1891 
moveTo(JmolScriptEvaluator eval, float floatSecondsTotal, P3 center, V3 rotAxis, float degrees, M3 rotationMatrix, float zoom, float xTrans, float yTrans, float rotationRadius, P3 navCenter, float xNav, float yNav, float navDepth, float cameraDepth, float cameraX, float cameraY)1892   public void moveTo(JmolScriptEvaluator eval, float floatSecondsTotal,
1893                      P3 center, V3 rotAxis, float degrees, M3 rotationMatrix,
1894                      float zoom, float xTrans, float yTrans,
1895                      float rotationRadius, P3 navCenter, float xNav, float yNav,
1896                      float navDepth, float cameraDepth, float cameraX,
1897                      float cameraY) {
1898     // from StateManager -- -1 for time --> no repaint
1899     if (!haveDisplay)
1900       floatSecondsTotal = 0;
1901     setTainted(true);
1902     tm.moveTo(eval, floatSecondsTotal, center, rotAxis, degrees, rotationMatrix,
1903         zoom, xTrans, yTrans, rotationRadius, navCenter, xNav, yNav, navDepth,
1904         cameraDepth, cameraX, cameraY);
1905   }
1906 
moveUpdate(float floatSecondsTotal)1907   public void moveUpdate(float floatSecondsTotal) {
1908     if (floatSecondsTotal > 0)
1909       requestRepaintAndWait("moveUpdate");
1910     else if (floatSecondsTotal == 0)
1911       setSync();
1912   }
1913 
navigatePt(P3 center)1914   public void navigatePt(P3 center) {
1915     // isosurface setHeading
1916     tm.setNavigatePt(center);
1917     setSync();
1918   }
1919 
navigateAxis(V3 rotAxis, float degrees)1920   public void navigateAxis(V3 rotAxis, float degrees) {
1921     // isosurface setHeading
1922     tm.navigateAxis(rotAxis, degrees);
1923     setSync();
1924   }
1925 
navTranslatePercent(float x, float y)1926   public void navTranslatePercent(float x, float y) {
1927     if (isJmolDataFrame())
1928       return;
1929     tm.navTranslatePercentOrTo(0, x, y);
1930     setSync();
1931   }
1932 
zoomBy(int pixels)1933   void zoomBy(int pixels) {
1934     // MouseManager.mouseSinglePressDrag
1935     //if (mouseEnabled)
1936     tm.zoomBy(pixels);
1937     refresh(REFRESH_SYNC, sm.syncingMouse ? "Mouse: zoomBy " + pixels : "");
1938   }
1939 
zoomByFactor(float factor, int x, int y)1940   void zoomByFactor(float factor, int x, int y) {
1941     // MouseManager.mouseWheel
1942     //if (mouseEnabled)
1943     tm.zoomByFactor(factor, x, y);
1944     refresh(REFRESH_SYNC,
1945         !sm.syncingMouse ? ""
1946             : "Mouse: zoomByFactor " + factor
1947                 + (x == Integer.MAX_VALUE ? "" : " " + x + " " + y));
1948   }
1949 
rotateXYBy(float degX, float degY)1950   void rotateXYBy(float degX, float degY) {
1951     // mouseSinglePressDrag
1952     //if (mouseEnabled)
1953     tm.rotateXYBy(degX, degY, null);
1954     refresh(REFRESH_SYNC,
1955         sm.syncingMouse ? "Mouse: rotateXYBy " + degX + " " + degY : "");
1956   }
1957 
spinXYBy(int xDelta, int yDelta, float speed)1958   public void spinXYBy(int xDelta, int yDelta, float speed) {
1959     //if (mouseEnabled)
1960     tm.spinXYBy(xDelta, yDelta, speed);
1961     if (xDelta == 0 && yDelta == 0)
1962       return;
1963     refresh(REFRESH_SYNC,
1964         sm.syncingMouse
1965             ? "Mouse: spinXYBy " + xDelta + " " + yDelta + " " + speed
1966             : "");
1967   }
1968 
rotateZBy(int zDelta, int x, int y)1969   public void rotateZBy(int zDelta, int x, int y) {
1970     // mouseSinglePressDrag
1971     //if (mouseEnabled)
1972     tm.rotateZBy(zDelta, x, y);
1973     refresh(REFRESH_SYNC,
1974         sm.syncingMouse
1975             ? "Mouse: rotateZBy " + zDelta
1976                 + (x == Integer.MAX_VALUE ? "" : " " + x + " " + y)
1977             : "");
1978   }
1979 
rotateSelected(float deltaX, float deltaY, BS bsSelected)1980   void rotateSelected(float deltaX, float deltaY, BS bsSelected) {
1981     // bsSelected null comes from sync.
1982     if (isJmolDataFrame())
1983       return;
1984     //if (mouseEnabled) {
1985     // "true" in setMovableBitSet call is necessary to implement set allowMoveAtoms
1986     tm.rotateXYBy(deltaX, deltaY, setMovableBitSet(bsSelected, true));
1987     refreshMeasures(true);
1988     //}
1989     //TODO: note that sync may not work with set allowRotateSelectedAtoms
1990     refresh(REFRESH_SYNC,
1991         sm.syncingMouse ? "Mouse: rotateMolecule " + deltaX + " " + deltaY
1992             : "");
1993   }
1994 
1995   public BS movableBitSet;
1996 
setMovableBitSet(BS bsSelected, boolean checkMolecule)1997   private BS setMovableBitSet(BS bsSelected, boolean checkMolecule) {
1998     if (bsSelected == null)
1999       bsSelected = bsA();
2000     bsSelected = BSUtil.copy(bsSelected);
2001     BSUtil.andNot(bsSelected, getMotionFixedAtoms());
2002     if (checkMolecule && !g.allowMoveAtoms)
2003       bsSelected = ms.getMoleculeBitSet(bsSelected);
2004     return movableBitSet = bsSelected;
2005   }
2006 
translateXYBy(int xDelta, int yDelta)2007   public void translateXYBy(int xDelta, int yDelta) {
2008     // mouseDoublePressDrag, mouseSinglePressDrag
2009     //if (mouseEnabled)
2010     tm.translateXYBy(xDelta, yDelta);
2011     refresh(REFRESH_SYNC,
2012         sm.syncingMouse ? "Mouse: translateXYBy " + xDelta + " " + yDelta : "");
2013   }
2014 
2015   @Override
rotateFront()2016   public void rotateFront() {
2017     // deprecated
2018     tm.resetRotation();
2019     refresh(REFRESH_REPAINT, "Viewer:rotateFront()");
2020   }
2021 
translate(char xyz, float x, char type, BS bsAtoms)2022   public void translate(char xyz, float x, char type, BS bsAtoms) {
2023     int xy = (type == '\0' ? (int) x
2024         : type == '%' ? tm.percentToPixels(xyz, x)
2025             : tm.angstromsToPixels(x * (type == 'n' ? 10f : 1f)));
2026     if (bsAtoms != null) {
2027       if (xy == 0)
2028         return;
2029       tm.setSelectedTranslation(bsAtoms, xyz, xy);
2030     } else {
2031       switch (xyz) {
2032       case 'X':
2033       case 'x':
2034         if (type == '\0')
2035           tm.translateToPercent('x', x);
2036         else
2037           tm.translateXYBy(xy, 0);
2038         break;
2039       case 'Y':
2040       case 'y':
2041         if (type == '\0')
2042           tm.translateToPercent('y', x);
2043         else
2044           tm.translateXYBy(0, xy);
2045         break;
2046       case 'Z':
2047       case 'z':
2048         if (type == '\0')
2049           tm.translateToPercent('z', x);
2050         else
2051           tm.translateZBy(xy);
2052         break;
2053       }
2054     }
2055     refresh(REFRESH_REPAINT, "Viewer:translate()");
2056   }
2057 
slabByPixels(int pixels)2058   void slabByPixels(int pixels) {
2059     // MouseManager.mouseSinglePressDrag
2060     tm.slabByPercentagePoints(pixels);
2061     refresh(REFRESH_SYNC_MASK, "slabByPixels");
2062   }
2063 
depthByPixels(int pixels)2064   void depthByPixels(int pixels) {
2065     // MouseManager.mouseDoublePressDrag
2066     tm.depthByPercentagePoints(pixels);
2067     refresh(REFRESH_SYNC_MASK, "depthByPixels");
2068 
2069   }
2070 
slabDepthByPixels(int pixels)2071   void slabDepthByPixels(int pixels) {
2072     // MouseManager.mouseSinglePressDrag
2073     tm.slabDepthByPercentagePoints(pixels);
2074     refresh(REFRESH_SYNC_MASK, "slabDepthByPixels");
2075   }
2076 
2077   //  @Override
2078   //  public M4 getUnscaledTransformMatrix() {
2079   //    // unused
2080   //    return tm.getUnscaledTransformMatrix();
2081   //  }
2082 
finalizeTransformParameters()2083   public void finalizeTransformParameters() {
2084     // FrameRenderer
2085     // InitializeModel
2086 
2087     tm.finalizeTransformParameters();
2088     gdata.setSlabAndZShade(tm.slabValue, tm.depthValue,
2089         (tm.zShadeEnabled ? tm.zSlabValue : Integer.MAX_VALUE), tm.zDepthValue,
2090         g.zShadePower);
2091   }
2092 
getScalePixelsPerAngstrom(boolean asAntialiased)2093   public float getScalePixelsPerAngstrom(boolean asAntialiased) {
2094     return tm.scalePixelsPerAngstrom
2095         * (asAntialiased || !antialiased ? 1f : 0.5f);
2096   }
2097 
setSpin(String key, int value)2098   public void setSpin(String key, int value) {
2099     // Eval
2100     if (!PT.isOneOf(key, ";x;y;z;fps;X;Y;Z;FPS;"))
2101       return;
2102     int i = "x;y;z;fps;X;Y;Z;FPS".indexOf(key);
2103     switch (i) {
2104     case 0:
2105       tm.setSpinXYZ(value, Float.NaN, Float.NaN);
2106       break;
2107     case 2:
2108       tm.setSpinXYZ(Float.NaN, value, Float.NaN);
2109       break;
2110     case 4:
2111       tm.setSpinXYZ(Float.NaN, Float.NaN, value);
2112       break;
2113     case 6:
2114     default:
2115       tm.setSpinFps(value);
2116       break;
2117     case 10:
2118       tm.setNavXYZ(value, Float.NaN, Float.NaN);
2119       break;
2120     case 12:
2121       tm.setNavXYZ(Float.NaN, value, Float.NaN);
2122       break;
2123     case 14:
2124       tm.setNavXYZ(Float.NaN, Float.NaN, value);
2125       break;
2126     case 16:
2127       tm.setNavFps(value);
2128       break;
2129     }
2130     g.setI((i < 10 ? "spin" : "nav") + key, value);
2131   }
2132 
getSpinState()2133   public String getSpinState() {
2134     return getStateCreator().getSpinState(false);
2135   }
2136 
2137   /**
2138    *
2139    * @param type
2140    * @param name
2141    * @param bs
2142    * @return String or Quat or P3[]
2143    */
getOrientationText(int type, String name, BS bs)2144   public Object getOrientationText(int type, String name, BS bs) {
2145     switch (type) {
2146     case T.volume:
2147     case T.unitcell:
2148     case T.best:
2149     case T.x:
2150     case T.y:
2151     case T.z:
2152     case T.quaternion:
2153       if (bs == null)
2154         bs = bsA();
2155       if (bs.isEmpty())
2156         return (type == T.volume ? "0"
2157             : type == T.unitcell ? null : new Quat());
2158       Object q = ms.getBoundBoxOrientation(type, bs);
2159       return (name == "best" && type != T.volume
2160           ? ((Quat) q).div(tm.getRotationQ())
2161           : q);
2162     case T.name:
2163       return stm.getSavedOrientationText(name);
2164     default:
2165       return tm.getOrientationText(type, name == "best");
2166     }
2167   }
2168 
2169   // ///////////////////////////////////////////////////////////////
2170   // delegated to ColorManager
2171   // ///////////////////////////////////////////////////////////////
2172 
getCurrentColorRange()2173   public float[] getCurrentColorRange() {
2174     return cm.getPropertyColorRange();
2175   }
2176 
setDefaultColors(boolean isRasmol)2177   private void setDefaultColors(boolean isRasmol) {
2178     cm.setDefaultColors(isRasmol);
2179     g.setB("colorRasmol", isRasmol);
2180     g.setO("defaultColorScheme", (isRasmol ? "rasmol" : "jmol"));
2181   }
2182 
setElementArgb(int elementNumber, int argb)2183   public void setElementArgb(int elementNumber, int argb) {
2184     // Eval
2185     g.setO("=color " + Elements.elementNameFromNumber(elementNumber),
2186         Escape.escapeColor(argb));
2187     cm.setElementArgb(elementNumber, argb);
2188   }
2189 
2190   @Override
setVectorScale(float scale)2191   public void setVectorScale(float scale) {
2192     g.setF("vectorScale", scale);
2193     g.vectorScale = scale;
2194   }
2195 
2196   @Override
setVibrationScale(float scale)2197   public void setVibrationScale(float scale) {
2198     // Eval
2199     // public legacy in JmolViewer
2200     tm.setVibrationScale(scale);
2201     g.vibrationScale = scale;
2202     // because this is public:
2203     g.setF("vibrationScale", scale);
2204   }
2205 
2206   @Override
setVibrationPeriod(float period)2207   public void setVibrationPeriod(float period) {
2208     // Eval
2209     tm.setVibrationPeriod(period);
2210     period = Math.abs(period);
2211     g.vibrationPeriod = period;
2212     // because this is public:
2213     g.setF("vibrationPeriod", period);
2214   }
2215 
setObjectColor(String name, String colorName)2216   void setObjectColor(String name, String colorName) {
2217     if (colorName == null || colorName.length() == 0)
2218       return;
2219     setObjectArgb(name, CU.getArgbFromString(colorName));
2220   }
2221 
setObjectVisibility(String name, boolean b)2222   public void setObjectVisibility(String name, boolean b) {
2223     int objId = StateManager.getObjectIdFromName(name);
2224     if (objId >= 0) {
2225       setShapeProperty(objId, "display", b ? Boolean.TRUE : Boolean.FALSE);
2226     }
2227 
2228   }
2229 
setObjectArgb(String name, int argb)2230   public void setObjectArgb(String name, int argb) {
2231     int objId = StateManager.getObjectIdFromName(name);
2232     if (objId < 0) {
2233       if (name.equalsIgnoreCase("axes")) {
2234         setObjectArgb("axis1", argb);
2235         setObjectArgb("axis2", argb);
2236         setObjectArgb("axis3", argb);
2237       }
2238       return;
2239     }
2240     g.objColors[objId] = argb;
2241     switch (objId) {
2242     case StateManager.OBJ_BACKGROUND:
2243       gdata.setBackgroundArgb(argb);
2244       cm.setColixBackgroundContrast(argb);
2245       break;
2246     }
2247     g.setO(name + "Color", Escape.escapeColor(argb));
2248   }
2249 
setBackgroundImage(String fileName, Object image)2250   public void setBackgroundImage(String fileName, Object image) {
2251     g.backgroundImageFileName = fileName;
2252     gdata.setBackgroundImage(image);
2253   }
2254 
getObjectColix(int objId)2255   public short getObjectColix(int objId) {
2256     int argb = g.objColors[objId];
2257     return (argb == 0 ? cm.colixBackgroundContrast : C.getColix(argb));
2258   }
2259 
2260   // for historical reasons, leave these two:
2261 
2262   @Override
setColorBackground(String colorName)2263   public void setColorBackground(String colorName) {
2264     setObjectColor("background", colorName);
2265   }
2266 
2267   @Override
getBackgroundArgb()2268   public int getBackgroundArgb() {
2269     return g.objColors[(StateManager.OBJ_BACKGROUND)];
2270   }
2271 
2272   /**
2273    * input here is a JC.SHAPE_xxxx identifier
2274    *
2275    * @param iShape
2276    * @param name
2277    * @param mad10
2278    */
setObjectMad10(int iShape, String name, int mad10)2279   public void setObjectMad10(int iShape, String name, int mad10) {
2280     int objId = StateManager
2281         .getObjectIdFromName(name.equalsIgnoreCase("axes") ? "axis" : name);
2282     if (objId < 0)
2283       return;
2284     if (mad10 == -2 || mad10 == -4) { // turn on if not set "showAxes = true"
2285       int m = mad10 + 3;
2286       mad10 = getObjectMad10(objId);
2287       if (mad10 == 0)
2288         mad10 = m;
2289     }
2290     g.setB("show" + name, mad10 != 0);
2291     g.objStateOn[objId] = (mad10 != 0);
2292     if (mad10 == 0)
2293       return;
2294     g.objMad10[objId] = mad10;
2295     setShapeSize(iShape, mad10, null); // just loads it
2296   }
2297 
2298   /**
2299    *
2300    * @param objId
2301    * @return mad10
2302    */
getObjectMad10(int objId)2303   public int getObjectMad10(int objId) {
2304     return (g.objStateOn[objId] ? g.objMad10[objId] : 0);
2305   }
2306 
setPropertyColorScheme(String scheme, boolean isTranslucent, boolean isOverloaded)2307   public void setPropertyColorScheme(String scheme, boolean isTranslucent,
2308                                      boolean isOverloaded) {
2309     g.propertyColorScheme = scheme;
2310     if (scheme.startsWith("translucent ")) {
2311       isTranslucent = true;
2312       scheme = scheme.substring(12).trim();
2313     }
2314     cm.setPropertyColorScheme(scheme, isTranslucent, isOverloaded);
2315   }
2316 
getLightingState()2317   public String getLightingState() {
2318     return getStateCreator().getLightingState(true);
2319   }
2320 
getColorPointForPropertyValue(float val)2321   public P3 getColorPointForPropertyValue(float val) {
2322     // x = {atomno=3}.partialcharge.color
2323     return CU.colorPtFromInt(gdata.getColorArgbOrGray(cm.ce.getColorIndex(val)),
2324         null);
2325   }
2326 
2327   // ///////////////////////////////////////////////////////////////
2328   // delegated to SelectionManager
2329   // ///////////////////////////////////////////////////////////////
2330 
select(BS bs, boolean isGroup, int addRemove, boolean isQuiet)2331   public void select(BS bs, boolean isGroup, int addRemove, boolean isQuiet) {
2332     // Eval, ActionManager
2333     if (isGroup)
2334       bs = getUndeletedGroupAtomBits(bs);
2335     slm.select(bs, addRemove, isQuiet);
2336     shm.setShapeSizeBs(JC.SHAPE_STICKS, Integer.MAX_VALUE, null, null);
2337   }
2338 
2339   @Override
setSelectionSet(BS set)2340   public void setSelectionSet(BS set) {
2341     select(set, false, 0, true);
2342   }
2343 
selectBonds(BS bs)2344   public void selectBonds(BS bs) {
2345     shm.setShapeSizeBs(JC.SHAPE_STICKS, Integer.MAX_VALUE, null, bs);
2346   }
2347 
displayAtoms(BS bs, boolean isDisplay, boolean isGroup, int addRemove, boolean isQuiet)2348   public void displayAtoms(BS bs, boolean isDisplay, boolean isGroup,
2349                            int addRemove, boolean isQuiet) {
2350     // Eval
2351     if (isGroup)
2352       bs = getUndeletedGroupAtomBits(bs);
2353     if (isDisplay)
2354       slm.display(ms, bs, addRemove, isQuiet);
2355     else
2356       slm.hide(ms, bs, addRemove, isQuiet);
2357   }
2358 
getUndeletedGroupAtomBits(BS bs)2359   private BS getUndeletedGroupAtomBits(BS bs) {
2360     bs = ms.getAtoms(T.group, bs);
2361     BSUtil.andNot(bs, slm.bsDeleted);
2362     return bs;
2363   }
2364 
reportSelection(String msg)2365   void reportSelection(String msg) {
2366     if (selectionHalosEnabled)
2367       setTainted(true);
2368     if (isScriptQueued() || g.debugScript)
2369       scriptStatus(msg);
2370   }
2371 
clearAtomSets()2372   private void clearAtomSets() {
2373     slm.setSelectionSubset(null);
2374     definedAtomSets.clear();
2375     if (haveDisplay)
2376       acm.exitMeasurementMode("clearAtomSets");
2377   }
2378 
getDefinedAtomSet(String name)2379   public BS getDefinedAtomSet(String name) {
2380     Object o = definedAtomSets.get(name.toLowerCase());
2381     return (o instanceof BS ? (BS) o : new BS());
2382   }
2383 
2384   @Override
selectAll()2385   public void selectAll() {
2386     // initializeModel
2387     slm.selectAll(false);
2388   }
2389 
2390   @Override
clearSelection()2391   public void clearSelection() {
2392     // not used in this project; in jmolViewer interface, though
2393     slm.clearSelection(true);
2394     g.setB("hideNotSelected", false);
2395   }
2396 
bsA()2397   public BS bsA() {
2398     return slm.getSelectedAtoms();
2399   }
2400 
2401   @Override
addSelectionListener(JmolSelectionListener listener)2402   public void addSelectionListener(JmolSelectionListener listener) {
2403     slm.addListener(listener);
2404   }
2405 
2406   @Override
removeSelectionListener(JmolSelectionListener listener)2407   public void removeSelectionListener(JmolSelectionListener listener) {
2408     slm.addListener(listener);
2409   }
2410 
getAtomBitSetEval(JmolScriptEvaluator eval, Object atomExpression)2411   BS getAtomBitSetEval(JmolScriptEvaluator eval, Object atomExpression) {
2412     return (allowScripting
2413         ? getScriptManager().getAtomBitSetEval(eval, atomExpression)
2414         : new BS());
2415   }
2416 
2417   // ///////////////////////////////////////////////////////////////
2418   // delegated to Mouse (part of the apiPlatform system),
2419   // ///////////////////////////////////////////////////////////////
2420 
2421   /**
2422    * either org.jmol.awt.Mouse or org.jmol.awtjs2d.Mouse
2423    */
2424   private GenericMouseInterface mouse;
2425 
processTwoPointGesture(float[][][] touches)2426   public void processTwoPointGesture(float[][][] touches) {
2427     mouse.processTwoPointGesture(touches);
2428   }
2429 
processMouseEvent(int id, int x, int y, int modifiers, long time)2430   public boolean processMouseEvent(int id, int x, int y, int modifiers,
2431                                    long time) {
2432     // also used for JavaScript from jQuery
2433     return mouse.processEvent(id, x, y, modifiers, time);
2434   }
2435 
2436   // ///////////////////////////////////////////////////////////////
2437   // delegated to ActionManager
2438   // ///////////////////////////////////////////////////////////////
2439 
getRubberBandSelection()2440   public Rectangle getRubberBandSelection() {
2441     return (haveDisplay ? acm.getRubberBand() : null);
2442   }
2443 
isBound(int mouseAction, int jmolAction)2444   public boolean isBound(int mouseAction, int jmolAction) {
2445     return (haveDisplay && acm.bnd(mouseAction, jmolAction));
2446 
2447   }
2448 
getCursorX()2449   public int getCursorX() {
2450     return (haveDisplay ? acm.getCurrentX() : 0);
2451   }
2452 
getCursorY()2453   public int getCursorY() {
2454     return (haveDisplay ? acm.getCurrentY() : 0);
2455   }
2456 
2457   // ///////////////////////////////////////////////////////////////
2458   // delegated to FileManager
2459   // ///////////////////////////////////////////////////////////////
2460 
getDefaultDirectory()2461   public String getDefaultDirectory() {
2462     return g.defaultDirectory;
2463   }
2464 
getLocalUrl(String fileName)2465   public String getLocalUrl(String fileName) {
2466     return apiPlatform.getLocalUrl(fileName);
2467   }
2468 
getFileAsString(String fileName)2469   public String getFileAsString(String fileName) {
2470     return getAsciiFileOrNull(fileName);
2471   }
2472 
2473   @Override
getBufferedInputStream(String fullPathName)2474   public BufferedInputStream getBufferedInputStream(String fullPathName) {
2475     // used by some JVXL readers, also OutputManager.writeZipFile and ScriptManager.openFileAsync
2476     return fm.getBufferedInputStream(fullPathName);
2477   }
2478 
setLoadParameters(Map<String, Object> htParams, boolean isAppend)2479   public Map<String, Object> setLoadParameters(Map<String, Object> htParams,
2480                                                boolean isAppend) {
2481     if (htParams == null)
2482       htParams = new Hashtable<String, Object>();
2483     htParams.put("vwr", this);
2484     if (g.atomTypes.length() > 0)
2485       htParams.put("atomTypes", g.atomTypes);
2486     if (!htParams.containsKey("lattice"))
2487       htParams.put("lattice", g.ptDefaultLattice);
2488     if (g.applySymmetryToBonds)
2489       htParams.put("applySymmetryToBonds", Boolean.TRUE);
2490     if (g.pdbGetHeader)
2491       htParams.put("getHeader", Boolean.TRUE);
2492     if (g.pdbSequential)
2493       htParams.put("isSequential", Boolean.TRUE);
2494     if (g.legacyJavaFloat)
2495       htParams.put("legacyJavaFloat", Boolean.TRUE);
2496     htParams.put("stateScriptVersionInt",
2497         Integer.valueOf(stateScriptVersionInt));
2498     if (!htParams.containsKey("filter")) {
2499       String filter = g.defaultLoadFilter;
2500       if (filter.length() > 0)
2501         htParams.put("filter", filter);
2502     }
2503     boolean merging = (isAppend && !g.appendNew && ms.ac > 0);
2504     htParams.put("baseAtomIndex", Integer.valueOf(isAppend ? ms.ac : 0));
2505     htParams.put("baseBondIndex", Integer.valueOf(isAppend ? ms.bondCount : 0));
2506     htParams.put("baseModelIndex",
2507         Integer.valueOf(ms.ac == 0 ? 0 : ms.mc + (merging ? -1 : 0)));
2508     if (merging)
2509       htParams.put("merging", Boolean.TRUE);
2510     return htParams;
2511   }
2512 
2513   // //////////////// methods that open a file to create a model set ///////////
2514 
2515   //  *indicates when a refresh is made (external apps and applets only)
2516   //
2517   //  external apps only
2518   //    via loadInline(List)*
2519   //      createModelSetAndReturnError
2520   //
2521   //  openDOM, openReader, openFile, openFiles
2522   //    via loadModelFromFileRepaint*
2523   //      createModelSetAndReturnError
2524   //
2525   //  loadInLine(String) via loadInLineScriptRepaint*
2526   //  FileDropper (string drop) via openStringInline*
2527   //    via openStringInlineParamsAppend
2528   //      createModelSetAndReturnError
2529   //
2530   //  external apps, applet only, via loadInline(String[])*
2531   //    via openStringsInlineParamsAppend
2532   //      createModelSetAndReturnError
2533   //
2534   //  script LOAD
2535   //    via loadModelFromFile
2536   //      createModelSetAndReturnError
2537   //
2538   //  script CALCULATE HYDROGENS, PLOT, ZAP (modelkit)
2539   //    via openStringInlineParamsAppend
2540   //      createModelSetAndReturnError
2541   //
2542   //  script LOAD DATA via loadFileFull and loadInlineScript
2543   //    openStringsInlineParamsAppend
2544   //      createModelSetAndReturnError
2545 
2546   /**
2547    * opens a file as a model, a script, or a surface via the creation of a
2548    * script that is queued \t at the beginning disallows script option - used by
2549    * JmolFileDropper and JmolPanel file-open actions - sets up a script to load
2550    * the file.
2551    *
2552    * Called from (JSmolCore.js)Jmol.$appEvent(,,"drop").reader.onloadend()
2553    *
2554    * @param fileName
2555    * @param flags
2556    *        1=pdbCartoons, 2=no scripting, 4=append, 8=fileOpen, 16=fileDropped
2557    *
2558    */
2559   @Override
openFileAsyncSpecial(String fileName, int flags)2560   public void openFileAsyncSpecial(String fileName, int flags) {
2561     getScriptManager().openFileAsync(fileName, flags, false);
2562   }
2563 
openFileDropped(String fname, boolean checkDims)2564   public void openFileDropped(String fname, boolean checkDims) {
2565     getScriptManager().openFileAsync(fname, JmolScriptManager.FILE_DROPPED,
2566         checkDims);
2567   }
2568 
2569   /**
2570    *
2571    * for JmolSimpleViewer -- external applications only (and no-script
2572    * JavaScript)
2573    *
2574    * @param fileName
2575    * @return null or error
2576    */
2577   @Override
openFile(String fileName)2578   public String openFile(String fileName) {
2579     zap(true, true, false);
2580     return loadModelFromFileRepaint(null, fileName, null, null);
2581   }
2582 
2583   /**
2584    * for JmolSimpleViewer -- external applications only
2585    *
2586    * @param fileNames
2587    * @return null or error
2588    */
2589   @Override
openFiles(String[] fileNames)2590   public String openFiles(String[] fileNames) {
2591     zap(true, true, false);
2592     return loadModelFromFileRepaint(null, null, fileNames, null);
2593   }
2594 
2595   /**
2596    * Opens the file, given an already-created reader.
2597    *
2598    * @param fullPathName
2599    * @param fileName
2600    *        name without path or can just be null
2601    * @param reader
2602    *        could be Reader, BufferedInputStream, or byte[]
2603    * @return null or error message
2604    */
2605   @Override
openReader(String fullPathName, String fileName, Object reader)2606   public String openReader(String fullPathName, String fileName,
2607                            Object reader) {
2608     zap(true, true, false);
2609     return loadModelFromFileRepaint(fullPathName, fileName, null, reader);
2610   }
2611 
2612   /**
2613    * applet DOM method -- does not preserve state
2614    *
2615    * @param DOMNode
2616    * @return null or error
2617    *
2618    */
2619   @Override
openDOM(Object DOMNode)2620   public String openDOM(Object DOMNode) {
2621     // applet.loadDOMNode
2622     zap(true, true, false);
2623     return loadModelFromFileRepaint("?", "?", null, DOMNode);
2624   }
2625 
2626   /**
2627    *
2628    * for JmolSimpleViewer -- external applications only (and no-script
2629    * JavaScript)
2630    *
2631    * @param fullPathName
2632    * @param fileName
2633    * @param fileNames
2634    * @param reader
2635    * @return error message or null
2636    *
2637    */
loadModelFromFileRepaint(String fullPathName, String fileName, String[] fileNames, Object reader)2638   private String loadModelFromFileRepaint(String fullPathName, String fileName,
2639                                           String[] fileNames, Object reader) {
2640     String ret = loadModelFromFile(fullPathName, fileName, fileNames, reader,
2641         false, null, null, null, 0, " ");
2642     refresh(REFRESH_REPAINT, "loadModelFromFileRepaint");
2643     return ret;
2644   }
2645 
2646   /**
2647    * Used by the ScriptEvaluator LOAD command to open one or more files. Now
2648    * necessary for EVERY load of a file, as loadScript must be passed to the
2649    * ModelLoader.
2650    *
2651    * @param fullPathName
2652    *        may be null; used only when reader != null
2653    * @param fileName
2654    *        must not be null
2655    * @param fileNames
2656    *        when present, reader is ignored
2657    * @param reader
2658    *        may be a Reader, BufferedReader, byte[], or BufferedInputStream
2659    * @param isAppend
2660    * @param htParams
2661    * @param loadScript
2662    * @param sOptions
2663    * @param tokType
2664    * @param filecat
2665    *        + or null, -, or space
2666    * @return null or error
2667    */
loadModelFromFile(String fullPathName, String fileName, String[] fileNames, Object reader, boolean isAppend, Map<String, Object> htParams, SB loadScript, SB sOptions, int tokType, String filecat)2668   public String loadModelFromFile(String fullPathName, String fileName,
2669                                   String[] fileNames, Object reader,
2670                                   boolean isAppend,
2671                                   Map<String, Object> htParams, SB loadScript,
2672                                   SB sOptions, int tokType, String filecat) {
2673     if (htParams == null)
2674       htParams = setLoadParameters(null, isAppend);
2675     if (tokType != T.nada)
2676       htParams.put("dataType", T.nameOf(tokType));
2677     if (filecat != " ")
2678       htParams.put("concatenate", Boolean.TRUE);
2679     Object atomSetCollection;
2680     String[] saveInfo = fm.getFileInfo();
2681 
2682     // testing only reader = fm.getFileAsBytes(fileName,  null);
2683     if (fileNames != null) {
2684 
2685       // 1) a set of file names
2686 
2687       if (loadScript == null) {
2688         loadScript = new SB().append("load files");
2689         for (int i = 0; i < fileNames.length; i++)
2690           loadScript.append(i == 0 || filecat == null ? " " : filecat)
2691               .append("/*file*/$FILENAME" + (i + 1) + "$");
2692         if (sOptions.length() > 0)
2693           loadScript.append(" /*options*/ ").append(sOptions.toString());
2694       }
2695       long timeBegin = System.currentTimeMillis();
2696 
2697       atomSetCollection = fm.createAtomSetCollectionFromFiles(fileNames,
2698           setLoadParameters(htParams, isAppend), isAppend);
2699       long ms = System.currentTimeMillis() - timeBegin;
2700       Logger.info("openFiles(" + fileNames.length + ") " + ms + " ms");
2701       fileNames = (String[]) htParams.get("fullPathNames");
2702       String[] fileTypes = (String[]) htParams.get("fileTypes");
2703       String s = loadScript.toString();
2704       for (int i = 0; i < fileNames.length; i++) {
2705         String fname = fileNames[i];
2706         if (fileTypes != null && fileTypes[i] != null)
2707           fname = fileTypes[i] + "::" + fname;
2708         s = PT.rep(s, "$FILENAME" + (i + 1) + "$",
2709             PT.esc(FileManager.fixDOSName(fname)));
2710       }
2711 
2712       loadScript = new SB().append(s);
2713 
2714     } else if (reader == null) {
2715 
2716       // 2) a standard, single file
2717 
2718       if (loadScript == null)
2719         loadScript = new SB().append("load /*file*/$FILENAME$");
2720 
2721       atomSetCollection = openFileFull(fileName, isAppend, htParams,
2722           loadScript);
2723 
2724     } else if (reader instanceof Reader || reader instanceof BufferedInputStream
2725         || AU.isAB(reader)) {
2726 
2727       // 3) a file reader, BufferedInputStream, or byte[] (not used by Jmol)
2728 
2729       atomSetCollection = fm.createAtomSetCollectionFromReader(fullPathName,
2730           fileName, reader, setLoadParameters(htParams, isAppend));
2731 
2732     } else {
2733 
2734       // 4) a DOM reader (could be used by Jmol)
2735 
2736       atomSetCollection = fm.createAtomSetCollectionFromDOM(reader,
2737           setLoadParameters(htParams, isAppend));
2738 
2739     }
2740 
2741     // OK, the file has been read and is now closed.
2742 
2743     if (tokType != 0) { // all we are doing is reading atom data
2744       fm.setFileInfo(saveInfo);
2745       return loadAtomDataAndReturnError(atomSetCollection, tokType);
2746     }
2747 
2748     if (htParams.containsKey("isData"))
2749       return (String) atomSetCollection;
2750 
2751     // now we fix the load script (possibly) with the full path name
2752     if (loadScript != null && !(atomSetCollection instanceof String)) {
2753       String fname = (String) htParams.get("fullPathName");
2754       if (fname == null)
2755         fname = "";
2756       // may have been modified.
2757       if (htParams.containsKey("loadScript"))
2758         loadScript = (SB) htParams.get("loadScript");
2759       htParams.put("loadScript",
2760           loadScript = new SB().append(javajs.util.PT.rep(loadScript.toString(),
2761               "$FILENAME$", PT.esc(FileManager.fixDOSName(fname)))));
2762     }
2763 
2764     // and finally to create the model set...
2765 
2766     return createModelSetAndReturnError(atomSetCollection, isAppend, loadScript,
2767         htParams);
2768   }
2769 
2770   Map<String, Object> ligandModels;
2771   Map<String, Boolean> ligandModelSet;
2772 
setLigandModel(String key, String data)2773   public void setLigandModel(String key, String data) {
2774     if (ligandModels == null)
2775       ligandModels = new Hashtable<String, Object>();
2776     ligandModels.put(key, data);
2777   }
2778 
2779   /**
2780    * obtain CIF data for a ligand for purposes of adding hydrogens or for any
2781    * other purpose in terms of saving a data set for a file in a state
2782    *
2783    * @param id
2784    *        unique key; if null, clear "bad" entries from the set.
2785    * @param prefix
2786    * @param suffix
2787    *        or fileName
2788    * @param terminator
2789    *        Only save to this if not null
2790    * @return a ligand model or a string if just file data or null
2791    */
getLigandModel(String id, String prefix, String suffix, String terminator)2792   public Object getLigandModel(String id, String prefix, String suffix,
2793                                String terminator) {
2794     // getLigandModel(id, m40File, "_file", "----"));
2795     // getLigandModel(group3, "ligand_", "_data", null);
2796     if (id == null) {
2797       if (ligandModelSet != null) {
2798         Iterator<Map.Entry<String, Object>> e = ligandModels.entrySet()
2799             .iterator();
2800         while (e.hasNext()) {
2801           Entry<String, Object> entry = e.next();
2802           if (entry.getValue() instanceof Boolean)
2803             e.remove();
2804         }
2805       }
2806       return null;
2807     }
2808     id = id.replace('\\', '/');
2809     boolean isLigand = prefix.equals("ligand_");
2810     id = (id.indexOf("/cif") >= 0 ? id
2811         : isLigand ? id.toUpperCase() : id.substring(id.lastIndexOf("/") + 1));
2812     if (ligandModelSet == null)
2813       ligandModelSet = new Hashtable<String, Boolean>();
2814     ligandModelSet.put(id, Boolean.TRUE);
2815     if (ligandModels == null)
2816       ligandModels = new Hashtable<String, Object>();
2817     int pngPt = id.indexOf("|");
2818     if (pngPt >= 0)
2819       id = id.substring(id.indexOf("|") + 1);
2820     Object model = (terminator == null ? ligandModels.get(id) : null);
2821     String data;
2822     String fname = null;
2823     if (model instanceof Boolean)
2824       return null;
2825     if (model == null && (terminator == null || pngPt >= 0))
2826       model = ligandModels.get(id + suffix);
2827     boolean isError = false;
2828     boolean isNew = (model == null);
2829     if (isNew) {
2830       String s;
2831       if (isLigand) {
2832         fname = (String) setLoadFormat("#" + id, '#', false);
2833         if (fname.length() == 0)
2834           return null;
2835         scriptEcho("fetching " + fname);
2836         s = getFileAsString3(fname, false, null);
2837       } else {
2838         scriptEcho("fetching " + prefix);
2839         s = getFileAsString3(prefix, false, null);
2840         int pt = (terminator == null ? -1 : s.indexOf(terminator));
2841         if (pt >= 0)
2842           s = s.substring(0, pt);
2843       }
2844       isError = (s.indexOf("java.") == 0);
2845       model = s;
2846       if (!isError)
2847         ligandModels.put(id + suffix, model);
2848     }
2849     if (!isLigand) {
2850       if (!isNew)
2851         scriptEcho(prefix + " loaded from cache");
2852       return model;
2853     }
2854     // process ligand business
2855 
2856     if (!isError && model instanceof String) {
2857       data = (String) model;
2858       // TODO: check for errors in reading file
2859       if (data.length() != 0) {
2860         Map<String, Object> htParams = new Hashtable<String, Object>();
2861         htParams.put("modelOnly", Boolean.TRUE);
2862         model = getModelAdapter().getAtomSetCollectionReader("ligand", null,
2863             Rdr.getBR(data), htParams);
2864         isError = (model instanceof String);
2865         if (!isError) {
2866           model = getModelAdapter().getAtomSetCollection(model);
2867           isError = (model instanceof String);
2868           if (fname != null && !isError)
2869             scriptEcho((String) getModelAdapter()
2870                 .getAtomSetCollectionAuxiliaryInfo(model).get("modelLoadNote"));
2871         }
2872       }
2873     }
2874     if (isError) {
2875       scriptEcho(model.toString());
2876       ligandModels.put(id, Boolean.FALSE);
2877       return null;
2878     }
2879     return model;
2880   }
2881 
2882   /**
2883    *
2884    * does NOT repaint
2885    *
2886    * @param fileName
2887    * @param isAppend
2888    * @param htParams
2889    * @param loadScript
2890    *        only necessary for string reading
2891    * @return an AtomSetCollection or a String (error)
2892    */
openFileFull(String fileName, boolean isAppend, Map<String, Object> htParams, SB loadScript)2893   private Object openFileFull(String fileName, boolean isAppend,
2894                               Map<String, Object> htParams, SB loadScript) {
2895     if (fileName == null)
2896       return null;
2897     if (fileName.equals("String[]")) {
2898       // no reloading of string[] or file[] data -- just too complicated
2899       return null;
2900     }
2901     Object atomSetCollection;
2902     String msg = "openFile(" + fileName + ")";
2903     Logger.startTimer(msg);
2904     htParams = setLoadParameters(htParams, isAppend);
2905     boolean isLoadVariable = fileName.startsWith("@");
2906     boolean haveFileData = (htParams.containsKey("fileData"));
2907     if (fileName.indexOf('$') == 0)
2908       htParams.put("smilesString", fileName.substring(1));
2909     boolean isString = (fileName.equals("string")
2910         || fileName.equals(JC.MODELKIT_ZAP_TITLE));
2911     String strModel = null;
2912     if (haveFileData) {
2913       strModel = (String) htParams.get("fileData");
2914       if (htParams.containsKey("isData")) {
2915         Object o = loadInlineScript(strModel, '\0', isAppend, htParams);
2916         lastData = (g.preserveState ? getDataManager().createFileData(strModel)
2917             : null);
2918         return o;
2919       }
2920     } else if (isString) {
2921       strModel = ms.getInlineData(-1);
2922       if (strModel == null)
2923         if (g.modelKitMode)
2924           strModel = JC.MODELKIT_ZAP_STRING;
2925         else
2926           return "cannot find string data";
2927       if (loadScript != null)
2928         htParams.put("loadScript",
2929             loadScript = new SB().append(PT.rep(loadScript.toString(),
2930                 "/*file*/$FILENAME$", "/*data*/data \"model inline\"\n"
2931                     + strModel + "end \"model inline\"")));
2932     }
2933     if (strModel != null) {
2934       if (!isAppend)
2935         zap(true, false/*true*/, false);
2936       if (!isLoadVariable && (!haveFileData || isString))
2937         getStateCreator().getInlineData(loadScript, strModel, isAppend,
2938             (Integer) htParams.get("appendToModelIndex"),
2939             g.defaultLoadFilter);
2940       atomSetCollection = fm.createAtomSetCollectionFromString(strModel,
2941           htParams, isAppend);
2942     } else {
2943 
2944       // if the filename has a "?" at the beginning, we don't zap,
2945       // because the user might cancel the operation.
2946 
2947       atomSetCollection = fm.createAtomSetCollectionFromFile(fileName, htParams,
2948           isAppend);
2949     }
2950     Logger.checkTimer(msg, false);
2951     return atomSetCollection;
2952   }
2953 
2954   /**
2955    * only used by file dropper.
2956    */
2957 
2958   @Override
openStringInline(String strModel)2959   public String openStringInline(String strModel) {
2960     // JmolSimpleViewer; JmolFileDropper inline string event
2961     String ret = openStringInlineParamsAppend(strModel, null, false);
2962     refresh(REFRESH_REPAINT, "openStringInline");
2963     return ret;
2964   }
2965 
2966   /**
2967    * from Applet and external applications only
2968    */
2969 
2970   @Override
loadInline(String strModel)2971   public String loadInline(String strModel) {
2972     // jmolViewer interface
2973     return loadInlineScriptRepaint(strModel, g.inlineNewlineChar, false);
2974   }
2975 
2976   /**
2977    * external apps only
2978    *
2979    */
2980 
2981   @Override
loadInline(String strModel, char newLine)2982   public String loadInline(String strModel, char newLine) {
2983     // JmolViewer interface
2984     return loadInlineScriptRepaint(strModel, newLine, false);
2985   }
2986 
2987   /**
2988    * used by applet and console
2989    */
2990 
2991   @Override
loadInlineAppend(String strModel, boolean isAppend)2992   public String loadInlineAppend(String strModel, boolean isAppend) {
2993     // JmolViewer interface
2994     return loadInlineScriptRepaint(strModel, '\0', isAppend);
2995   }
2996 
loadInlineScriptRepaint(String strModel, char newLine, boolean isAppend)2997   private String loadInlineScriptRepaint(String strModel, char newLine,
2998                                          boolean isAppend) {
2999     String ret = loadInlineScript(strModel, newLine, isAppend, null);
3000     refresh(REFRESH_REPAINT, "loadInlineScript");
3001     return ret;
3002   }
3003 
3004   /**
3005    * external apps only
3006    *
3007    */
3008 
3009   @Override
loadInline(String[] arrayModels)3010   public String loadInline(String[] arrayModels) {
3011     // JmolViewer interface
3012     return loadInline(arrayModels, false);
3013   }
3014 
3015   /**
3016    * external apps and applet only
3017    *
3018    */
3019   @Override
loadInline(String[] arrayModels, boolean isAppend)3020   public String loadInline(String[] arrayModels, boolean isAppend) {
3021     // JmolViewer interface
3022     // Eval data
3023     // loadInline
3024     if (arrayModels == null || arrayModels.length == 0)
3025       return null;
3026     String ret = openStringsInlineParamsAppend(arrayModels,
3027         new Hashtable<String, Object>(), isAppend);
3028     refresh(REFRESH_REPAINT, "loadInline String[]");
3029     return ret;
3030   }
3031 
3032   /**
3033    * External applications only; does not preserve state -- intentionally!
3034    *
3035    * @param arrayData
3036    * @param isAppend
3037    * @return null or error string
3038    *
3039    */
3040   @Override
loadInline(java.util.List<Object> arrayData, boolean isAppend)3041   public String loadInline(java.util.List<Object> arrayData, boolean isAppend) {
3042     // NO STATE SCRIPT -- HERE WE ARE TRYING TO CONSERVE SPACE
3043 
3044     // loadInline
3045     if (arrayData == null || arrayData.size() == 0)
3046       return null;
3047     if (!isAppend)
3048       zap(true, false/*true*/, false);
3049     Lst<Object> list = new Lst<Object>();
3050     for (int i = 0; i < arrayData.size(); i++)
3051       list.addLast(arrayData.get(i));
3052     Object atomSetCollection = fm.createAtomSeCollectionFromArrayData(list,
3053         setLoadParameters(null, isAppend), isAppend);
3054     String ret = createModelSetAndReturnError(atomSetCollection, isAppend, null,
3055         new Hashtable<String, Object>());
3056     refresh(REFRESH_REPAINT, "loadInline");
3057     return ret;
3058   }
3059 
3060   /**
3061    * used by loadInline and openFileFull
3062    *
3063    * @param strModel
3064    * @param newLine
3065    * @param isAppend
3066    * @param htParams
3067    * @return null or error message
3068    */
loadInlineScript(String strModel, char newLine, boolean isAppend, Map<String, Object> htParams)3069   private String loadInlineScript(String strModel, char newLine,
3070                                   boolean isAppend,
3071                                   Map<String, Object> htParams) {
3072     if (strModel == null || strModel.length() == 0)
3073       return null;
3074     strModel = fixInlineString(strModel, newLine);
3075     if (newLine != 0)
3076       Logger.info("loading model inline, " + strModel.length()
3077           + " bytes, with newLine character " + (int) newLine + " isAppend="
3078           + isAppend);
3079     if (Logger.debugging)
3080       Logger.debug(strModel);
3081     String datasep = getDataSeparator();
3082     int i;
3083     if (datasep != null && datasep != "" && (i = strModel.indexOf(datasep)) >= 0
3084         && strModel.indexOf("# Jmol state") < 0) {
3085       int n = 2;
3086       while ((i = strModel.indexOf(datasep, i + 1)) >= 0)
3087         n++;
3088       String[] strModels = new String[n];
3089       int pt = 0, pt0 = 0;
3090       for (i = 0; i < n; i++) {
3091         pt = strModel.indexOf(datasep, pt0);
3092         if (pt < 0)
3093           pt = strModel.length();
3094         strModels[i] = strModel.substring(pt0, pt);
3095         pt0 = pt + datasep.length();
3096       }
3097       return openStringsInlineParamsAppend(strModels, htParams, isAppend);
3098     }
3099     return openStringInlineParamsAppend(strModel, htParams, isAppend);
3100   }
3101 
fixInlineString(String strModel, char newLine)3102   public static String fixInlineString(String strModel, char newLine) {
3103     // only if first character is "|" do we consider "|" to be new line
3104     int i;
3105     if (strModel.indexOf("\\/n") >= 0) {
3106       // the problem is that when this string is passed to Jmol
3107       // by the web page <embed> mechanism, browsers differ
3108       // in how they handle CR and LF. Some will pass it,
3109       // some will not.
3110       strModel = PT.rep(strModel, "\n", "");
3111       strModel = PT.rep(strModel, "\\/n", "\n");
3112       newLine = 0;
3113     }
3114     if (newLine != 0 && newLine != '\n') {
3115       boolean repEmpty = (strModel.indexOf('\n') >= 0);
3116       int len = strModel.length();
3117       for (i = 0; i < len && strModel.charAt(i) == ' '; ++i) {
3118       }
3119       if (i < len && strModel.charAt(i) == newLine)
3120         strModel = strModel.substring(i + 1);
3121       if (repEmpty)
3122         strModel = PT.rep(strModel, "" + newLine, "");
3123       else
3124         strModel = strModel.replace(newLine, '\n');
3125     }
3126     return strModel;
3127   }
3128 
3129   /**
3130    * Only used for adding hydrogen atoms and adding the model kit methane model;
3131    * not part of the public interface.
3132    *
3133    * @param strModel
3134    * @param htParams
3135    * @param isAppend
3136    * @return null or error string
3137    *
3138    */
openStringInlineParamsAppend(String strModel, Map<String, Object> htParams, boolean isAppend)3139   public String openStringInlineParamsAppend(String strModel,
3140                                              Map<String, Object> htParams,
3141                                              boolean isAppend) {
3142     // loadInline, openStringInline
3143 
3144     String type = getModelAdapter().getFileTypeName(Rdr.getBR(strModel));
3145     if (type == null)
3146       return "unknown file type";
3147     if (type.equals("spt")) {
3148       return "cannot open script inline";
3149     }
3150 
3151     htParams = setLoadParameters(htParams, isAppend);
3152     SB loadScript = (SB) htParams.get("loadScript");
3153     boolean isLoadCommand = htParams.containsKey("isData");
3154     if (loadScript == null)
3155       loadScript = new SB();
3156     if (!isAppend)
3157       zap(true, false/*true*/, false);
3158     if (!isLoadCommand)
3159       getStateCreator().getInlineData(loadScript, strModel, isAppend,
3160           (Integer) htParams.get("appendToModelIndex"), g.defaultLoadFilter);
3161     Object atomSetCollection = fm.createAtomSetCollectionFromString(strModel,
3162         htParams, isAppend);
3163     return createModelSetAndReturnError(atomSetCollection, isAppend, loadScript,
3164         htParams);
3165   }
3166 
3167   /**
3168    * opens multiple files inline; does NOT repaint
3169    *
3170    * @param arrayModels
3171    * @param htParams
3172    * @param isAppend
3173    * @return null or error message
3174    */
openStringsInlineParamsAppend(String[] arrayModels, Map<String, Object> htParams, boolean isAppend)3175   private String openStringsInlineParamsAppend(String[] arrayModels,
3176                                                Map<String, Object> htParams,
3177                                                boolean isAppend) {
3178     // loadInline
3179     SB loadScript = new SB();
3180     if (!isAppend)
3181       zap(true, false/*true*/, false);
3182     Object atomSetCollection = fm.createAtomSeCollectionFromStrings(arrayModels,
3183         loadScript, setLoadParameters(htParams, isAppend), isAppend);
3184     return createModelSetAndReturnError(atomSetCollection, isAppend, loadScript,
3185         htParams);
3186   }
3187 
getInlineChar()3188   public char getInlineChar() {
3189     // used by the ScriptEvaluator DATA command
3190     return g.inlineNewlineChar;
3191   }
3192 
getDataSeparator()3193   String getDataSeparator() {
3194     // used to separate data files within a single DATA command
3195     return (String) g.getParameter("dataseparator", true);
3196   }
3197 
3198   ////////// create the model set ////////////
3199 
3200   /**
3201    * finally(!) we are ready to create the "model set" from the "atom set
3202    * collection" - does NOT repaint
3203    *
3204    * @param atomSetCollection
3205    * @param isAppend
3206    * @param loadScript
3207    *        if null, then some special method like DOM; turn of preserveState
3208    * @param htParams
3209    * @return errMsg
3210    */
createModelSetAndReturnError(Object atomSetCollection, boolean isAppend, SB loadScript, Map<String, Object> htParams)3211   private String createModelSetAndReturnError(Object atomSetCollection,
3212                                               boolean isAppend, SB loadScript,
3213                                               Map<String, Object> htParams) {
3214 
3215     Logger.startTimer("creating model");
3216 
3217     String fullPathName = fm.getFullPathName(false);
3218     String fileName = fm.getFileName();
3219     String errMsg;
3220     if (loadScript == null) {
3221       setBooleanProperty("preserveState", false);
3222       loadScript = new SB().append("load \"???\"");
3223     }
3224     if (atomSetCollection instanceof String) {
3225       errMsg = (String) atomSetCollection;
3226       setFileLoadStatus(FIL.NOT_LOADED, fullPathName, null, null, errMsg, null);
3227       if (displayLoadErrors && !isAppend && !errMsg.equals("#CANCELED#")
3228           && !errMsg.startsWith(JC.READER_NOT_FOUND))
3229         zapMsg(errMsg);
3230       return errMsg;
3231     }
3232     if (isAppend)
3233       clearAtomSets();
3234     else if (g.modelKitMode && !fileName.equals("Jmol Model Kit"))
3235       setModelKitMode(false);
3236     setFileLoadStatus(FIL.CREATING_MODELSET, fullPathName, fileName, null, null,
3237         null);
3238 
3239     // null fullPathName implies we are doing a merge
3240     pushHoldRepaintWhy("createModelSet");
3241     setErrorMessage(null, null);
3242     try {
3243       BS bsNew = new BS();
3244       mm.createModelSet(fullPathName, fileName, loadScript, atomSetCollection,
3245           bsNew, isAppend);
3246       if (bsNew.cardinality() > 0) {
3247         // is a 2D dataset, as from JME
3248         String jmolScript = (String) ms.getInfoM("jmolscript");
3249         if (ms.getMSInfoB("doMinimize")) {
3250           try {
3251             JmolScriptEvaluator eval = (JmolScriptEvaluator) htParams
3252                 .get("eval");
3253             BS stereo = getAtomBitSet("_C & connected(3) & !connected(double)");
3254             stereo.and(bsNew);
3255             if (stereo.nextSetBit(0) >= 0) {
3256               bsNew.or(addHydrogens(stereo,MIN_NO_RANGE | MIN_SILENT | MIN_QUICK));
3257             }
3258             minimize(eval, Integer.MAX_VALUE, 0, bsNew, null, 0,
3259                 MIN_ADDH | MIN_NO_RANGE | MIN_SILENT | MIN_QUICK);
3260           } catch (Exception e) {
3261           }
3262         } else {
3263           addHydrogens(bsNew, MIN_SILENT | MIN_QUICK);
3264         }
3265         // no longer necessary? -- this is the JME/SMILES data:
3266         if (jmolScript != null)
3267           ms.msInfo.put("jmolscript", jmolScript);
3268       }
3269       initializeModel(isAppend);
3270       // if (global.modelkitMode &&
3271       // (modelSet.modelCount > 1 || modelSet.models[0].isPDB()))
3272       // setBooleanProperty("modelkitmode", false);
3273 
3274     } catch (Error er) {
3275       handleError(er, true);
3276       errMsg = getShapeErrorState();
3277       errMsg = ("ERROR creating model: " + er
3278           + (errMsg.length() == 0 ? "" : "|" + errMsg));
3279       zapMsg(errMsg);
3280       setErrorMessage(errMsg, null);
3281     }
3282     popHoldRepaint("createModelSet " + JC.REPAINT_IGNORE);
3283     errMsg = getErrorMessage();
3284 
3285     setFileLoadStatus(FIL.CREATED, fullPathName, fileName, ms.modelSetName,
3286         errMsg, (Boolean) htParams.get("async"));
3287     if (isAppend) {
3288       selectAll();
3289       setTainted(true);
3290       axesAreTainted = true;
3291     }
3292     atomSetCollection = null;
3293     Logger.checkTimer("creating model", false);
3294     System.gc();
3295     return errMsg;
3296   }
3297 
3298   /**
3299    *
3300    * or just apply the data to the current model set
3301    *
3302    * @param atomSetCollection
3303    * @param tokType
3304    * @return error or null
3305    */
loadAtomDataAndReturnError(Object atomSetCollection, int tokType)3306   private String loadAtomDataAndReturnError(Object atomSetCollection,
3307                                             int tokType) {
3308     if (atomSetCollection instanceof String)
3309       return (String) atomSetCollection;
3310     setErrorMessage(null, null);
3311     try {
3312       String script = mm.createAtomDataSet(atomSetCollection, tokType);
3313       switch (tokType) {
3314       case T.xyz:
3315         if (script != null)
3316           runScriptCautiously(script);
3317         break;
3318       case T.vibration:
3319         setStatusFrameChanged(true, false);
3320         break;
3321       case T.vanderwaals:
3322         shm.deleteVdwDependentShapes(null);
3323         break;
3324       }
3325     } catch (Error er) {
3326       handleError(er, true);
3327       String errMsg = getShapeErrorState();
3328       errMsg = ("ERROR adding atom data: " + er
3329           + (errMsg.length() == 0 ? "" : "|" + errMsg));
3330       zapMsg(errMsg);
3331       setErrorMessage(errMsg, null);
3332       setParallel(false);
3333     }
3334     return getErrorMessage();
3335   }
3336 
3337   ////////// File-related methods ////////////
3338 
getCurrentFileAsString(String state)3339   public String getCurrentFileAsString(String state) {
3340     String filename = fm.getFullPathName(false);
3341     if (filename.equals("string") || filename.equals(JC.MODELKIT_ZAP_TITLE))
3342       return ms.getInlineData(am.cmi);
3343     if (filename.equals("String[]"))
3344       return filename;
3345     if (filename == "JSNode")
3346       return "<DOM NODE>";
3347     //    filename = mm.getModelSetPathName();
3348     //    if (filename == null)
3349     //      return null;
3350     return getFileAsString4(filename, -1, true, false, false, state);
3351   }
3352 
3353   /**
3354    *
3355    * @param filename
3356    * @return String[2] where [0] is fullpathname and [1] is error message or
3357    *         null
3358    */
getFullPathNameOrError(String filename)3359   public String[] getFullPathNameOrError(String filename) {
3360     String[] data = new String[2];
3361     fm.getFullPathNameOrError(filename, false, data);
3362     return data;
3363   }
3364 
getFileAsString3(String name, boolean checkProtected, String state)3365   public String getFileAsString3(String name, boolean checkProtected,
3366                                  String state) {
3367     return getFileAsString4(name, -1, false, false, checkProtected, state);
3368   }
3369 
getFileAsString4(String name, int nBytesMax, boolean doSpecialLoad, boolean allowBinary, boolean checkProtected, String state)3370   public String getFileAsString4(String name, int nBytesMax,
3371                                  boolean doSpecialLoad, boolean allowBinary,
3372                                  boolean checkProtected, String state) {
3373     if (name == null)
3374       return getCurrentFileAsString(state);
3375     String[] data = new String[] { name, null };
3376     // ignore error completely
3377     fm.getFileDataAsString(data, nBytesMax, doSpecialLoad, allowBinary,
3378         checkProtected);
3379     return data[1];
3380   }
3381 
getAsciiFileOrNull(String name)3382   public String getAsciiFileOrNull(String name) {
3383     String[] data = new String[] { name, null };
3384     return (fm.getFileDataAsString(data, -1, false, false, false) ? data[1]
3385         : null);
3386   }
3387 
3388   // ///////////////////////////////////////////////////////////////
3389   // delegated to ModelManager
3390   // ///////////////////////////////////////////////////////////////
3391 
autoCalculate(int tokProperty, String dataType)3392   public void autoCalculate(int tokProperty, String dataType) {
3393     switch (tokProperty) {
3394     case T.surfacedistance:
3395       ms.getSurfaceDistanceMax();
3396       break;
3397     case T.straightness:
3398       ms.calculateStraightnessAll();
3399       break;
3400     case T.dssr:
3401       ms.calculateDssrProperty(dataType);
3402     }
3403   }
3404 
3405   // This was just the sum of the atomic volumes, not considering overlap
3406   // It was never documented.
3407   // Removed in Jmol 13.0.RC4
3408 
3409   //  public float getVolume(BitSet bs, String type) {
3410   //    // Eval.calculate(), math function volume({atomExpression},"type")
3411   //    if (bs == null)
3412   //      bs = getSelectionSet();
3413   //    EnumVdw vType = EnumVdw.getVdwType(type);
3414   //    if (vType == null)
3415   //      vType = EnumVdw.AUTO;
3416   //    return modelSet.calculateVolume(bs, vType);
3417   //  }
3418 
calculateStraightness()3419   public void calculateStraightness() {
3420     ms.haveStraightness = false;
3421     ms.calculateStraightnessAll();
3422   }
3423 
calculateSurface(BS bsSelected, float envelopeRadius)3424   public P3[] calculateSurface(BS bsSelected, float envelopeRadius) {
3425     if (bsSelected == null)
3426       bsSelected = bsA();
3427     if (envelopeRadius == Float.MAX_VALUE || envelopeRadius == -1)
3428       ms.addStateScript(
3429           "calculate surfaceDistance "
3430               + (envelopeRadius == Float.MAX_VALUE ? "FROM" : "WITHIN"),
3431           null, bsSelected, null, "", false, true);
3432     return ms.calculateSurface(bsSelected, envelopeRadius);
3433   }
3434 
getStructureList()3435   public Map<STR, float[]> getStructureList() {
3436     return g.getStructureList();
3437   }
3438 
setStructureList(float[] list, STR type)3439   public void setStructureList(float[] list, STR type) {
3440     // none, turn, sheet, helix
3441     g.setStructureList(list, type);
3442     ms.setStructureList(getStructureList());
3443   }
3444 
3445   /**
3446    *
3447    * @param bsAtoms
3448    * @param asDSSP
3449    * @param setStructure
3450    *        to actually change structures
3451    * @param version
3452    * @return structure string from DSSP
3453    */
calculateStructures(BS bsAtoms, boolean asDSSP, boolean setStructure, int version)3454   public String calculateStructures(BS bsAtoms, boolean asDSSP,
3455                                     boolean setStructure, int version) {
3456     // Eval
3457     if (bsAtoms == null)
3458       bsAtoms = bsA();
3459     return ms.calculateStructures(bsAtoms, asDSSP, !am.animationOn,
3460         g.dsspCalcHydrogen, setStructure, version);
3461   }
3462 
3463   private JmolAnnotationParser annotationParser, dssrParser;
3464 
getAnnotationParser(boolean isDSSR)3465   public JmolAnnotationParser getAnnotationParser(boolean isDSSR) {
3466     return (isDSSR
3467         ? (dssrParser == null
3468             ? (dssrParser = (JmolAnnotationParser) Interface
3469                 .getOption("dssx.DSSR1", this, "script"))
3470             : dssrParser)
3471         : (annotationParser == null
3472             ? (annotationParser = (JmolAnnotationParser) Interface
3473                 .getOption("dssx.AnnotationParser", this, "script"))
3474             : annotationParser));
3475   }
3476 
3477   @Override
getSelectedAtomIterator(BS bsSelected, boolean isGreaterOnly, boolean modelZeroBased, boolean isMultiModel)3478   public AtomIndexIterator getSelectedAtomIterator(BS bsSelected,
3479                                                    boolean isGreaterOnly,
3480                                                    boolean modelZeroBased,
3481                                                    boolean isMultiModel) {
3482     return ms.getSelectedAtomIterator(bsSelected, isGreaterOnly, modelZeroBased,
3483         false, isMultiModel);
3484   }
3485 
3486   @Override
setIteratorForAtom(AtomIndexIterator iterator, int atomIndex, float distance)3487   public void setIteratorForAtom(AtomIndexIterator iterator, int atomIndex,
3488                                  float distance) {
3489     ms.setIteratorForAtom(iterator, -1, atomIndex, distance, null);
3490   }
3491 
3492   @Override
setIteratorForPoint(AtomIndexIterator iterator, int modelIndex, T3 pt, float distance)3493   public void setIteratorForPoint(AtomIndexIterator iterator, int modelIndex,
3494                                   T3 pt, float distance) {
3495     ms.setIteratorForPoint(iterator, modelIndex, pt, distance);
3496   }
3497 
3498   @Override
fillAtomData(AtomData atomData, int mode)3499   public void fillAtomData(AtomData atomData, int mode) {
3500     atomData.programInfo = "Jmol Version " + getJmolVersion();
3501     atomData.fileName = fm.getFileName();
3502     ms.fillAtomData(atomData, mode);
3503   }
3504 
addStateScript(String script, boolean addFrameNumber, boolean postDefinitions)3505   public StateScript addStateScript(String script, boolean addFrameNumber,
3506                                     boolean postDefinitions) {
3507     // calculate
3508     // configuration
3509     // plot
3510     // rebond
3511     // setPdbConectBonding
3512     return ms.addStateScript(script, null, null, null, null, addFrameNumber,
3513         postDefinitions);
3514   }
3515 
3516   private Minimizer minimizer;
3517   private SmilesMatcherInterface smilesMatcher;
3518 
getMinimizer(boolean createNew)3519   public Minimizer getMinimizer(boolean createNew) {
3520     return (minimizer == null && createNew
3521         ? (minimizer = (Minimizer) Interface
3522             .getInterface("org.jmol.minimize.Minimizer", this, "script"))
3523                 .setProperty("vwr", this)
3524         : minimizer);
3525   }
3526 
getSmilesMatcher()3527   public SmilesMatcherInterface getSmilesMatcher() {
3528     return (smilesMatcher == null
3529         ? (smilesMatcher = (SmilesMatcherInterface) Interface
3530             .getInterface("org.jmol.smiles.SmilesMatcher", this, "script"))
3531         : smilesMatcher);
3532   }
3533 
clearModelDependentObjects()3534   public void clearModelDependentObjects() {
3535     setFrameOffsets(null, false);
3536     stopMinimization();
3537     minimizer = null;
3538     smilesMatcher = null;
3539   }
3540 
zap(boolean notify, boolean resetUndo, boolean zapModelKit)3541   public void zap(boolean notify, boolean resetUndo, boolean zapModelKit) {
3542 
3543     clearThreads();
3544     if (mm.modelSet == null) {
3545       mm.zap();
3546 
3547     } else {
3548 
3549       //setBooleanProperty("appendNew", true);
3550       ligandModelSet = null;
3551       clearModelDependentObjects();
3552       fm.clear();
3553       clearRepaintManager(-1);
3554       am.clear();
3555       tm.clear();
3556       slm.clear();
3557       clearAllMeasurements();
3558       clearMinimization();
3559       gdata.clear();
3560       mm.zap();
3561       if (scm != null)
3562         scm.clear(false);
3563       if (nmrCalculation != null)
3564         getNMRCalculation().setChemicalShiftReference(null, 0);
3565 
3566       if (haveDisplay) {
3567         mouse.clear();
3568         clearTimeouts();
3569         acm.clear();
3570       }
3571       stm.clear(g);
3572       tempArray.clear();
3573       chainMap.clear();
3574       chainList.clear();
3575       chainCaseSpecified = false;
3576       //cm.clear();
3577       definedAtomSets.clear();
3578       lastData = null;
3579       if (dm != null)
3580         dm.clear();
3581       setBooleanProperty("legacyjavafloat", false);
3582       if (resetUndo) {
3583         if (zapModelKit)
3584           g.removeParam("_pngjFile");
3585         if (zapModelKit && g.modelKitMode) {
3586           loadDefaultModelKitModel(null);
3587         }
3588         undoClear();
3589       }
3590       System.gc();
3591     }
3592     initializeModel(false);
3593     if (notify) {
3594       setFileLoadStatus(FIL.ZAPPED, null,
3595           (resetUndo ? "resetUndo" : getZapName()), null, null, null);
3596     }
3597     if (Logger.debugging)
3598       Logger.checkMemory();
3599   }
3600 
loadDefaultModelKitModel(Map<String, Object> htParams)3601   private void loadDefaultModelKitModel(Map<String, Object> htParams) {
3602     openStringInlineParamsAppend(getModelkit(false).getDefaultModel(), htParams, true);
3603     setRotationRadius(5.0f, true);
3604     setStringProperty("picking", "assignAtom_C");
3605     setStringProperty("picking", "assignBond_p");
3606   }
3607 
zapMsg(String msg)3608   private void zapMsg(String msg) {
3609     zap(true, true, false);
3610     echoMessage(msg);
3611   }
3612 
echoMessage(String msg)3613   void echoMessage(String msg) {
3614     int iShape = JC.SHAPE_ECHO;
3615     shm.loadShape(iShape);
3616     setShapeProperty(iShape, "font", getFont3D("SansSerif", "Plain", 20));
3617     setShapeProperty(iShape, "target", "error");
3618     setShapeProperty(iShape, "text", msg);
3619   }
3620 
initializeModel(boolean isAppend)3621   private void initializeModel(boolean isAppend) {
3622     clearThreads();
3623     if (isAppend) {
3624       am.initializePointers(1);
3625       return;
3626     }
3627     reset(true);
3628     selectAll();
3629 
3630     if (modelkit != null)
3631       modelkit.initializeForModel();
3632     movingSelected = false;
3633     slm.noneSelected = Boolean.FALSE;
3634     setHoverEnabled(true);
3635 
3636     setSelectionHalosEnabled(false);
3637     tm.setCenter();
3638 
3639     am.initializePointers(1);
3640     setBooleanProperty("multipleBondBananas", false);
3641     if (!ms.getMSInfoB("isPyMOL")) {
3642       clearAtomSets();
3643       setCurrentModelIndex(0);
3644     }
3645     setBackgroundModelIndex(-1);
3646     setFrankOn(getShowFrank());
3647     startHoverWatcher(true);
3648 
3649     setTainted(true);
3650     finalizeTransformParameters();
3651   }
3652 
startHoverWatcher(boolean tf)3653   public void startHoverWatcher(boolean tf) {
3654     if (tf && inMotion || !haveDisplay
3655         || tf && (!hoverEnabled && !sm.haveHoverCallback() || am.animationOn))
3656       return;
3657     acm.startHoverWatcher(tf);
3658   }
3659 
3660   @Override
getModelSetPathName()3661   public String getModelSetPathName() {
3662     return mm.modelSetPathName;
3663   }
3664 
3665   @Override
getModelSetFileName()3666   public String getModelSetFileName() {
3667     return (mm.fileName == null ? getZapName() : mm.fileName);
3668   }
3669 
getUnitCellInfoText()3670   public String getUnitCellInfoText() {
3671     SymmetryInterface c = getCurrentUnitCell();
3672     return (c == null ? "not applicable" : c.getUnitCellInfo());
3673   }
3674 
getUnitCellInfo(int infoType)3675   public float getUnitCellInfo(int infoType) {
3676     SymmetryInterface symmetry = getCurrentUnitCell();
3677     return (symmetry == null ? Float.NaN
3678         : symmetry.getUnitCellInfoType(infoType));
3679   }
3680 
3681   /**
3682    *
3683    * convert string abc;offset or M3 or M4 to origin and three vectors -- a, b,
3684    * c. The string can be preceded by ! for "reverse of". For example,
3685    * "!a-b,-5a-5b,-c;7/8,0,1/8" offset is optional, but it still needs a
3686    * semicolon: "a/2,b/2,c;"
3687    * @param iModel
3688    *
3689    * @param def
3690    *        a string or an M3 or M4
3691    * @return vectors [origin a b c]
3692    */
getV0abc(int iModel, Object def)3693   public T3[] getV0abc(int iModel, Object def) {
3694     SymmetryInterface uc = (iModel < 0 ? getCurrentUnitCell() : getUnitCell(iModel));
3695     return (uc == null ? null : uc.getV0abc(def));
3696   }
3697 
getCurrentUnitCell()3698   public SymmetryInterface getCurrentUnitCell() {
3699     int iAtom = am.getUnitCellAtomIndex();
3700     if (iAtom >= 0)
3701       return ms.getUnitCellForAtom(iAtom);
3702     return getUnitCell(am.cmi);
3703   }
3704 
getUnitCell(int m)3705   private SymmetryInterface getUnitCell(int m) {
3706     if (m >= 0)
3707       return ms.getUnitCell(m);
3708     BS models = getVisibleFramesBitSet();
3709     SymmetryInterface ucLast = null;
3710     for (int i = models.nextSetBit(0); i >= 0; i = models.nextSetBit(i + 1)) {
3711       SymmetryInterface uc = ms.getUnitCell(i);
3712       if (uc == null)
3713         continue;
3714       if (ucLast == null) {
3715         ucLast = uc;
3716         continue;
3717       }
3718       if (!ucLast.unitCellEquals(uc))
3719         return null;
3720     }
3721     return ucLast;
3722   }
3723 
3724 
getPolymerPointsAndVectors(BS bs, Lst<P3[]> vList)3725   public void getPolymerPointsAndVectors(BS bs, Lst<P3[]> vList) {
3726     ms.getPolymerPointsAndVectors(bs, vList, g.traceAlpha, g.sheetSmoothing);
3727   }
3728 
getHybridizationAndAxes(int atomIndex, V3 z, V3 x, String lcaoType)3729   public String getHybridizationAndAxes(int atomIndex, V3 z, V3 x,
3730                                         String lcaoType) {
3731     return ms.getHybridizationAndAxes(atomIndex, 0, z, x, lcaoType, true, true,
3732         false);
3733   }
3734 
getAllAtoms()3735   public BS getAllAtoms() {
3736     return getModelUndeletedAtomsBitSet(-1);
3737   }
3738 
getFrameAtoms()3739   public BS getFrameAtoms() {
3740     return getModelUndeletedAtomsBitSetBs(getVisibleFramesBitSet());
3741   }
3742 
3743   @Override
getVisibleFramesBitSet()3744   public BS getVisibleFramesBitSet() {
3745     BS bs = BSUtil.copy(am.bsVisibleModels);
3746     if (ms.trajectory != null)
3747       ms.trajectory.selectDisplayed(bs);
3748     return bs;
3749 
3750   }
3751 
getModelUndeletedAtomsBitSet(int modelIndex)3752   public BS getModelUndeletedAtomsBitSet(int modelIndex) {
3753     return slm.excludeAtoms(
3754         ms.getModelAtomBitSetIncludingDeleted(modelIndex, true), false);
3755   }
3756 
getModelUndeletedAtomsBitSetBs(BS bsModels)3757   public BS getModelUndeletedAtomsBitSetBs(BS bsModels) {
3758     return slm.excludeAtoms(ms.getModelAtomBitSetIncludingDeletedBs(bsModels),
3759         false);
3760   }
3761 
3762   @Override
getBoundBoxCenter()3763   public P3 getBoundBoxCenter() {
3764     return ms.getBoundBoxCenter(am.cmi);
3765   }
3766 
calcBoundBoxDimensions(BS bs, float scale)3767   public void calcBoundBoxDimensions(BS bs, float scale) {
3768     ms.calcBoundBoxDimensions(bs, scale);
3769     axesAreTainted = true;
3770   }
3771 
3772   @Override
getBoundBoxCornerVector()3773   public V3 getBoundBoxCornerVector() {
3774     return ms.getBoundBoxCornerVector();
3775   }
3776 
3777   @Override
getModelSetProperties()3778   public Properties getModelSetProperties() {
3779     return ms.modelSetProperties;
3780   }
3781 
3782   @Override
getModelProperties(int modelIndex)3783   public Properties getModelProperties(int modelIndex) {
3784     return ms.am[modelIndex].properties;
3785   }
3786 
getModelForAtomIndex(int iatom)3787   public Model getModelForAtomIndex(int iatom) {
3788     return ms.am[ms.at[iatom].mi];
3789   }
3790 
3791   @Override
getModelSetAuxiliaryInfo()3792   public Map<String, Object> getModelSetAuxiliaryInfo() {
3793     return ms.getAuxiliaryInfo(null);
3794   }
3795 
3796   @Override
getModelNumber(int modelIndex)3797   public int getModelNumber(int modelIndex) {
3798     return (modelIndex < 0 ? modelIndex : ms.getModelNumber(modelIndex));
3799   }
3800 
getModelFileNumber(int modelIndex)3801   public int getModelFileNumber(int modelIndex) {
3802     return (modelIndex < 0 ? 0 : ms.modelFileNumbers[modelIndex]);
3803   }
3804 
3805   @Override
getModelNumberDotted(int modelIndex)3806   public String getModelNumberDotted(int modelIndex) {
3807     // must not return "all" for -1, because this could be within a frame RANGE
3808     return modelIndex < 0 ? "0" : ms.getModelNumberDotted(modelIndex);
3809   }
3810 
3811   @Override
getModelName(int modelIndex)3812   public String getModelName(int modelIndex) {
3813     return ms.getModelName(modelIndex);
3814   }
3815 
modelHasVibrationVectors(int modelIndex)3816   public boolean modelHasVibrationVectors(int modelIndex) {
3817     return (ms.getLastVibrationVector(modelIndex, T.vibration) >= 0);
3818   }
3819 
getBondsForSelectedAtoms(BS bsAtoms)3820   public BS getBondsForSelectedAtoms(BS bsAtoms) {
3821     // eval
3822     return ms.getBondsForSelectedAtoms(bsAtoms,
3823         g.bondModeOr || BSUtil.cardinalityOf(bsAtoms) == 1);
3824   }
3825 
frankClicked(int x, int y)3826   public boolean frankClicked(int x, int y) {
3827     // bottom right Jmol logo
3828     return !g.disablePopupMenu && getShowFrank() && shm.checkFrankclicked(x, y);
3829   }
3830 
frankClickedModelKit(int x, int y)3831   public boolean frankClickedModelKit(int x, int y) {
3832     // top left indicator
3833     return !g.disablePopupMenu && g.modelKitMode && x >= 0 && y >= 0 && x < 40
3834         && y < 26*4; // See FrankRenderer
3835   }
3836 
3837   @Override
findNearestAtomIndex(int x, int y)3838   public int findNearestAtomIndex(int x, int y) {
3839     return findNearestAtomIndexMovable(x, y, false);
3840   }
3841 
findNearestAtomIndexMovable(int x, int y, boolean mustBeMovable)3842   public int findNearestAtomIndexMovable(int x, int y, boolean mustBeMovable) {
3843     return (!g.atomPicking ? -1
3844         : ms.findNearestAtomIndex(x, y,
3845             mustBeMovable ? slm.getMotionFixedAtoms() : null,
3846             g.minPixelSelRadius));
3847   }
3848 
3849   /**
3850    * absolute or relative to origin of UNITCELL {x y z}
3851    *
3852    * @param pt
3853    * @param ignoreOffset
3854    *        TODO
3855    */
toCartesian(T3 pt, boolean ignoreOffset)3856   public void toCartesian(T3 pt, boolean ignoreOffset) {
3857     SymmetryInterface unitCell = getCurrentUnitCell();
3858     if (unitCell != null) {
3859       unitCell.toCartesian(pt, ignoreOffset);
3860       if (!g.legacyJavaFloat)
3861         PT.fixPtFloats(pt, PT.CARTESIAN_PRECISION);
3862     }
3863   }
3864 
3865   /**
3866    *
3867    * @param pt
3868    * @param ignoreOffset
3869    *        set true for relative to {0 0 0}; otherwise relative to origin of
3870    *        UNITCELL {x y z}
3871    */
toFractional(T3 pt, boolean ignoreOffset)3872   public void toFractional(T3 pt, boolean ignoreOffset) {
3873     SymmetryInterface unitCell = getCurrentUnitCell();
3874     if (unitCell != null) {
3875       unitCell.toFractional(pt, ignoreOffset);
3876       if (!g.legacyJavaFloat)
3877         PT.fixPtFloats(pt, PT.FRACTIONAL_PRECISION);
3878     }
3879 
3880   }
3881 
3882   /**
3883    * relative to origin without regard to UNITCELL {x y z}
3884    *
3885    * @param pt
3886    * @param offset
3887    */
toUnitCell(P3 pt, P3 offset)3888   public void toUnitCell(P3 pt, P3 offset) {
3889     SymmetryInterface unitCell = getCurrentUnitCell();
3890     if (unitCell != null)
3891       unitCell.toUnitCell(pt, offset);
3892   }
3893 
setCurrentCage(String isosurfaceId)3894   public void setCurrentCage(String isosurfaceId) {
3895     Object[] data = new Object[] { isosurfaceId, null };
3896     shm.getShapePropertyData(JC.SHAPE_ISOSURFACE, "unitCell", data);
3897     ms.setModelCage(am.cmi, (SymmetryInterface) data[1]);
3898   }
3899 
addUnitCellOffset(P3 pt)3900   public void addUnitCellOffset(P3 pt) {
3901     SymmetryInterface unitCell = getCurrentUnitCell();
3902     if (unitCell == null)
3903       return;
3904     pt.add(unitCell.getCartesianOffset());
3905   }
3906 
setAtomData(int type, String name, String coordinateData, boolean isDefault)3907   public void setAtomData(int type, String name, String coordinateData,
3908                           boolean isDefault) {
3909     // DATA "xxxx"
3910     // atom coordinates may be moved here
3911     //  but this is not included as an atomMovedCallback
3912     ms.setAtomData(type, name, coordinateData, isDefault);
3913     if (type == AtomCollection.TAINT_COORD)
3914       checkCoordinatesChanged();
3915     refreshMeasures(true);
3916   }
3917 
3918   @Override
setCenterSelected()3919   public void setCenterSelected() {
3920     // depricated
3921     setCenterBitSet(bsA(), true);
3922   }
3923 
setApplySymmetryToBonds(boolean TF)3924   void setApplySymmetryToBonds(boolean TF) {
3925     g.applySymmetryToBonds = TF;
3926   }
3927 
3928   @Override
setBondTolerance(float bondTolerance)3929   public void setBondTolerance(float bondTolerance) {
3930     g.setF("bondTolerance", bondTolerance);
3931     g.bondTolerance = bondTolerance;
3932   }
3933 
3934   @Override
setMinBondDistance(float minBondDistance)3935   public void setMinBondDistance(float minBondDistance) {
3936     // PreferencesDialog
3937     g.setF("minBondDistance", minBondDistance);
3938     g.minBondDistance = minBondDistance;
3939   }
3940 
getAtomsNearPt(float distance, P3 coord)3941   public BS getAtomsNearPt(float distance, P3 coord) {
3942     BS bs = new BS();
3943     ms.getAtomsWithin(distance, coord, bs, -1);
3944     return bs;
3945   }
3946 
3947   /**
3948    * given a set of atoms, a subset of atoms to test, two atoms that start the
3949    * branch, and whether or not to allow the branch to cycle back on
3950    * itself,deliver the set of atoms constituting this branch.
3951    *
3952    * @param atomIndex
3953    * @param atomIndexNot
3954    * @param allowCyclic
3955    * @return bitset for this branch
3956    */
getBranchBitSet(int atomIndex, int atomIndexNot, boolean allowCyclic)3957   public BS getBranchBitSet(int atomIndex, int atomIndexNot,
3958                             boolean allowCyclic) {
3959     if (atomIndex < 0 || atomIndex >= ms.ac)
3960       return new BS();
3961     return JmolMolecule.getBranchBitSet(ms.at, atomIndex,
3962         getModelUndeletedAtomsBitSet(ms.at[atomIndex].mi), null, atomIndexNot,
3963         allowCyclic, true);
3964   }
3965 
3966   @Override
getElementsPresentBitSet(int modelIndex)3967   public BS getElementsPresentBitSet(int modelIndex) {
3968     return ms.getElementsPresentBitSet(modelIndex);
3969   }
3970 
getFileHeader()3971   String getFileHeader() {
3972     return ms.getFileHeader(am.cmi);
3973   }
3974 
getFileData()3975   Object getFileData() {
3976     return ms.getFileData(am.cmi);
3977   }
3978 
getCifData(int modelIndex)3979   public Map<String, Object> getCifData(int modelIndex) {
3980     return readCifData(ms.getModelFileName(modelIndex),
3981         ms.getModelFileType(modelIndex).toUpperCase());
3982   }
3983 
readCifData(String fileName, String type)3984   public Map<String, Object> readCifData(String fileName, String type) {
3985     String fname = (fileName == null ? ms.getModelFileName(am.cmi) : fileName);
3986     if (type == null && fname != null
3987         && fname.toUpperCase().indexOf("BCIF") >= 0) {
3988       BufferedInputStream is = fm.getBufferedInputStream(fname);
3989       try {
3990         return ((javajs.util.MessagePackReader) Interface
3991             .getInterface("javajs.util.MessagePackReader", this, "script"))
3992                 .getMapForStream(is);
3993       } catch (Exception e) {
3994         e.printStackTrace();
3995         return new Hashtable<String, Object>();
3996       }
3997     }
3998     String data = (fileName == null || fileName.length() == 0
3999         ? getCurrentFileAsString("script")
4000         : getFileAsString3(fileName, false, null));
4001     if (data == null || data.length() < 2)
4002       return null;
4003     BufferedReader rdr = Rdr.getBR(data);
4004     if (type == null)
4005       type = getModelAdapter().getFileTypeName(rdr);
4006     return (type == null ? null : readCifData(null, rdr, type));
4007 
4008   }
4009 
4010   @Override
readCifData(String fileName, Object rdrOrStringData, String type)4011   public Map<String, Object> readCifData(String fileName,
4012                                          Object rdrOrStringData, String type) {
4013     if (rdrOrStringData == null)
4014       rdrOrStringData = getFileAsString(fileName);
4015     BufferedReader rdr = (rdrOrStringData instanceof BufferedReader
4016         ? (BufferedReader) rdrOrStringData
4017         : Rdr.getBR((String) rdrOrStringData));
4018     return Rdr.readCifData((GenericCifDataParser) Interface.getInterface(
4019         ("Cif2".equals(type) ? "org.jmol.adapter.readers.cif.Cif2DataParser"
4020             : "javajs.util.CifDataParser"),
4021         this, "script"), rdr);
4022   }
4023 
4024   JmolStateCreator jsc;
4025 
getStateCreator()4026   public JmolStateCreator getStateCreator() {
4027     if (jsc == null)
4028       (jsc = (JmolStateCreator) Interface
4029           .getInterface("org.jmol.viewer.StateCreator", this, "script"))
4030               .setViewer(this);
4031     return jsc;
4032   }
4033 
getWrappedStateScript()4034   public String getWrappedStateScript() {
4035     return (String) getOutputManager().getWrappedState(null, null, null, null);
4036   }
4037 
4038   @Override
getStateInfo()4039   public String getStateInfo() {
4040     return getStateInfo3(null, 0, 0);
4041   }
4042 
getStateInfo3(String type, int width, int height)4043   public String getStateInfo3(String type, int width, int height) {
4044     return (g.preserveState
4045         ? getStateCreator().getStateScript(type, width, height)
4046         : "");
4047   }
4048 
getStructureState()4049   public String getStructureState() {
4050     return getStateCreator().getModelState(null, false, true);
4051   }
4052 
getCoordinateState(BS bsSelected)4053   public String getCoordinateState(BS bsSelected) {
4054     return getStateCreator().getAtomicPropertyState(AtomCollection.TAINT_COORD,
4055         bsSelected);
4056   }
4057 
setCurrentColorRange(String label)4058   public void setCurrentColorRange(String label) {
4059     float[] data = (float[]) getDataObj(label, null,
4060         JmolDataManager.DATA_TYPE_AF);
4061     BS bs = (data == null ? null
4062         : (BS) ((Object[]) getDataObj(label, null,
4063             JmolDataManager.DATA_TYPE_UNKNOWN))[2]);
4064     if (bs != null && g.rangeSelected)
4065       bs.and(bsA());
4066     cm.setPropertyColorRangeData(data, bs);
4067   }
4068 
4069   private Object[] lastData;
4070 
4071   /**
4072    * A general-purpose data storage method. Note that matchFieldCount and
4073    * dataFieldCount should both be positive or both be negative.
4074    *
4075    * @param key
4076    *
4077    *        a simple key name for the data, starting with "property_" if
4078    *        user-defined
4079    *
4080    * @param data
4081    *
4082    *        data[0] -- label
4083    *
4084    *        data[1] -- string or float[] or float[][] or float[][][]
4085    *
4086    *        data[2] -- selection bitset or int[] atomMap when field > 0
4087    *
4088    *        data[3] -- arrayDepth
4089    *        0(String),1(float[]),2(float[][]),3(float[][][]) or -1 to indidate
4090    *        that it is set by data type
4091    *
4092    *        data[4] -- Boolean.TRUE == saveInState
4093    *
4094    * @param dataType
4095    *
4096    *        see JmolDataManager interface
4097    *
4098    * @param matchField
4099    *
4100    *        if positive, data must match atomNo in this column
4101    *
4102    *        if 0, no match column
4103    *
4104    * @param matchFieldColumnCount
4105    *        if positive, this number of characters in match column if 0,
4106    *        reference is to tokens, not characters
4107    *
4108    * @param dataField
4109    *
4110    *        if positive, column containing the data
4111    *
4112    *        if 0, values are a simple list; clear the data
4113    *
4114    *        if Integer.MAX_VALUE, values are a simple list; don't clear the data
4115    *
4116    *        if Integer.MIN_VALUE, have one SINGLE data value for all selected
4117    *        atoms
4118    *
4119    * @param dataFieldColumnCount
4120    *
4121    *        if positive, this number of characters in data column
4122    *
4123    *        if 0, reference is to tokens, not characters
4124    */
setData(String key, Object[] data, int dataType, int matchField, int matchFieldColumnCount, int dataField, int dataFieldColumnCount)4125   public void setData(String key, Object[] data, int dataType, int matchField,
4126                       int matchFieldColumnCount, int dataField,
4127                       int dataFieldColumnCount) {
4128     getDataManager().setData(key, lastData = data, dataType, ms.ac, matchField,
4129         matchFieldColumnCount, dataField, dataFieldColumnCount);
4130   }
4131 
4132   /**
4133    * Retrieve a data object
4134    *
4135    * @param key
4136    *
4137    * @param bsSelected
4138    *
4139    *        selected atoms; for DATA_AF only
4140    *
4141    * @param dataType
4142    *
4143    *        see JmolDataManager interface
4144    *
4145    * @return data object
4146    *
4147    *         data[0] -- label (same as key)
4148    *
4149    *         data[1] -- string or float[] or float[][] or float[][][]
4150    *
4151    *         data[2] -- selection bitset or int[] atomMap when field > 0
4152    *
4153    *         data[3] -- arrayDepth
4154    *         0(String),1(float[]),2(float[][]),3(float[][][]) or -1 to indicate
4155    *         that it is set by data type
4156    *
4157    *         data[4] -- Boolean.TRUE == saveInState
4158    */
getDataObj(String key, BS bsSelected, int dataType)4159   public Object getDataObj(String key, BS bsSelected, int dataType) {
4160     return (key == null && dataType == JmolDataManager.DATA_TYPE_LAST ? lastData
4161         : getDataManager().getData(key, bsSelected, dataType));
4162   }
4163 
4164   //  public float getDataFloatAt(String label, int atomIndex) {
4165   //    return getDataManager().getDataFloatAt(label, atomIndex);
4166   //  }
4167 
4168   // boolean autoLoadOrientation() {
4169   // return true;//global.autoLoadOrientation; 12.0.RC10
4170   // }
4171 
autoHbond(BS bsFrom, BS bsTo, boolean onlyIfHaveCalculated)4172   public int autoHbond(BS bsFrom, BS bsTo, boolean onlyIfHaveCalculated) {
4173     if (bsFrom == null)
4174       bsFrom = bsTo = bsA();
4175     // bsTo null --> use DSSP method further developed
4176     // here to give the "defining" Hbond set only
4177     return ms.autoHbond(bsFrom, bsTo, onlyIfHaveCalculated);
4178   }
4179 
4180   /*
4181    * ****************************************************************************
4182    * delegated to MeasurementManager
4183    * **************************************************************************
4184    */
4185 
getDefaultMeasurementLabel(int nPoints)4186   public String getDefaultMeasurementLabel(int nPoints) {
4187     switch (nPoints) {
4188     case 2:
4189       return g.defaultDistanceLabel;
4190     case 3:
4191       return g.defaultAngleLabel;
4192     default:
4193       return g.defaultTorsionLabel;
4194     }
4195   }
4196 
4197   @Override
getMeasurementCount()4198   public int getMeasurementCount() {
4199     int count = getShapePropertyAsInt(JC.SHAPE_MEASURES, "count");
4200     return count <= 0 ? 0 : count;
4201   }
4202 
4203   @Override
getMeasurementStringValue(int i)4204   public String getMeasurementStringValue(int i) {
4205     return "" + shm.getShapePropertyIndex(JC.SHAPE_MEASURES, "stringValue", i);
4206   }
4207 
getMeasurementInfoAsString()4208   public String getMeasurementInfoAsString() {
4209     return (String) getShapeProperty(JC.SHAPE_MEASURES, "infostring");
4210   }
4211 
4212   @Override
getMeasurementCountPlusIndices(int i)4213   public int[] getMeasurementCountPlusIndices(int i) {
4214     return (int[]) shm.getShapePropertyIndex(JC.SHAPE_MEASURES,
4215         "countPlusIndices", i);
4216   }
4217 
setPendingMeasurement(MeasurementPending mp)4218   void setPendingMeasurement(MeasurementPending mp) {
4219     // from MouseManager
4220     shm.loadShape(JC.SHAPE_MEASURES);
4221     setShapeProperty(JC.SHAPE_MEASURES, "pending", mp);
4222   }
4223 
getPendingMeasurement()4224   public MeasurementPending getPendingMeasurement() {
4225     return (MeasurementPending) getShapeProperty(JC.SHAPE_MEASURES, "pending");
4226   }
4227 
clearAllMeasurements()4228   public void clearAllMeasurements() {
4229     // Eval only
4230     setShapeProperty(JC.SHAPE_MEASURES, "clear", null);
4231   }
4232 
4233   @Override
clearMeasurements()4234   public void clearMeasurements() {
4235     // depricated but in the API -- use "script" directly
4236     // see clearAllMeasurements()
4237     evalString("measures delete");
4238   }
4239 
4240   // ///////////////////////////////////////////////////////////////
4241   // delegated to AnimationManager
4242   // ///////////////////////////////////////////////////////////////
4243 
setAnimation(int tok)4244   public void setAnimation(int tok) {
4245     switch (tok) {
4246     case T.playrev:
4247       am.reverseAnimation();
4248       //$FALL-THROUGH$
4249     case T.play:
4250     case T.resume:
4251       if (!am.animationOn)
4252         am.resumeAnimation();
4253       return;
4254     case T.pause:
4255       if (am.animationOn && !am.animationPaused)
4256         am.pauseAnimation();
4257       return;
4258     case T.next:
4259       am.setAnimationNext();
4260       return;
4261     case T.prev:
4262       am.setAnimationPrevious();
4263       return;
4264     case T.first:
4265     case T.rewind:
4266       am.rewindAnimation();
4267       return;
4268     case T.last:
4269       am.setAnimationLast();
4270       return;
4271     }
4272   }
4273 
4274   @Override
setAnimationFps(int fps)4275   public void setAnimationFps(int fps) {
4276     am.setAnimationFps(fps);
4277   }
4278 
setAnimationMode(String mode)4279   private void setAnimationMode(String mode) {
4280     if (mode.equalsIgnoreCase("once")) {
4281       am.setAnimationReplayMode(T.once, 0, 0);
4282     } else if (mode.equalsIgnoreCase("loop")) {
4283       am.setAnimationReplayMode(T.loop, 1, 1);
4284     } else if (mode.startsWith("pal")) {
4285       am.setAnimationReplayMode(T.palindrome, 1, 1);
4286     }
4287   }
4288 
setAnimationOn(boolean animationOn)4289   public void setAnimationOn(boolean animationOn) {
4290     // Eval
4291     boolean wasAnimating = am.animationOn;
4292     if (animationOn == wasAnimating)
4293       return;
4294     am.setAnimationOn(animationOn);
4295   }
4296 
setAnimationRange(int modelIndex1, int modelIndex2)4297   public void setAnimationRange(int modelIndex1, int modelIndex2) {
4298     am.setAnimationRange(modelIndex1, modelIndex2);
4299   }
4300 
defineAtomSets(Map<String, Object> info)4301   public void defineAtomSets(Map<String, Object> info) {
4302     definedAtomSets.putAll(info);
4303   }
4304 
setAnimDisplay(BS bs)4305   public void setAnimDisplay(BS bs) {
4306     am.setDisplay(bs);
4307     if (!am.animationOn)
4308       am.morph(am.currentMorphModel + 1);
4309   }
4310 
setCurrentModelIndex(int modelIndex)4311   public void setCurrentModelIndex(int modelIndex) {
4312     // Eval
4313     // initializeModel
4314     if (modelIndex == Integer.MIN_VALUE) {
4315       // just forcing popup menu update
4316       prevFrame = Integer.MIN_VALUE;
4317       setCurrentModelIndexClear(am.cmi, true);
4318       return;
4319     }
4320     am.setModel(modelIndex, true);
4321   }
4322 
getTrajectoryState()4323   public String getTrajectoryState() {
4324     return (ms.trajectory == null ? "" : ms.trajectory.getState());
4325   }
4326 
setFrameOffsets(BS bsAtoms, boolean isFull)4327   public void setFrameOffsets(BS bsAtoms, boolean isFull) {
4328     tm.bsFrameOffsets = null;
4329     if (isFull)
4330       clearModelDependentObjects();
4331     else
4332       tm.bsFrameOffsets = bsAtoms;
4333     tm.frameOffsets = ms.getFrameOffsets(bsAtoms, isFull);
4334   }
4335 
setCurrentModelIndexClear(int modelIndex, boolean clearBackground)4336   public void setCurrentModelIndexClear(int modelIndex,
4337                                         boolean clearBackground) {
4338     // Eval
4339     // initializeModel
4340     am.setModel(modelIndex, clearBackground);
4341   }
4342 
haveFileSet()4343   public boolean haveFileSet() {
4344     return (ms.mc > 1 && getModelNumber(Integer.MAX_VALUE) > 2000000);
4345   }
4346 
setBackgroundModelIndex(int modelIndex)4347   public void setBackgroundModelIndex(int modelIndex) {
4348     am.setBackgroundModelIndex(modelIndex);
4349     g.setO("backgroundModel", ms.getModelNumberDotted(modelIndex));
4350   }
4351 
setFrameVariables()4352   void setFrameVariables() {
4353     g.setO("animationMode", T.nameOf(am.animationReplayMode));
4354     g.setI("animationFps", am.animationFps);
4355     g.setO("_firstFrame", am.getModelSpecial(-1));
4356     g.setO("_lastFrame", am.getModelSpecial(1));
4357     g.setF("_animTimeSec", am.getAnimRunTimeSeconds());
4358     g.setB("_animMovie", am.isMovie);
4359   }
4360 
4361   private int motionEventNumber;
4362   private boolean inMotion;
4363 
getInMotion(boolean includeAnim)4364   public boolean getInMotion(boolean includeAnim) {
4365     return (inMotion || includeAnim && am.animationOn);
4366   }
4367 
4368   @Override
getMotionEventNumber()4369   public int getMotionEventNumber() {
4370     return motionEventNumber;
4371   }
4372 
4373   @Override
setInMotion(boolean inMotion)4374   public void setInMotion(boolean inMotion) {
4375     if (this.inMotion ^ inMotion) {
4376       this.inMotion = inMotion;
4377       resizeImage(0, 0, false, false, true); // for antialiasdisplay
4378       if (inMotion) {
4379         startHoverWatcher(false);
4380         ++motionEventNumber;
4381       } else {
4382         startHoverWatcher(true);
4383         refresh(REFRESH_SYNC_MASK, "vwr setInMotion " + inMotion);
4384       }
4385     }
4386   }
4387 
4388   private boolean refreshing = true;
4389 
setRefreshing(boolean TF)4390   private void setRefreshing(boolean TF) {
4391     refreshing = TF;
4392   }
4393 
getRefreshing()4394   public boolean getRefreshing() {
4395     return refreshing;
4396   }
4397 
4398   @Override
pushHoldRepaint()4399   public void pushHoldRepaint() {
4400     pushHoldRepaintWhy(null);
4401   }
4402 
4403   /**
4404    *
4405    * @param why
4406    */
pushHoldRepaintWhy(String why)4407   public void pushHoldRepaintWhy(String why) {
4408     if (rm != null)
4409       rm.pushHoldRepaint(why);
4410   }
4411 
4412   @Override
popHoldRepaint(String why)4413   public void popHoldRepaint(String why) {
4414     if (rm != null) {
4415       rm.popHoldRepaint(why.indexOf(JC.REPAINT_IGNORE) < 0, why);
4416     }
4417   }
4418 
4419   public final static int REFRESH_REPAINT = 1;
4420   public final static int REFRESH_SYNC = 2;
4421   public final static int REFRESH_SYNC_MASK = REFRESH_REPAINT | REFRESH_SYNC;
4422   public final static int REFRESH_REPAINT_NO_MOTION_ONLY = 6;
4423   public final static int REFRESH_SEND_WEBGL_NEW_ORIENTATION = 7;
4424 
4425   /**
4426    * initiate a repaint/update sequence if it has not already been requested.
4427    * invoked whenever any operation causes changes that require new rendering.
4428    *
4429    * The repaint/update sequence will only be invoked if (a) no repaint is
4430    * already pending and (b) there is no hold flag set in repaintManager.
4431    *
4432    * Sequence is as follows:
4433    *
4434    * 1) RepaintManager.refresh() checks flags and then calls Viewer.repaint()
4435    *
4436    * 2) Viewer.repaint() invokes display.repaint(), provided display is not null
4437    * (headless)
4438    *
4439    * 3) The system responds with an invocation of Jmol.update(Graphics g), which
4440    * we are routing through Jmol.paint(Graphics g).
4441    *
4442    * 4) Jmol.update invokes Viewer.setScreenDimensions(size), which makes the
4443    * necessary changes in parameters for any new window size.
4444    *
4445    * 5) Jmol.update invokes Viewer.renderScreenImage(g, size, rectClip)
4446    *
4447    * 6) Viewer.renderScreenImage checks object visibility, invokes render1 to do
4448    * the actual creation of the image pixel map and send it to the screen, and
4449    * then invokes repaintView()
4450    *
4451    * 7) Viewer.repaintView() invokes RepaintManager.repaintDone(), to clear the
4452    * flags and then use notify() to release any threads holding on wait().
4453    *
4454    * @param mode
4455    *
4456    *        REFRESH_REPAINT: ONLY do a repaint -- no syncing
4457    *
4458    *        REFRESH_SYNC: mouse motion requiring synchronization -- not going
4459    *        through Eval so we bypass Eval and mainline on the other vwr! Also
4460    *        called from j2sApplet.js
4461    *
4462    *        REFRESH_REPAINT_SYNC_MASK: same as REFRESH_REPAINT, but not WebGL
4463    *
4464    *        REFRESH_NO_MOTION_ONLY: refresh only if not in motion
4465    *
4466    *        REFRESH_SEND_WEBGL_NEW_ORIENTATION: send WebGL a "new orientation"
4467    *        command at the end of a script using html5applet._refresh()
4468    *
4469    *
4470    * @param strWhy
4471    *        debugging or for passing mouse command when using REFRESH_SYNC
4472    *
4473    */
4474   @Override
4475   public void refresh(int mode, String strWhy) {
4476 
4477     if (rm == null || !refreshing
4478         || mode == REFRESH_REPAINT_NO_MOTION_ONLY && getInMotion(true)
4479         || !isWebGL && mode == REFRESH_SEND_WEBGL_NEW_ORIENTATION)
4480       return;
4481     if (isWebGL) {
4482       switch (mode) {
4483       case REFRESH_REPAINT:
4484       case REFRESH_SYNC:
4485       case REFRESH_SEND_WEBGL_NEW_ORIENTATION:
4486         tm.finalizeTransformParameters();
4487         if (html5Applet == null)
4488           return;
4489         html5Applet._refresh();
4490         if (mode == REFRESH_SEND_WEBGL_NEW_ORIENTATION)
4491           return;
4492         break;
4493       }
4494     } else {
4495       rm.repaintIfReady("refresh " + mode + " " + strWhy);
4496     }
4497     // Q: Why this, since all of these % 3 != 0
4498     if (/*mode % REFRESH_SYNC_MASK != 0 && */ sm.doSync())
4499       sm.setSync(mode == REFRESH_SYNC ? strWhy : null);
4500   }
4501 
4502   public void requestRepaintAndWait(String why) {
4503     // called by moveUpdate from move, moveTo, navigate,
4504     // navTranslate
4505     // called by ScriptEvaluator "refresh" command
4506     // called by AnimationThread run()
4507     // called by TransformationManager move and moveTo
4508     // called by TransformationManager11 navigate, navigateTo
4509     if (rm == null)
4510       return;
4511     if (!haveDisplay) {
4512       setModelVisibility();
4513       shm.finalizeAtoms(null, true);
4514       return;
4515     }
4516     rm.requestRepaintAndWait(why);
4517     setSync();
4518   }
4519 
4520   public void clearShapeRenderers() {
4521     clearRepaintManager(-1);
4522   }
4523 
4524   public boolean isRepaintPending() {
4525     return (rm == null ? false : rm.isRepaintPending());
4526   }
4527 
4528   @Override
4529   public void notifyViewerRepaintDone() {
4530     if (rm != null)
4531       rm.repaintDone();
4532     am.repaintDone();
4533   }
4534 
4535   private boolean axesAreTainted = false;
4536 
4537   public boolean areAxesTainted() {
4538     boolean TF = axesAreTainted;
4539     axesAreTainted = false;
4540     return TF;
4541   }
4542 
4543   @Override
4544   public String generateOutputForExport(Map<String, Object> params) {
4545     return (noGraphicsAllowed || rm == null ? null
4546         : getOutputManager().getOutputFromExport(params));
4547   }
4548 
4549   private void clearRepaintManager(int iShape) {
4550     if (rm != null)
4551       rm.clear(iShape);
4552   }
4553 
4554   /**
4555    * JmolViewer interface uses this, but that is all
4556    */
4557   @Override
4558   public void renderScreenImage(Object g, int width, int height) {
4559     renderScreenImageStereo(g, false, width, height);
4560   }
4561 
4562   public void renderScreenImageStereo(Object gLeft, boolean checkStereoSlave,
4563                                       int width, int height) {
4564     // from paint/update event
4565     // gRight is for second stereo applet
4566     // when this is the stereoSlave, no rendering occurs through this applet
4567     // directly, only from the other applet.
4568     // this is for relatively specialized geoWall-type installations
4569 
4570     //      Jmol repaint/update system for the application:
4571     //
4572     //      threads invoke vwr.refresh()
4573     //
4574     //        --> repaintManager.refresh()
4575     //        --> vwr.repaint()
4576     //        --> display.repaint()
4577     //        --> OS event queue calls Applet.paint()
4578     //        --> vwr.renderScreenImage()
4579     //        --> vwr.notifyViewerRepaintDone()
4580     //        --> repaintManager.repaintDone()
4581     //        --> which sets repaintPending false and does notify();
4582 
4583     if (updateWindow(width, height)) {
4584       if (!checkStereoSlave || gRight == null) {
4585         getScreenImageBuffer(gLeft, false);
4586       } else {
4587         drawImage(gRight, getImage(true, false), 0, 0, tm.stereoDoubleDTI);
4588         drawImage(gLeft, getImage(false, false), 0, 0, tm.stereoDoubleDTI);
4589       }
4590     }
4591     if (captureParams != null
4592         && Boolean.FALSE != captureParams.get("captureEnabled")) {
4593       captureParams.remove("imagePixels");
4594       //showString(transformManager.matrixRotate.toString(), false);
4595       long t = ((Long) captureParams.get("endTime")).longValue();
4596       if (t > 0 && System.currentTimeMillis() + 50 > t)
4597         captureParams.put("captureMode", "end");
4598       processWriteOrCapture(captureParams);
4599     }
4600     notifyViewerRepaintDone();
4601   }
4602 
4603   public Map<String, Object> captureParams;
4604   private Map<String, Object> jsParams;
4605 
4606   /**
4607    * for JavaScript only
4608    *
4609    */
updateJS()4610   public void updateJS() {
4611     if (isWebGL) {
4612       if (jsParams == null) {
4613         jsParams = new Hashtable<String, Object>();
4614         jsParams.put("type", "JS");
4615       }
4616       if (updateWindow(0, 0))
4617         render();
4618       notifyViewerRepaintDone();
4619     } else {
4620       if (isStereoSlave)
4621         return;
4622       // getGraphics returns a canvas context2d
4623       renderScreenImageStereo(apiPlatform.getGraphics(null), true, 0, 0);
4624     }
4625   }
4626 
4627   /**
4628    * File has been loaded or model has been changed or atom picked. This is a
4629    * call to Jmol.View for view sets (new in Jmol 14.1.8)
4630    *
4631    * @param imodel
4632    * @param iatom
4633    *
4634    */
updateJSView(int imodel, int iatom)4635   private void updateJSView(int imodel, int iatom) {
4636     if (html5Applet == null)
4637       return;
4638     @SuppressWarnings("unused")
4639     JSmolAppletObject applet = this.html5Applet;
4640     boolean doViewPick = true;
4641     /**
4642      * @j2sNative
4643      *
4644      *            doViewPick = (applet != null && applet._viewSet != null);
4645      *
4646      */
4647     {
4648     }
4649     if (doViewPick)
4650       html5Applet._atomPickedCallback(imodel, iatom);
4651   }
4652 
4653   // ///////////////////////////////////////////////////////////////
4654   // routines for script support
4655   // ///////////////////////////////////////////////////////////////
4656 
4657   @Override
evalFile(String strFilename)4658   public String evalFile(String strFilename) {
4659     // from JmolApp and test suite only
4660     return (allowScripting && getScriptManager() != null
4661         ? scm.evalFile(strFilename)
4662         : null);
4663   }
4664 
getInsertedCommand()4665   public String getInsertedCommand() {
4666     String s = insertedCommand;
4667     insertedCommand = "";
4668     if (Logger.debugging && s != "")
4669       Logger.debug("inserting: " + s);
4670     return s;
4671   }
4672 
4673   @Override
script(String strScript)4674   public String script(String strScript) {
4675     // JmolViewer -- just an alias for evalString
4676     return evalStringQuietSync(strScript, false, true);
4677   }
4678 
4679   @Override
evalString(String strScript)4680   public String evalString(String strScript) {
4681     // JmolSimpleViewer
4682     return evalStringQuietSync(strScript, false, true);
4683   }
4684 
4685   @Override
evalStringQuiet(String strScript)4686   public String evalStringQuiet(String strScript) {
4687     // JmolViewer
4688     return evalStringQuietSync(strScript, true, true);
4689   }
4690 
evalStringQuietSync(String strScript, boolean isQuiet, boolean allowSyncScript)4691   public String evalStringQuietSync(String strScript, boolean isQuiet,
4692                                     boolean allowSyncScript) {
4693     return (getScriptManager() == null ? null
4694         : scm.evalStringQuietSync(strScript, isQuiet, allowSyncScript));
4695   }
4696 
clearScriptQueue()4697   public void clearScriptQueue() {
4698     if (scm != null)
4699       scm.clearQueue();
4700   }
4701 
setScriptQueue(boolean TF)4702   private void setScriptQueue(boolean TF) {
4703     g.useScriptQueue = TF;
4704     if (!TF)
4705       clearScriptQueue();
4706   }
4707 
4708   @Override
checkHalt(String str, boolean isInsert)4709   public boolean checkHalt(String str, boolean isInsert) {
4710     return (scm != null && scm.checkHalt(str, isInsert));
4711   }
4712 
4713   // / direct no-queue use:
4714 
4715   @Override
scriptWait(String strScript)4716   public String scriptWait(String strScript) {
4717     return (String) evalWait("JSON", strScript,
4718         "+scriptStarted,+scriptStatus,+scriptEcho,+scriptTerminated");
4719   }
4720 
4721   @Override
scriptWaitStatus(String strScript, String statusList)4722   public Object scriptWaitStatus(String strScript, String statusList) {
4723     // null statusList will return a String
4724     //  -- output from PRINT/MESSAGE/ECHO commands or an error message
4725     // otherwise, specific status messages will be created as a Java object
4726     return evalWait("object", strScript, statusList);
4727   }
4728 
evalWait(String returnType, String strScript, String statusList)4729   private Object evalWait(String returnType, String strScript,
4730                           String statusList) {
4731     //can't do waitForQueue in JavaScript and then wait for the queue:
4732     if (getScriptManager() == null)
4733       return null;
4734     scm.waitForQueue();
4735     boolean doTranslateTemp = GT.setDoTranslate(false);
4736     Object ret = evalStringWaitStatusQueued(returnType, strScript, statusList,
4737         false, false);
4738     GT.setDoTranslate(doTranslateTemp);
4739     return ret;
4740   }
4741 
exitJmol()4742   public void exitJmol() {
4743     if (isApplet && !isJNLP)
4744       return;
4745     if (headlessImageParams != null) {
4746       try {
4747         if (headless)
4748           outputToFile(headlessImageParams);
4749       } catch (Exception e) {
4750         //
4751       }
4752     }
4753 
4754     if (Logger.debugging)
4755       Logger.debug("exitJmol -- exiting");
4756     System.out.flush();
4757     System.exit(0);
4758   }
4759 
scriptCheckRet(String strScript, boolean returnContext)4760   private Object scriptCheckRet(String strScript, boolean returnContext) {
4761     return (getScriptManager() == null ? null
4762         : scm.scriptCheckRet(strScript, returnContext));
4763   }
4764 
4765   @Override
scriptCheck(String strScript)4766   public synchronized Object scriptCheck(String strScript) {
4767     return scriptCheckRet(strScript, false);
4768   }
4769 
4770   @Override
isScriptExecuting()4771   public boolean isScriptExecuting() {
4772     return (eval != null && eval.isExecuting());
4773   }
4774 
4775   @Override
haltScriptExecution()4776   public void haltScriptExecution() {
4777     if (eval != null) {
4778       eval.haltExecution();
4779       eval.stopScriptThreads();
4780     }
4781     setStringPropertyTok("pathForAllFiles", T.pathforallfiles, "");
4782     clearTimeouts();
4783   }
4784 
pauseScriptExecution()4785   public void pauseScriptExecution() {
4786     if (eval != null)
4787       eval.pauseExecution(true);
4788   }
4789 
resolveDatabaseFormat(String fileName)4790   String resolveDatabaseFormat(String fileName) {
4791     return (hasDatabasePrefix(fileName)
4792         || fileName.indexOf(JC.legacyResolver) >= 0
4793             ? (String) setLoadFormat(fileName, fileName.charAt(0), false)
4794             : fileName);
4795   }
4796 
hasDatabasePrefix(String fileName)4797   public static boolean hasDatabasePrefix(String fileName) {
4798     return (fileName.length() != 0 && isDatabaseCode(fileName.charAt(0)));
4799   }
4800 
isDatabaseCode(char ch)4801   public static boolean isDatabaseCode(char ch) {
4802     return (ch == '*' // PDBE
4803         || ch == '$' // NCI resolver
4804         || ch == '=' // RCSB model or ligand
4805         || ch == ':' // PubChem
4806     );
4807   }
4808 
4809   /**
4810    * Jmol will either specify a type or look for it in the first character,
4811    * making sure it is found using isDatabaseCode() first. Starting with Jmol
4812    * 13.1.13, we allow a generalized search using =xxx= where xxx is a known or
4813    * user-specified database designation.
4814    *
4815    * @param name
4816    * @param type
4817    *        a character to distinguish the type of file, '?' means we are just
4818    *        doing an isosurface check
4819    * @param withPrefix
4820    * @return String or String[]
4821    */
setLoadFormat(String name, char type, boolean withPrefix)4822   public Object setLoadFormat(String name, char type, boolean withPrefix) {
4823     String format = null;
4824     String id = name.substring(1);
4825     switch (type) {
4826     case 'c': // cache:local//... legacyResolver....
4827       return name;
4828     case 'h':
4829       // legacy resolver https://
4830       checkCIR(false);
4831       return g.nihResolverFormat
4832           + name.substring(name.indexOf("/structure") + 10);
4833     case '=':
4834       if (name.startsWith("==")) {
4835         id = id.substring(1);
4836         type = '#';
4837       } else if (id.indexOf("/") > 0) {
4838         // =xxxx/....
4839         try {
4840           int pt = id.indexOf("/");
4841           String database = id.substring(0, pt);
4842           id = JC.resolveDataBase(database, id.substring(pt + 1), null);
4843           if (id != null && id.startsWith("'"))
4844             id = evaluateExpression(id).toString();
4845           return (id == null || id.length() == 0 ? name : id);
4846         } catch (Exception e) {
4847           return name;
4848         }
4849       } else {
4850         if (id.endsWith(".mmtf")) {
4851           id = id.substring(0, id.indexOf(".mmtf"));
4852           return JC.resolveDataBase("mmtf", id.toUpperCase(), null);
4853         }
4854         format = g.loadFormat;
4855       }
4856       //$FALL-THROUGH$
4857     case '#': // ligand
4858       if (format == null)
4859         format = g.pdbLoadLigandFormat;
4860       return JC.resolveDataBase(null, id, format);
4861     case '*':
4862       // European Bioinformatics Institute
4863       int pt = name.lastIndexOf("/");
4864       if (name.startsWith("*dom/")) {
4865         //  *dom/.../.../.../xxxx
4866         id = name.substring(pt + 1);
4867         format = (pt > 4 ? name.substring(5) : "mappings");
4868         return PT.rep(JC.resolveDataBase("map", id, null), "%TYPE", format);
4869       } else if (name.startsWith("*val/")) {
4870         //  *val/.../.../.../xxxx
4871         id = name.substring(pt + 1);
4872         format = (pt > 4 ? name.substring(5) : "validation/outliers/all");
4873         return PT.rep(JC.resolveDataBase("map", id, null), "%TYPE", format);
4874       } else if (name.startsWith("*rna3d/")) {
4875         //  *rna3d/.../.../.../xxxx
4876         id = name.substring(pt + 1);
4877         format = (pt > 6 ? name.substring(6) : "loops");
4878         return PT.rep(JC.resolveDataBase("rna3d", id, null), "%TYPE", format);
4879       } else if (name.startsWith("*dssr--")) {
4880         id = name.substring(pt + 1);
4881         id = JC.resolveDataBase("dssr", id, null);
4882         return id + "%20" + PT.rep(name.substring(5, pt), " ", "%20");
4883       } else if (name.startsWith("*dssr/")) {
4884         id = name.substring(pt + 1);
4885         return JC.resolveDataBase("dssr", id, null);
4886       } else if (name.startsWith("*dssr1/")) {
4887         id = name.substring(pt + 1);
4888         return JC.resolveDataBase("dssr1", id, null);
4889       }
4890       // these are processed in SmarterJmolAdapter
4891       String pdbe = "pdbe";
4892       if (id.length() == 5 && id.charAt(4) == '*') {
4893         pdbe = "pdbe2";
4894         id = id.substring(0, 4);
4895       }
4896       return JC.resolveDataBase(pdbe, id, null);
4897     case ':': // PubChem
4898       format = g.pubChemFormat;
4899       if (id.equals("")) {
4900         try {
4901           id = "smiles:" + getOpenSmiles(bsA());
4902         } catch (Exception e) {
4903           // oh well.
4904         }
4905       }
4906       String fl = id.toLowerCase();
4907       int fi = Integer.MIN_VALUE;
4908       try {
4909         fi = Integer.parseInt(id);
4910       } catch (Exception e) {
4911         //
4912       }
4913       if (fi != Integer.MIN_VALUE) {
4914         id = "cid/" + fi;
4915       } else {
4916         if (fl.startsWith("smiles:")) {
4917           format += "?POST?smiles=" + id.substring(7);
4918           id = "smiles";
4919         } else if (id.startsWith("cid:") || id.startsWith("inchikey:")
4920             || id.startsWith("cas:")) {
4921           id = id.replace(':', '/');
4922         } else {
4923           if (fl.startsWith("name:"))
4924             id = id.substring(5);
4925           id = "name/" + PT.escapeUrl(id);
4926         }
4927       }
4928       return PT.formatStringS(format, "FILE", id);
4929     case '$':
4930       checkCIR(false);
4931       if (name.equals("$")) {
4932         try {
4933           id = getOpenSmiles(bsA());
4934         } catch (Exception e) {
4935           // oh well...
4936         }
4937       } else if (name.startsWith("$$")) {
4938         // 2D version
4939         id = id.substring(1);
4940         if (id.length() == 0) {
4941           try {
4942             id = getOpenSmiles(bsA());
4943           } catch (Exception e) {
4944           }
4945         }
4946         //http://cactus.nci.nih.gov/chemical/structure/C%28O%29CCC/file?format=sdf
4947         format = PT.rep(g.smilesUrlFormat, "get3d=true", "get3d=false");
4948         return PT.formatStringS(format, "FILE", PT.escapeUrl(id));
4949       }
4950       //$FALL-THROUGH$
4951     case 'M':
4952     case 'N':
4953     case '2':
4954     case 'I':
4955     case 'K':
4956     case 'S':
4957     case 'T':
4958     case '/':
4959       id = PT.escapeUrl(id);
4960       switch (type) {
4961       case 'M':
4962       case 'N':
4963         format = g.nihResolverFormat + "/%FILE/names";
4964         break;
4965       case '2':
4966         format = g.nihResolverFormat + "/%FILE/image";
4967         break;
4968       case 'I':
4969       case 'T':
4970         format = g.nihResolverFormat + "/%FILE/stdinchi";
4971         break;
4972       case 'K':
4973         format = g.nihResolverFormat + "/%FILE/inchikey";
4974         break;
4975       case 'S':
4976         format = g.nihResolverFormat + "/%FILE/stdinchikey";
4977         break;
4978       case '/':
4979         format = g.nihResolverFormat + "/%FILE/";
4980         break;
4981       default:
4982         format = g.smilesUrlFormat;
4983         break;
4984       }
4985       return (withPrefix ? "MOL3D::" : "")
4986           + PT.formatStringS(format, "FILE", id);
4987     case '?': // check only
4988     case '-': // localized version using the PDBe density server and box....
4989     case '_': // isosurface "=...", but we code that type as '_'
4990       // now *xxxx or =xxxx   --  both go to EBI instead of Uppsala,
4991       // as Uppsala is being decommissioned in 2018
4992       boolean isDiff = id.startsWith("*") || id.startsWith("=");
4993       if (isDiff)
4994         id = id.substring(1);
4995       String ciftype = null;
4996       pt = id.indexOf(".");
4997       if (pt >= 0) {
4998         ciftype = id.substring(pt + 1);
4999         id = id.substring(0, pt);
5000       }
5001       boolean checkXray = id.startsWith("density");
5002       if (checkXray)
5003         id = "em" + id.substring(7);
5004       if (id.equals("emdb") || id.equals("em"))
5005         id += "/";
5006       if (id.startsWith("em/"))
5007         id = "emdb" + id.substring(2);
5008       if (id.startsWith("emdb/")) {
5009         // *emdb/9357
5010         // *emdb/=6nef
5011         // *emdb/=, *emdb/, *emdb
5012         id = id.substring(5);
5013         if (id.length() == 0)
5014           id = "=";
5015         else if (id.startsWith("*"))
5016           id = "=" + id.substring(1);
5017         String emdext = "#-sigma=10";
5018         if (id.startsWith("=")) {
5019           id = (id.equals("=") ? getPdbID()
5020               : id.substring(1));
5021           if (id == null || type == '?')
5022             return id;
5023           String q = JC.resolveDataBase("emdbquery", id, null);
5024           String data = (String) fm.cacheGet(q, false);
5025           if (data == null) {
5026               showString("retrieving " + q, false);
5027             data = getFileAsString(q);
5028             if (data == null) {
5029               showString("EM retrieve failed for " + id, false);
5030               if (!checkXray)
5031                 return null;
5032               data = "FAILED";
5033             } else {
5034               showString(data, false);
5035             }
5036             fm.cachePut(q, data);
5037           }
5038           pt = data.indexOf("EMD-");
5039           if (pt >= 0) {
5040             id = data.substring(pt + 4);
5041             pt = id.indexOf('\n');
5042             if (pt > 0)
5043               id = id.substring(0, pt);
5044             pt = id.indexOf(",");
5045             if (pt > 0) {
5046               emdext = "#-cutoff=" + id.substring(pt + 1);
5047               id = id.substring(0, pt);
5048             }
5049           } else {
5050             if (!checkXray)
5051               return null;
5052             emdext = null;
5053           }
5054         }
5055         if (emdext != null)
5056           return JC.resolveDataBase("emdbmap" + (type == '-' ? "server" : ""), id,
5057               null) + emdext;
5058       }
5059       id = JC.resolveDataBase(
5060           (isDiff ? "pdbemapdiff" : "pdbemap") + (type == '-' ? "server" : ""),
5061           id, null);
5062       if ("cif".equals(ciftype)) {
5063         id = id.replace("bcif", "cif");
5064       }
5065       break;
5066     }
5067     return id;
5068   }
5069 
5070   boolean cirChecked;
5071 
5072   /**
5073    * Check to see if the resolver is working
5074    *
5075    * @param forceCheck
5076    */
checkCIR(boolean forceCheck)5077   private void checkCIR(boolean forceCheck) {
5078     if (cirChecked && !forceCheck)
5079       return;
5080     try {
5081       g.removeParam("_cirStatus");
5082       Map<String, Object> m = getModelSetAuxiliaryInfo();
5083       m.remove("cirInfo");
5084       Map<String, Object> map = parseJSONMap(
5085           getFileAsString(g.resolverResolver));
5086       m.put("cirInfo", map);
5087       ms.msInfo = m;
5088       String s = (String) map.get("status");
5089       g.setO("_cirStatus", s);
5090       g.setCIR((String) map.get("rfc6570Template"));
5091       System.out.println("Viewer.checkCIR _.cirInfo.status = " + s);
5092     } catch (Throwable t) {
5093       System.out.println(
5094           "Viewer.checkCIR failed at " + g.resolverResolver + ": " + t);
5095     }
5096     cirChecked = true;
5097   }
5098 
getStandardLabelFormat(int type)5099   public String getStandardLabelFormat(int type) {
5100     switch (type) {
5101     default:
5102     case 0: // standard
5103       return LabelToken.STANDARD_LABEL;
5104     case 1:
5105       return g.defaultLabelXYZ;
5106     case 2:
5107       return g.defaultLabelPDB;
5108     }
5109   }
5110 
getAdditionalHydrogens(BS bsAtoms, Lst<Atom> vConnections, int flags)5111   public P3[] getAdditionalHydrogens(BS bsAtoms, Lst<Atom> vConnections,
5112                                      int flags) {
5113     if (bsAtoms == null)
5114       bsAtoms = bsA();
5115     int[] nTotal = new int[1];
5116     P3[][] pts = ms.calculateHydrogens(bsAtoms, nTotal, vConnections, flags);
5117     P3[] points = new P3[nTotal[0]];
5118     for (int i = 0, pt = 0; i < pts.length; i++)
5119       if (pts[i] != null)
5120         for (int j = 0; j < pts[i].length; j++)
5121           points[pt++] = pts[i][j];
5122     return points;
5123   }
5124 
5125   @Override
setMarBond(short marBond)5126   public void setMarBond(short marBond) {
5127     g.bondRadiusMilliAngstroms = marBond;
5128     g.setI("bondRadiusMilliAngstroms", marBond);
5129     setShapeSize(JC.SHAPE_STICKS, marBond * 2, BSUtil.setAll(ms.ac));
5130   }
5131 
5132   private int hoverAtomIndex = -1;
5133   private String hoverText, hoverLabel = "%U";
5134   private boolean hoverEnabled = true;
5135 
setHoverLabel(String strLabel)5136   public void setHoverLabel(String strLabel) {
5137     shm.loadShape(JC.SHAPE_HOVER);
5138     setShapeProperty(JC.SHAPE_HOVER, "label", strLabel);
5139     setHoverEnabled(strLabel != null);
5140     g.setO("_hoverLabel", hoverLabel = strLabel);
5141     if (!hoverEnabled && !sm.haveHoverCallback())
5142       startHoverWatcher(false);
5143   }
5144 
setHoverEnabled(boolean tf)5145   private void setHoverEnabled(boolean tf) {
5146     hoverEnabled = tf;
5147     g.setB("_hoverEnabled", tf);
5148   }
5149 
5150   /*
5151    * hoverCallback reports information about the atom being hovered over.
5152    *
5153    * jmolSetCallback("hoverCallback", "myHoverCallback") function
5154    * myHoverCallback(strInfo, iAtom) {}
5155    *
5156    * strInfo == the atom's identity, including x, y, and z coordinates iAtom ==
5157    * the index of the atom being hovered over
5158    *
5159    * Viewer.setStatusAtomHovered Hover.setProperty("target") Viewer.hoverOff
5160    * Viewer.hoverOn
5161    */
hoverOn(int atomIndex, boolean isLabel)5162   void hoverOn(int atomIndex, boolean isLabel) {
5163     g.removeParam("_objecthovered");
5164     g.setI("_atomhovered", atomIndex);
5165     g.setO("_hoverLabel", hoverLabel);
5166     g.setUserVariable("hovered",
5167         SV.getVariable(BSUtil.newAndSetBit(atomIndex)));
5168     if (sm.haveHoverCallback())
5169       sm.setStatusAtomHovered(atomIndex, getAtomInfoXYZ(atomIndex, false));
5170     if (!hoverEnabled || eval != null && isScriptExecuting()
5171         || atomIndex == hoverAtomIndex || g.hoverDelayMs == 0
5172         || !slm.isInSelectionSubset(atomIndex))
5173       return;
5174     String label = (isLabel ? GT.$("Drag to move label")
5175         : g.modelKitMode && modelkit != null
5176             ? (String) modelkit.setProperty("hoverLabel",
5177                 Integer.valueOf(atomIndex))
5178             : null);
5179 
5180     shm.loadShape(JC.SHAPE_HOVER);
5181     if (label != null
5182         && (!isLabel || ms.at[atomIndex].isVisible(JC.VIS_LABEL_FLAG))) {
5183       setShapeProperty(JC.SHAPE_HOVER, "specialLabel", label);
5184     }
5185     setShapeProperty(JC.SHAPE_HOVER, "text", hoverText = null);
5186     setShapeProperty(JC.SHAPE_HOVER, "target",
5187         Integer.valueOf(hoverAtomIndex = atomIndex));
5188     refresh(REFRESH_SYNC_MASK, "hover on atom");
5189   }
5190 
5191   /**
5192    * Hover over an arbitrary point.
5193    *
5194    * @param x
5195    * @param y
5196    * @param text
5197    * @param id
5198    *        optional id to set _objecthovered to
5199    * @param pt
5200    *        optional pt to set "hovered" to
5201    */
hoverOnPt(int x, int y, String text, String id, T3 pt)5202   public void hoverOnPt(int x, int y, String text, String id, T3 pt) {
5203     // from draw for drawhover on
5204     if (eval != null && isScriptExecuting())
5205       return;
5206     g.setO("_hoverLabel", text);
5207     if (id != null && pt != null) {
5208       g.setO("_objecthovered", id);
5209       g.setI("_atomhovered", -1);
5210       g.setUserVariable("hovered", SV.getVariable(pt));
5211       if (sm.haveHoverCallback())
5212         sm.setStatusObjectHovered(id, text, pt);
5213     }
5214     if (!hoverEnabled)
5215       return;
5216     shm.loadShape(JC.SHAPE_HOVER);
5217     setShapeProperty(JC.SHAPE_HOVER, "xy", P3i.new3(x, y, 0));
5218     setShapeProperty(JC.SHAPE_HOVER, "target", null);
5219     setShapeProperty(JC.SHAPE_HOVER, "specialLabel", null);
5220     setShapeProperty(JC.SHAPE_HOVER, "text", text);
5221     hoverAtomIndex = -1;
5222     hoverText = text;
5223     refresh(REFRESH_SYNC_MASK, "hover on point");
5224   }
5225 
hoverOff()5226   void hoverOff() {
5227     try {
5228       if (g.modelKitMode
5229           && acm.getBondPickingMode() != ActionManager.PICKING_ROTATE_BOND)
5230         highlight(null);
5231       if (!hoverEnabled)
5232         return;
5233       boolean isHover = (hoverText != null || hoverAtomIndex >= 0);
5234       if (hoverAtomIndex >= 0) {
5235         setShapeProperty(JC.SHAPE_HOVER, "target", null);
5236         hoverAtomIndex = -1;
5237       }
5238       if (hoverText != null) {
5239         setShapeProperty(JC.SHAPE_HOVER, "text", null);
5240         hoverText = null;
5241       }
5242       setShapeProperty(JC.SHAPE_HOVER, "specialLabel", null);
5243       if (isHover)
5244         refresh(REFRESH_SYNC_MASK, "hover off");
5245     } catch (Exception e) {
5246       // ignore
5247     }
5248   }
5249 
5250   @Override
setDebugScript(boolean debugScript)5251   public void setDebugScript(boolean debugScript) {
5252     g.debugScript = debugScript;
5253     g.setB("debugScript", debugScript);
5254     if (eval != null)
5255       eval.setDebugging();
5256   }
5257 
clearClickCount()5258   void clearClickCount() {
5259     setTainted(true);
5260   }
5261 
5262   public int currentCursor = GenericPlatform.CURSOR_DEFAULT;
5263 
setCursor(int cursor)5264   public void setCursor(int cursor) {
5265     if (isKiosk || currentCursor == cursor || multiTouch || !haveDisplay)
5266       return;
5267     apiPlatform.setCursor(currentCursor = cursor, display);
5268   }
5269 
setPickingMode(String strMode, int pickingMode)5270   public void setPickingMode(String strMode, int pickingMode) {
5271     if (!haveDisplay)
5272       return;
5273     showSelected = false;
5274     String option = null;
5275     if (strMode != null) {
5276       int pt = strMode.indexOf("_");
5277       if (pt >= 0) {
5278         option = strMode.substring(pt + 1);
5279         strMode = strMode.substring(0, pt);
5280       }
5281       pickingMode = ActionManager.getPickingMode(strMode);
5282     }
5283     if (pickingMode < 0)
5284       pickingMode = ActionManager.PICKING_IDENTIFY;
5285     acm.setPickingMode(pickingMode);
5286     g.setO("picking",
5287         ActionManager.getPickingModeName(acm.getAtomPickingMode()));
5288     if (option == null || option.length() == 0)
5289       return;
5290     option = Character.toUpperCase(option.charAt(0))
5291         + (option.length() == 1 ? "" : option.substring(1, 2));
5292     switch (pickingMode) {
5293     case ActionManager.PICKING_ASSIGN_ATOM:
5294       getModelkit(false).setProperty("atomType", option);
5295       break;
5296     case ActionManager.PICKING_ASSIGN_BOND:
5297       getModelkit(false).setProperty("bondType", option);
5298       break;
5299     default:
5300       Logger.error("Bad picking mode: " + strMode + "_" + option);
5301     }
5302   }
5303 
getPickingMode()5304   public int getPickingMode() {
5305     return (haveDisplay ? acm.getAtomPickingMode() : 0);
5306   }
5307 
setPickingStyle(String style, int pickingStyle)5308   void setPickingStyle(String style, int pickingStyle) {
5309     if (!haveDisplay)
5310       return;
5311     if (style != null)
5312       pickingStyle = ActionManager.getPickingStyleIndex(style);
5313     if (pickingStyle < 0)
5314       pickingStyle = ActionManager.PICKINGSTYLE_SELECT_JMOL;
5315     acm.setPickingStyle(pickingStyle);
5316     g.setO("pickingStyle",
5317         ActionManager.getPickingStyleName(acm.getPickingStyle()));
5318   }
5319 
getDrawHover()5320   public boolean getDrawHover() {
5321     return haveDisplay && g.drawHover;
5322   }
5323 
5324   private P3 ptTemp;
5325 
getAtomInfo(int atomOrPointIndex)5326   public String getAtomInfo(int atomOrPointIndex) {
5327     if (ptTemp == null)
5328       ptTemp = new P3();
5329     // only for MeasurementTable and actionManager
5330     return (atomOrPointIndex >= 0
5331         ? ms.getAtomInfo(atomOrPointIndex, null, ptTemp)
5332         : (String) shm.getShapePropertyIndex(JC.SHAPE_MEASURES, "pointInfo",
5333             -atomOrPointIndex));
5334   }
5335 
getAtomInfoXYZ(int atomIndex, boolean useChimeFormat)5336   private String getAtomInfoXYZ(int atomIndex, boolean useChimeFormat) {
5337     Atom atom = ms.at[atomIndex];
5338     if (useChimeFormat)
5339       return getChimeMessenger().getInfoXYZ(atom);
5340     if (ptTemp == null)
5341       ptTemp = new P3();
5342     return atom.getIdentityXYZ(true, ptTemp);
5343   }
5344 
5345   // //////////////status manager dispatch//////////////
5346 
setSync()5347   private void setSync() {
5348     if (sm.doSync())
5349       sm.setSync(null);
5350   }
5351 
5352   @Override
setJmolCallbackListener(JmolCallbackListener listener)5353   public void setJmolCallbackListener(JmolCallbackListener listener) {
5354     sm.cbl = listener;
5355   }
5356 
5357   @Override
setJmolStatusListener(JmolStatusListener listener)5358   public void setJmolStatusListener(JmolStatusListener listener) {
5359     sm.cbl = sm.jsl = listener;
5360   }
5361 
getStatusChanged(String statusNameList)5362   public Lst<Lst<Lst<Object>>> getStatusChanged(String statusNameList) {
5363     return (statusNameList == null ? null
5364         : sm.getStatusChanged(statusNameList));
5365   }
5366 
menuEnabled()5367   public boolean menuEnabled() {
5368     return (!g.disablePopupMenu && getPopupMenu() != null);
5369   }
5370 
setStatusDragDropped(int mode, int x, int y, String fileName)5371   public boolean setStatusDragDropped(int mode, int x, int y, String fileName) {
5372     if (mode == 0) {
5373       g.setO("_fileDropped", fileName);
5374       g.setUserVariable("doDrop", SV.vT);
5375     }
5376     boolean handled = sm.setStatusDragDropped(mode, x, y, fileName);
5377     return (!handled || getP("doDrop").toString().equals("true"));
5378   }
5379 
5380   /*
5381    * resizeCallback is called whenever the applet gets a resize notification
5382    * from the browser
5383    *
5384    * jmolSetCallback("resizeCallback", "myResizeCallback") function
5385    * myResizeCallback(width, height) {}
5386    */
5387 
setStatusResized(int width, int height)5388   public void setStatusResized(int width, int height) {
5389     sm.setStatusResized(width, height);
5390   }
5391 
5392   /*
5393    * scriptCallback is the primary way to monitor script status. In addition, it
5394    * serves to for passing information to the user over the status line of the
5395    * browser as well as to the console. Note that console messages are also sent
5396    * by echoCallback. If messageCallback is enabled but not scriptCallback,
5397    * these messages go to the messageCallback function instead.
5398    *
5399    * jmolSetCallback("scriptCallback", "myScriptCallback") function
5400    * myScriptCallback(app, status, message, intStatus, errorMessageUntranslated)
5401    * {}
5402    *
5403    * intStatus == -2 script start -- message is the script itself intStatus == 0
5404    * general messages during script execution; translated error message may be
5405    * present intStatus >= 1 script termination message; translated and
5406    * untranslated message may be present value is time for execution in
5407    * milliseconds
5408    *
5409    * Eval.defineAtomSet -- compilation bug indicates problem in JmolConstants
5410    * array Eval.instructionDispatchLoop -- debugScript messages
5411    * Eval.logDebugScript -- debugScript messages Eval.pause -- script execution
5412    * paused message Eval.runEval -- "Script completed" message Eval.script --
5413    * Chime "script <exiting>" message Eval.scriptStatusOrBuffer -- various
5414    * messages for Eval.checkContinue (error message) Eval.connect Eval.delete
5415    * Eval.hbond Eval.load (logMessages message) Eval.message Eval.runEval (error
5416    * message) Eval.write (error reading file) Eval.zap (error message)
5417    * FileManager.createAtomSetCollectionFromFile "requesting..." for Chime-like
5418    * compatibility actionManager.atomPicked
5419    * "pick one more atom in order to spin..." for example
5420    * Viewer.evalStringWaitStatus -- see above -2, 0 only if error, >=1 at
5421    * termination Viewer.reportSelection "xxx atoms selected"
5422    */
5423 
scriptStatus(String strStatus)5424   public void scriptStatus(String strStatus) {
5425     setScriptStatus(strStatus, "", 0, null);
5426   }
5427 
scriptStatusMsg(String strStatus, String statusMessage)5428   public void scriptStatusMsg(String strStatus, String statusMessage) {
5429     setScriptStatus(strStatus, statusMessage, 0, null);
5430   }
5431 
setScriptStatus(String strStatus, String statusMessage, int msWalltime, String strErrorMessageUntranslated)5432   public void setScriptStatus(String strStatus, String statusMessage,
5433                               int msWalltime,
5434                               String strErrorMessageUntranslated) {
5435     sm.setScriptStatus(strStatus, statusMessage, msWalltime,
5436         strErrorMessageUntranslated);
5437   }
5438 
5439   /*
5440    * syncCallback traps script synchronization messages and allows for
5441    * cancellation (by returning "") or modification
5442    *
5443    * jmolSetCallback("syncCallback", "mySyncCallback") function
5444    * mySyncCallback(app, script, appletName) { ...[modify script here]... return
5445    * newScript }
5446    *
5447    * StatusManager.syncSend Viewer.setSyncTarget Viewer.syncScript
5448    */
5449 
5450   @Override
showUrl(String urlString)5451   public void showUrl(String urlString) {
5452     // applet.Jmol
5453     // app Jmol
5454     // StatusManager
5455     if (urlString == null)
5456       return;
5457     if (urlString.indexOf(":") < 0) {
5458       String base = fm.getAppletDocumentBase();
5459       if (base == "")
5460         base = fm.getFullPathName(false);
5461       if (base.indexOf("/") >= 0) {
5462         base = base.substring(0, base.lastIndexOf("/") + 1);
5463       } else if (base.indexOf("\\") >= 0) {
5464         base = base.substring(0, base.lastIndexOf("\\") + 1);
5465       }
5466       urlString = base + urlString;
5467     }
5468     Logger.info("showUrl:" + urlString);
5469     sm.showUrl(urlString);
5470   }
5471 
5472   /**
5473    * an external applet or app with class that extends org.jmol.jvxl.MeshCreator
5474    * might execute:
5475    *
5476    * org.jmol.viewer.Viewer vwr = applet.getViewer(); vwr.setMeshCreator(this);
5477    *
5478    * then that class's updateMesh(String id) method will be called whenever a
5479    * mesh is rendered.
5480    *
5481    * @param meshCreator
5482    */
setMeshCreator(Object meshCreator)5483   public void setMeshCreator(Object meshCreator) {
5484     shm.loadShape(JC.SHAPE_ISOSURFACE);
5485     setShapeProperty(JC.SHAPE_ISOSURFACE, "meshCreator", meshCreator);
5486   }
5487 
showConsole(boolean showConsole)5488   public void showConsole(boolean showConsole) {
5489     if (!haveDisplay)
5490       return;
5491     // Eval
5492     try {
5493       if (appConsole == null && showConsole)
5494         getConsole();
5495       appConsole.setVisible(true);
5496     } catch (Throwable e) {
5497       // no console for this client... maybe no Swing
5498     }
5499   }
5500 
getConsole()5501   public JmolAppConsoleInterface getConsole() {
5502     getProperty("DATA_API", "getAppConsole", Boolean.TRUE);
5503     return appConsole;
5504   }
5505 
5506   @Override
getParameter(String key)5507   public Object getParameter(String key) {
5508     return getP(key);
5509   }
5510 
getP(String key)5511   public Object getP(String key) {
5512     return g.getParameter(key, true);
5513   }
5514 
getPOrNull(String key)5515   public Object getPOrNull(String key) {
5516     return g.getParameter(key, false);
5517   }
5518 
unsetProperty(String key)5519   public void unsetProperty(String key) {
5520     key = key.toLowerCase();
5521     if (key.equals("all") || key.equals("variables"))
5522       fm.setPathForAllFiles("");
5523     g.unsetUserVariable(key);
5524   }
5525 
5526   @Override
notifyStatusReady(boolean isReady)5527   public void notifyStatusReady(boolean isReady) {
5528     System.out.println(
5529         "Jmol applet " + fullName + (isReady ? " ready" : " destroyed"));
5530     if (!isReady)
5531       dispose();
5532     sm.setStatusAppletReady(fullName, isReady);
5533   }
5534 
5535   @Override
getBooleanProperty(String key)5536   public boolean getBooleanProperty(String key) {
5537     key = key.toLowerCase();
5538     if (g.htBooleanParameterFlags.containsKey(key))
5539       return g.htBooleanParameterFlags.get(key).booleanValue();
5540     // special cases
5541     if (key.endsWith("p!")) {
5542       if (acm == null)
5543         return false;
5544       String s = acm.getPickingState().toLowerCase();
5545       key = key.substring(0, key.length() - 2) + ";";
5546       return (s.indexOf(key) >= 0);
5547     }
5548     if (key.equalsIgnoreCase("executionPaused"))
5549       return (eval != null && eval.isPaused());
5550     if (key.equalsIgnoreCase("executionStepping"))
5551       return (eval != null && eval.isStepping());
5552     if (key.equalsIgnoreCase("haveBFactors"))
5553       return (ms.getBFactors() != null);
5554     if (key.equalsIgnoreCase("colorRasmol"))
5555       return cm.isDefaultColorRasmol;
5556     if (key.equalsIgnoreCase("frank"))
5557       return getShowFrank();
5558     if (key.equalsIgnoreCase("spinOn"))
5559       return tm.spinOn;
5560     if (key.equalsIgnoreCase("isNavigating"))
5561       return tm.isNavigating();
5562     if (key.equalsIgnoreCase("showSelections"))
5563       return selectionHalosEnabled;
5564     if (g.htUserVariables.containsKey(key)) {
5565       SV t = g.getUserVariable(key);
5566       if (t.tok == T.on)
5567         return true;
5568       if (t.tok == T.off)
5569         return false;
5570     }
5571     Logger.error("vwr.getBooleanProperty(" + key + ") - unrecognized");
5572     return false;
5573   }
5574 
5575   @Override
getInt(int tok)5576   public int getInt(int tok) {
5577     switch (tok) {
5578     case T.animationfps:
5579       return am.animationFps;
5580     case T.dotdensity:
5581       return g.dotDensity;
5582     case T.dotscale:
5583       return g.dotScale;
5584     case T.helixstep:
5585       return g.helixStep;
5586     case T.infofontsize:
5587       return g.infoFontSize;
5588     case T.meshscale:
5589       return g.meshScale;
5590     case T.minpixelselradius:
5591       return g.minPixelSelRadius;
5592     case T.percentvdwatom:
5593       return g.percentVdwAtom;
5594     case T.pickingspinrate:
5595       return g.pickingSpinRate;
5596     case T.ribbonaspectratio:
5597       return g.ribbonAspectRatio;
5598     case T.showscript:
5599       return g.scriptDelay;
5600     case T.minimizationmaxatoms:
5601       return g.minimizationMaxAtoms;
5602     case T.smallmoleculemaxatoms:
5603       return g.smallMoleculeMaxAtoms;
5604     case T.strutspacing:
5605       return g.strutSpacing;
5606     case T.vectortrail:
5607       return g.vectorTrail;
5608     }
5609     Logger.error("viewer.getInt(" + T.nameOf(tok) + ") - not listed");
5610     return 0;
5611   }
5612 
5613   // special cases:
5614 
getDelayMaximumMs()5615   public int getDelayMaximumMs() {
5616     return (haveDisplay ? g.delayMaximumMs : 1);
5617   }
5618 
getHermiteLevel()5619   public int getHermiteLevel() {
5620     return (tm.spinOn && g.hermiteLevel > 0 ? 0 : g.hermiteLevel);
5621   }
5622 
getHoverDelay()5623   public int getHoverDelay() {
5624     return (g.modelKitMode ? 20 : g.hoverDelayMs);
5625 
5626   }
5627 
5628   @Override
getBoolean(int tok)5629   public boolean getBoolean(int tok) {
5630     switch (tok) {
5631     case T.nbocharges:
5632       return g.nboCharges;
5633     case T.hiddenlinesdashed:
5634       return g.hiddenLinesDashed;
5635     case T.pdb:
5636       return ms.getMSInfoB("isPDB");
5637     case T.autoplaymovie:
5638       return g.autoplayMovie;
5639     case T.allowaudio:
5640       return !headless && g.allowAudio;
5641     case T.allowgestures:
5642       return g.allowGestures;
5643     case T.allowmultitouch:
5644       return g.allowMultiTouch;
5645     case T.allowrotateselected:
5646       return g.allowRotateSelected;
5647     case T.appendnew:
5648       return g.appendNew;
5649     case T.applysymmetrytobonds:
5650       return g.applySymmetryToBonds;
5651     case T.atompicking:
5652       return g.atomPicking;
5653     case T.autobond:
5654       return g.autoBond;
5655     case T.autofps:
5656       return g.autoFps;
5657     case T.axesorientationrasmol:
5658       return g.axesOrientationRasmol;
5659     case T.cartoonsteps:
5660       return g.cartoonSteps;
5661     case T.cartoonblocks:
5662       return g.cartoonBlocks;
5663     case T.checkcir:
5664       return g.checkCIR;
5665     case T.bondmodeor:
5666       return g.bondModeOr;
5667     case T.cartoonbaseedges:
5668       return g.cartoonBaseEdges;
5669     case T.cartoonsfancy:
5670       return g.cartoonFancy;
5671     case T.cartoonladders:
5672       return g.cartoonLadders;
5673     case T.cartoonribose:
5674       return g.cartoonRibose;
5675     case T.cartoonrockets:
5676       return g.cartoonRockets;
5677     case T.chaincasesensitive:
5678       return g.chainCaseSensitive || chainCaseSpecified;
5679     case T.ciprule6full:
5680       return g.cipRule6Full;
5681     case T.debugscript:
5682       return g.debugScript;
5683     case T.defaultstructuredssp:
5684       return g.defaultStructureDSSP;
5685     case T.disablepopupmenu:
5686       return g.disablePopupMenu;
5687     case T.displaycellparameters:
5688       return g.displayCellParameters;
5689     case T.dotsurface:
5690       return g.dotSurface;
5691     case T.dotsselectedonly:
5692       return g.dotsSelectedOnly;
5693     case T.drawpicking:
5694       return g.drawPicking;
5695     case T.fontcaching:
5696       return g.fontCaching;
5697     case T.fontscaling:
5698       return g.fontScaling;
5699     case T.forceautobond:
5700       return g.forceAutoBond;
5701     case T.fractionalrelative:
5702       return false;//g.fractionalRelative;
5703     case T.greyscalerendering:
5704       return g.greyscaleRendering;
5705     case T.hbondsbackbone:
5706       return g.hbondsBackbone;
5707     case T.hbondsrasmol:
5708       return g.hbondsRasmol;
5709     case T.hbondssolid:
5710       return g.hbondsSolid;
5711     case T.hetero:
5712       return g.rasmolHeteroSetting;
5713     case T.hidenameinpopup:
5714       return g.hideNameInPopup;
5715     case T.highresolution:
5716       return g.highResolutionFlag;
5717     case T.hydrogen:
5718       return g.rasmolHydrogenSetting;
5719     case T.isosurfacekey:
5720       return g.isosurfaceKey;
5721     case T.jmolinjspecview:
5722       return g.jmolInJSpecView;
5723     case T.justifymeasurements:
5724       return g.justifyMeasurements;
5725     case T.legacyautobonding:
5726       // aargh -- BitSet efficiencies in Jmol 11.9.24, 2/3/2010, meant that
5727       // state files created before that that use select BONDS will select the
5728       // wrong bonds.
5729       // reset after a state script is read
5730       return g.legacyAutoBonding;
5731     case T.legacyhaddition:
5732       // aargh -- Some atoms missed before Jmol 13.1.17
5733       return g.legacyHAddition;
5734     case T.legacyjavafloat:
5735       return g.legacyJavaFloat;
5736     case T.loggestures:
5737       return g.logGestures;
5738     case T.measureallmodels:
5739       return g.measureAllModels;
5740     case T.measurementlabels:
5741       return g.measurementLabels;
5742     case T.messagestylechime:
5743       return g.messageStyleChime;
5744     case T.modelkitmode:
5745       return g.modelKitMode;
5746     case T.multiplebondbananas:
5747       return g.multipleBondBananas;
5748     case T.navigationmode:
5749       return g.navigationMode;
5750     case T.navigationperiodic:
5751       return g.navigationPeriodic;
5752     case T.partialdots:
5753       return g.partialDots;
5754     case T.pdbaddhydrogens:
5755       return g.pdbAddHydrogens;
5756     case T.pdbsequential:
5757       return g.pdbSequential;
5758     case T.preservestate:
5759       return g.preserveState;
5760     case T.refreshing:
5761       return refreshing;
5762     case T.ribbonborder:
5763       return g.ribbonBorder;
5764     case T.rocketbarrels:
5765       return g.rocketBarrels;
5766     case T.nodelay:
5767       return g.noDelay;
5768     case T.selectallmodels:
5769       return g.selectAllModels;
5770     case T.showhiddenselectionhalos:
5771       return g.showHiddenSelectionHalos;
5772     case T.showhydrogens:
5773       return g.showHydrogens;
5774     case T.showmeasurements:
5775       return g.showMeasurements;
5776     case T.showmodvecs:
5777       return g.showModVecs;
5778     case T.showmultiplebonds:
5779       return g.showMultipleBonds;
5780     case T.showtiming:
5781       return g.showTiming;
5782     case T.showunitcelldetails:
5783       return g.showUnitCellDetails;
5784     case T.slabbyatom:
5785       return g.slabByAtom;
5786     case T.slabbymolecule:
5787       return g.slabByMolecule;
5788     case T.smartaromatic:
5789       return g.smartAromatic;
5790     case T.solventprobe:
5791       return g.dotSolvent;
5792     case T.ssbondsbackbone:
5793       return g.ssbondsBackbone;
5794     case T.strutsmultiple:
5795       return g.strutsMultiple;
5796     case T.testflag1:
5797       // CIPChirality -- turns off tracking (skip   creation of _M.CIPInfo for speed tests)
5798       // no PNGJ caching
5799       // debug mouse actions
5800       return g.testFlag1;
5801     case T.testflag2:
5802       // no load processing (jmolscript or 2D file load minimization)
5803       // passed to MOCalcuation, but not used
5804       // nciCalculation special params.testFlag = 2 "absolute" calc.
5805       // GIF reducedColors
5806       // plug-in use variable
5807       return g.testFlag2;
5808     case T.testflag3:
5809       // isosurface numbers
5810       // polyhedra numbers
5811       // pmesh triangles
5812       return g.testFlag3;
5813     case T.testflag4:
5814       // isosurface normals
5815       return g.testFlag4;
5816 
5817     case T.tracealpha:
5818       return g.traceAlpha;
5819     case T.translucent:
5820       return g.translucent;
5821     case T.twistedsheets:
5822       return g.twistedSheets;
5823     case T.vectorscentered:
5824       return g.vectorsCentered;
5825     case T.vectorsymmetry:
5826       return g.vectorSymmetry;
5827     case T.waitformoveto:
5828       return g.waitForMoveTo;
5829     case T.zerobasedxyzrasmol:
5830       return g.zeroBasedXyzRasmol;
5831     }
5832     Logger.error("viewer.getBoolean(" + T.nameOf(tok) + ") - not listed");
5833     return false;
5834   }
5835 
5836   // special cases:
5837 
allowEmbeddedScripts()5838   public boolean allowEmbeddedScripts() {
5839     return (g.allowEmbeddedScripts && !isPreviewOnly);
5840   }
5841 
getDragSelected()5842   boolean getDragSelected() {
5843     return (g.dragSelected && !g.modelKitMode);
5844   }
5845 
getBondsPickable()5846   boolean getBondsPickable() {
5847     return (g.bondPicking || g.modelKitMode
5848         && getModelkitProperty("isMolecular") == Boolean.TRUE);
5849   }
5850 
useMinimizationThread()5851   public boolean useMinimizationThread() {
5852     return (g.useMinimizationThread && !autoExit);
5853   }
5854 
5855   @Override
getFloat(int tok)5856   public float getFloat(int tok) {
5857     switch (tok) {
5858     case T.atoms:
5859       return g.particleRadius;
5860     case T.axesoffset:
5861       return g.axesOffset;
5862     case T.axesscale:
5863       return g.axesScale;
5864     case T.bondtolerance:
5865       return g.bondTolerance;
5866     case T.defaulttranslucent:
5867       return g.defaultTranslucent;
5868     case T.defaultdrawarrowscale:
5869       return g.defaultDrawArrowScale;
5870     case T.dipolescale:
5871       return g.dipoleScale;
5872     case T.drawfontsize:
5873       return g.drawFontSize;
5874     case T.exportscale:
5875       return g.exportScale;
5876     case T.hbondsangleminimum:
5877       return g.hbondsAngleMinimum;
5878     case T.hbondhxdistancemaximum:
5879       return g.hbondHXDistanceMaximum;
5880     case T.hbondnodistancemaximum:
5881       return g.hbondNODistanceMaximum;
5882     case T.loadatomdatatolerance:
5883       return g.loadAtomDataTolerance;
5884     case T.minbonddistance:
5885       return g.minBondDistance;
5886     case T.modulation:
5887       return g.modulationScale;
5888     case T.multiplebondspacing:
5889       return g.multipleBondSpacing;
5890     case T.multiplebondradiusfactor:
5891       return g.multipleBondRadiusFactor;
5892     case T.navigationspeed:
5893       return g.navigationSpeed;
5894     case T.pointgroupdistancetolerance:
5895       return g.pointGroupDistanceTolerance;
5896     case T.pointgrouplineartolerance:
5897       return g.pointGroupLinearTolerance;
5898     case T.rotationradius:
5899       return tm.modelRadius;
5900     case T.sheetsmoothing:
5901       return g.sheetSmoothing;
5902     case T.solventproberadius:
5903       return g.solventProbeRadius;
5904     case T.starwidth:
5905       return g.starWidth;
5906     case T.strutdefaultradius:
5907       return g.strutDefaultRadius;
5908     case T.strutlengthmaximum:
5909       return g.strutLengthMaximum;
5910     case T.vectorscale:
5911       return g.vectorScale;
5912     case T.vibrationperiod:
5913       return g.vibrationPeriod;
5914     case T.cartoonblockheight:
5915       // 14.11.0
5916       return g.cartoonBlockHeight;
5917     }
5918     Logger.error("viewer.getFloat(" + T.nameOf(tok) + ") - not listed");
5919     return 0;
5920   }
5921 
5922   @Override
setStringProperty(String key, String value)5923   public void setStringProperty(String key, String value) {
5924     if (value == null || key == null || key.length() == 0)
5925       return;
5926     if (key.charAt(0) == '_') {
5927       g.setO(key, value);
5928       return;
5929     }
5930     int tok = T.getTokFromName(key);
5931     switch (T.getParamType(tok)) {
5932     case T.booleanparam:
5933       setBooleanPropertyTok(key, tok, SV.newV(T.string, value).asBoolean());
5934       break;
5935     case T.intparam:
5936       setIntPropertyTok(key, tok, SV.newV(T.string, value).asInt());
5937       break;
5938     case T.floatparam:
5939       setFloatPropertyTok(key, tok, PT.parseFloat(value));
5940       break;
5941     default:
5942       setStringPropertyTok(key, tok, value);
5943     }
5944   }
5945 
setStringPropertyTok(String key, int tok, String value)5946   private void setStringPropertyTok(String key, int tok, String value) {
5947     switch (tok) {
5948     // 14.29.54 new
5949     case T.macrodirectory:
5950       g.macroDirectory = value = (value == null || value.length() == 0
5951           ? JC.defaultMacroDirectory
5952           : value);
5953       macros = null;
5954       break;
5955     // 14.4.10 new
5956     case T.nihresolverformat:
5957       g.nihResolverFormat = value;
5958       break;
5959     // removed for Jmol 14.29.1
5960     //    // 14.3.10 (forgot to add these earlier)
5961     //    case T.edsurlcutoff:
5962     //      g.edsUrlCutoff = value;
5963     //      break;
5964     //    case T.edsurlformat:
5965     //      g.edsUrlFormat = value;
5966     //      break;
5967     //    // 14.3.10 new
5968     //    case T.edsurlformatdiff:
5969     //      g.edsUrlFormatDiff = value;
5970     //      break;
5971     // 13.3.6
5972     case T.animationmode:
5973       setAnimationMode(value);
5974       return;
5975     case T.nmrpredictformat:
5976       // 13.3.4
5977       g.nmrPredictFormat = value;
5978       break;
5979     case T.defaultdropscript:
5980       // 13.1.2
5981       // for File|Open and Drag/drop
5982       g.defaultDropScript = value;
5983       break;
5984 
5985     case T.pathforallfiles:
5986       // 12.3.29
5987       value = fm.setPathForAllFiles(value);
5988       break;
5989     case T.energyunits:
5990       // 12.3.26
5991       setUnits(value, false);
5992       return;
5993     case T.forcefield:
5994       // 12.3.25
5995       g.forceField = value = ("UFF".equalsIgnoreCase(value) ? "UFF"
5996           : "UFF2D".equalsIgnoreCase(value) ? "UFF2D"
5997               : "MMFF2D".equalsIgnoreCase(value) ? "MMFF2D" : "MMFF");
5998       minimizer = null;
5999       break;
6000     case T.nmrurlformat:
6001       // 12.3.3
6002       g.nmrUrlFormat = value;
6003       break;
6004     case T.measurementunits:
6005       setUnits(value, true);
6006       return;
6007     case T.loadligandformat:
6008       // /12.1.51//
6009       g.pdbLoadLigandFormat = value;
6010       break;
6011     // 12.1.50
6012     case T.defaultlabelpdb:
6013       g.defaultLabelPDB = value;
6014       break;
6015     case T.defaultlabelxyz:
6016       g.defaultLabelXYZ = value;
6017       break;
6018     case T.defaultloadfilter:
6019       // 12.0.RC10
6020       g.defaultLoadFilter = value;
6021       break;
6022     case T.logfile:
6023       value = getOutputManager().setLogFile(value);
6024       if (value == null)
6025         return;
6026       break;
6027     case T.filecachedirectory:
6028       // 11.9.21
6029       // not implemented -- application only -- CANNOT BE SET BY STATE
6030       // global.fileCacheDirectory = value;
6031       break;
6032     case T.atomtypes:
6033       // 11.7.7
6034       g.atomTypes = value;
6035       break;
6036     case T.currentlocalpath:
6037       // /11.6.RC15
6038       break;
6039     case T.picklabel:
6040       // /11.5.42
6041       g.pickLabel = value;
6042       break;
6043     case T.quaternionframe:
6044       // /11.5.39//
6045       if (value.length() == 2 && value.startsWith("R"))
6046         // C, P -- straightness from Ramachandran angles
6047         g.quaternionFrame = value.substring(0, 2);
6048       else
6049         g.quaternionFrame = "" + (value.toLowerCase() + "p").charAt(0);
6050       if (!PT.isOneOf(g.quaternionFrame, JC.allowedQuaternionFrames))
6051         g.quaternionFrame = "p";
6052       ms.haveStraightness = false;
6053       break;
6054     case T.defaultvdw:
6055       // /11.5.11//
6056       setVdwStr(value);
6057       return;
6058     case T.language:
6059       // /11.1.30//
6060       // fr cs en none, etc.
6061       // also serves to change language for callbacks and menu
6062       new GT(this, value);
6063       String language = GT.getLanguage();
6064       modelkit = null;
6065       if (jmolpopup != null) {
6066         jmolpopup.jpiDispose();
6067         jmolpopup = null;
6068         getPopupMenu();
6069       }
6070       sm.setCallbackFunction("language", language);
6071       value = GT.getLanguage();
6072       break;
6073     case T.loadformat:
6074       // /11.1.22//
6075       g.loadFormat = value;
6076       break;
6077     case T.backgroundcolor:
6078       // /11.1///
6079       setObjectColor("background", value);
6080       return;
6081     case T.axis1color:
6082       setObjectColor("axis1", value);
6083       return;
6084     case T.axis2color:
6085       setObjectColor("axis2", value);
6086       return;
6087     case T.axis3color:
6088       setObjectColor("axis3", value);
6089       return;
6090     case T.boundboxcolor:
6091       setObjectColor("boundbox", value);
6092       return;
6093     case T.unitcellcolor:
6094       setObjectColor("unitcell", value);
6095       return;
6096     case T.propertycolorscheme:
6097       setPropertyColorScheme(value, false, false);
6098       break;
6099     case T.hoverlabel:
6100       // a special label for selected atoms
6101       shm.loadShape(JC.SHAPE_HOVER);
6102       setShapeProperty(JC.SHAPE_HOVER, "atomLabel", value);
6103       break;
6104     case T.defaultdistancelabel:
6105       // /11.0///
6106       g.defaultDistanceLabel = value;
6107       break;
6108     case T.defaultanglelabel:
6109       g.defaultAngleLabel = value;
6110       break;
6111     case T.defaulttorsionlabel:
6112       g.defaultTorsionLabel = value;
6113       break;
6114     case T.defaultloadscript:
6115       g.defaultLoadScript = value;
6116       break;
6117     case T.appletproxy:
6118       fm.setAppletProxy(value);
6119       break;
6120     case T.defaultdirectory:
6121       if (value == null)
6122         value = "";
6123       value = value.replace('\\', '/');
6124       g.defaultDirectory = value;
6125       break;
6126     case T.helppath:
6127       g.helpPath = value;
6128       break;
6129     case T.defaults:
6130       if (!value.equalsIgnoreCase("RasMol") && !value.equalsIgnoreCase("PyMOL"))
6131         value = "Jmol";
6132       setDefaultsType(value);
6133       break;
6134     case T.defaultcolorscheme:
6135       // only two are possible: "jmol" and "rasmol"
6136       setDefaultColors(value.equalsIgnoreCase("rasmol"));
6137       return;
6138     case T.picking:
6139       setPickingMode(value, 0);
6140       return;
6141     case T.pickingstyle:
6142       setPickingStyle(value, 0);
6143       return;
6144     case T.dataseparator:
6145       // just saving this
6146       break;
6147     default:
6148       if (key.toLowerCase().endsWith("callback")) {
6149         sm.setCallbackFunction(key,
6150             (value.length() == 0 || value.equalsIgnoreCase("none") ? null
6151                 : value));
6152         break;
6153       }
6154       if (!g.htNonbooleanParameterValues.containsKey(key.toLowerCase())) {
6155         g.setUserVariable(key, SV.newV(T.string, value));
6156         return;
6157       }
6158       // a few String parameters may not be tokenized. Save them anyway.
6159       // for example, defaultDirectoryLocal
6160       break;
6161     }
6162     g.setO(key, value);
6163   }
6164 
6165   @Override
setFloatProperty(String key, float value)6166   public void setFloatProperty(String key, float value) {
6167     if (Float.isNaN(value) || key == null || key.length() == 0)
6168       return;
6169     if (key.charAt(0) == '_') {
6170       g.setF(key, value);
6171       return;
6172     }
6173     int tok = T.getTokFromName(key);
6174     switch (T.getParamType(tok)) {
6175     case T.strparam:
6176       setStringPropertyTok(key, tok, "" + value);
6177       break;
6178     case T.booleanparam:
6179       setBooleanPropertyTok(key, tok, value != 0);
6180       break;
6181     case T.intparam:
6182       setIntPropertyTok(key, tok, (int) value);
6183       break;
6184     default:
6185       setFloatPropertyTok(key, tok, value);
6186     }
6187   }
6188 
setFloatPropertyTok(String key, int tok, float value)6189   private void setFloatPropertyTok(String key, int tok, float value) {
6190     switch (tok) {
6191     case T.cartoonblockheight:
6192       // 14.11.0
6193       g.cartoonBlockHeight = value;
6194       break;
6195     case T.modulationscale:
6196       // 14.0.1
6197       ms.setModulation(null, false, null, false);
6198       g.modulationScale = value = Math.max(0.1f, value);
6199       ms.setModulation(null, true, null, false);
6200       break;
6201     case T.particleradius:
6202       // 13.3.9
6203       g.particleRadius = Math.abs(value);
6204       break;
6205     case T.drawfontsize:
6206       // 13.3.6
6207       g.drawFontSize = value;
6208       break;
6209     case T.exportscale:
6210       // 13.1.19
6211       g.exportScale = value;
6212       break;
6213     case T.starwidth:
6214       // 13.1.15
6215       g.starWidth = value;
6216       break;
6217     case T.multiplebondradiusfactor:
6218       // 12.1.11
6219       g.multipleBondRadiusFactor = value;
6220       break;
6221     case T.multiplebondspacing:
6222       // 12.1.11
6223       g.multipleBondSpacing = value;
6224       break;
6225     case T.slabrange:
6226       tm.setSlabRange(value);
6227       break;
6228     case T.minimizationcriterion:
6229       g.minimizationCriterion = value;
6230       break;
6231     case T.gestureswipefactor:
6232       if (haveDisplay)
6233         acm.setGestureSwipeFactor(value);
6234       break;
6235     case T.mousedragfactor:
6236       if (haveDisplay)
6237         acm.setMouseDragFactor(value);
6238       break;
6239     case T.mousewheelfactor:
6240       if (haveDisplay)
6241         acm.setMouseWheelFactor(value);
6242       break;
6243     case T.strutlengthmaximum:
6244       // 11.9.21
6245       g.strutLengthMaximum = value;
6246       break;
6247     case T.strutdefaultradius:
6248       g.strutDefaultRadius = value;
6249       break;
6250     case T.navx:
6251       // 11.7.47
6252       setSpin("X", (int) value);
6253       break;
6254     case T.navy:
6255       setSpin("Y", (int) value);
6256       break;
6257     case T.navz:
6258       setSpin("Z", (int) value);
6259       break;
6260     case T.navfps:
6261       if (Float.isNaN(value))
6262         return;
6263       setSpin("FPS", (int) value);
6264       break;
6265     case T.loadatomdatatolerance:
6266       g.loadAtomDataTolerance = value;
6267       break;
6268     case T.hbondsangleminimum:
6269       // 11.7.9
6270       g.hbondsAngleMinimum = value;
6271       break;
6272     case T.hbondhxdistancemaximum:
6273       // 14.31.33
6274       g.hbondHXDistanceMaximum = value;
6275       break;
6276     case T.hbondnodistancemaximum:
6277       // 11.7.9
6278       g.hbondNODistanceMaximum = value;
6279       break;
6280     case T.pointgroupdistancetolerance:
6281       // 11.6.RC2//
6282       g.pointGroupDistanceTolerance = value;
6283       break;
6284     case T.pointgrouplineartolerance:
6285       g.pointGroupLinearTolerance = value;
6286       break;
6287     case T.ellipsoidaxisdiameter:
6288       g.ellipsoidAxisDiameter = value;
6289       break;
6290     case T.spinx:
6291       // /11.3.52//
6292       setSpin("x", (int) value);
6293       break;
6294     case T.spiny:
6295       setSpin("y", (int) value);
6296       break;
6297     case T.spinz:
6298       setSpin("z", (int) value);
6299       break;
6300     case T.spinfps:
6301       setSpin("fps", (int) value);
6302       break;
6303     case T.defaultdrawarrowscale:
6304       // /11.3.17//
6305       g.defaultDrawArrowScale = value;
6306       break;
6307     case T.defaulttranslucent:
6308       // /11.1///
6309       g.defaultTranslucent = value;
6310       break;
6311     case T.axesoffset:
6312       setAxesScale(tok, value);
6313       break;
6314     case T.axesscale:
6315       setAxesScale(tok, value);
6316       break;
6317     case T.visualrange:
6318       tm.visualRangeAngstroms = value;
6319       refresh(REFRESH_REPAINT, "set visualRange");
6320       break;
6321     case T.navigationdepth:
6322       setNavigationDepthPercent(value);
6323       break;
6324     case T.navigationspeed:
6325       g.navigationSpeed = value;
6326       break;
6327     case T.navigationslab:
6328       tm.setNavigationSlabOffsetPercent(value);
6329       break;
6330     case T.cameradepth:
6331       tm.setCameraDepthPercent(value, false);
6332       refresh(REFRESH_REPAINT, "set cameraDepth");
6333       // transformManager will set global value for us;
6334       return;
6335     case T.rotationradius:
6336       setRotationRadius(value, true);
6337       return;
6338     case T.hoverdelay:
6339       g.hoverDelayMs = (int) (value * 1000);
6340       break;
6341     case T.sheetsmoothing:
6342       // /11.0///
6343       g.sheetSmoothing = value;
6344       break;
6345     case T.dipolescale:
6346       value = checkFloatRange(value, -10, 10);
6347       g.dipoleScale = value;
6348       break;
6349     case T.stereodegrees:
6350       tm.setStereoDegrees(value);
6351       break;
6352     case T.vectorscale:
6353       // public -- no need to set
6354       setVectorScale(value);
6355       return;
6356     case T.vibrationperiod:
6357       // public -- no need to set
6358       setVibrationPeriod(value);
6359       return;
6360     case T.vibrationscale:
6361       // public -- no need to set
6362       setVibrationScale(value);
6363       return;
6364     case T.bondtolerance:
6365       setBondTolerance(value);
6366       return;
6367     case T.minbonddistance:
6368       setMinBondDistance(value);
6369       return;
6370     case T.scaleangstromsperinch:
6371       tm.setScaleAngstromsPerInch(value);
6372       break;
6373     case T.solventproberadius:
6374       value = checkFloatRange(value, 0, 10);
6375       g.solventProbeRadius = value;
6376       break;
6377     default:
6378       if (!g.htNonbooleanParameterValues.containsKey(key.toLowerCase())) {
6379         g.setUserVariable(key, SV.newF(value));
6380         return;
6381       }
6382     }
6383     g.setF(key, value);
6384   }
6385 
6386   @Override
setIntProperty(String key, int value)6387   public void setIntProperty(String key, int value) {
6388     if (value == Integer.MIN_VALUE || key == null || key.length() == 0)
6389       return;
6390     if (key.charAt(0) == '_') {
6391       g.setI(key, value);
6392       return;
6393     }
6394     int tok = T.getTokFromName(key);
6395     switch (T.getParamType(tok)) {
6396     case T.strparam:
6397       setStringPropertyTok(key, tok, "" + value);
6398       break;
6399     case T.booleanparam:
6400       setBooleanPropertyTok(key, tok, value != 0);
6401       break;
6402     case T.floatparam:
6403       setFloatPropertyTok(key, tok, value);
6404       break;
6405     default:
6406       setIntPropertyTok(key, tok, value);
6407     }
6408   }
6409 
setIntPropertyTok(String key, int tok, int value)6410   private void setIntPropertyTok(String key, int tok, int value) {
6411     switch (tok) {
6412     case T.minimizationmaxatoms:
6413       // 14.30.0
6414       g.minimizationMaxAtoms = value;
6415       break;
6416     case T.infofontsize:
6417       g.infoFontSize = Math.max(0, value);
6418       break;
6419     case T.contextdepthmax:
6420     case T.historylevel:
6421     case T.scriptreportinglevel:
6422       value = eval.setStatic(tok, value);
6423       break;
6424     case T.vectortrail:
6425       g.vectorTrail = value;
6426       break;
6427     case T.bondingversion:
6428       // 14.1.11
6429       value = (value == 0 ? Elements.RAD_COV_IONIC_OB1_100_1
6430           : Elements.RAD_COV_BODR_2014_02_22);
6431       g.bondingVersion = Elements.bondingVersion = value;
6432       break;
6433     case T.celshadingpower:
6434       // 13.3.9
6435       gdata.setCelPower(value);
6436       break;
6437     case T.ambientocclusion:
6438       // 13.3.9
6439       gdata.setAmbientOcclusion(value);
6440       break;
6441     case T.platformspeed:
6442       // 13.3.4
6443       g.platformSpeed = Math.min(Math.max(value, 0), 10); // 0 could mean "adjust as needed"
6444       break;
6445     case T.meshscale:
6446       // 12.3.29
6447       g.meshScale = value;
6448       break;
6449     case T.minpixelselradius:
6450       // 12.2.RC6
6451       g.minPixelSelRadius = value;
6452       break;
6453     case T.isosurfacepropertysmoothingpower:
6454       // 12.1.11
6455       g.isosurfacePropertySmoothingPower = value;
6456       break;
6457     case T.repaintwaitms:
6458       // 12.0.RC4
6459       g.repaintWaitMs = value;
6460       break;
6461     case T.smallmoleculemaxatoms:
6462       // 12.0.RC3
6463       g.smallMoleculeMaxAtoms = value;
6464       break;
6465     case T.minimizationsteps:
6466       g.minimizationSteps = value;
6467       break;
6468     case T.strutspacing:
6469       // 11.9.21
6470       g.strutSpacing = value;
6471       break;
6472     case T.phongexponent:
6473       // 11.9.13
6474       value = checkIntRange(value, 0, 1000);
6475       gdata.setPhongExponent(value);
6476       break;
6477     case T.helixstep:
6478       // 11.8.RC3
6479       g.helixStep = value;
6480       ms.haveStraightness = false;
6481       break;
6482     case T.dotscale:
6483       // 12.0.RC25
6484       g.dotScale = value;
6485       break;
6486     case T.dotdensity:
6487       // 11.6.RC2//
6488       g.dotDensity = value;
6489       break;
6490     case T.delaymaximumms:
6491       // 11.5.4//
6492       g.delayMaximumMs = value;
6493       break;
6494     case T.loglevel:
6495       // /11.3.52//
6496       Logger.setLogLevel(value);
6497       Logger.info("logging level set to " + value);
6498       g.setI("logLevel", value);
6499       if (eval != null)
6500         eval.setDebugging();
6501       return;
6502     case T.axesmode:
6503       setAxesMode(value == 2 ? T.axesunitcell
6504           : value == 1 ? T.axesmolecular : T.axeswindow);
6505       return;
6506     case T.strandcount:
6507       // /11.1///
6508       setStrandCount(0, value);
6509       return;
6510     case T.strandcountforstrands:
6511       setStrandCount(JC.SHAPE_STRANDS, value);
6512       return;
6513     case T.strandcountformeshribbon:
6514       setStrandCount(JC.SHAPE_MESHRIBBON, value);
6515       return;
6516     case T.perspectivemodel:
6517       // abandoned in 13.1.10
6518       //setPerspectiveModel(value);
6519       return;
6520     case T.showscript:
6521       g.scriptDelay = value;
6522       break;
6523     case T.specularpower:
6524       if (value < 0)
6525         value = checkIntRange(value, -10, -1);
6526       else
6527         value = checkIntRange(value, 0, 100);
6528       gdata.setSpecularPower(value);
6529       break;
6530     case T.specularexponent:
6531       value = checkIntRange(-value, -10, -1);
6532       gdata.setSpecularPower(value);
6533       break;
6534     case T.bondradiusmilliangstroms:
6535       setMarBond((short) value);
6536       // public method -- no need to set
6537       return;
6538     case T.specular:
6539       setBooleanPropertyTok(key, tok, value == 1);
6540       return;
6541     case T.specularpercent:
6542       value = checkIntRange(value, 0, 100);
6543       gdata.setSpecularPercent(value);
6544       break;
6545     case T.diffusepercent:
6546       value = checkIntRange(value, 0, 100);
6547       gdata.setDiffusePercent(value);
6548       break;
6549     case T.ambientpercent:
6550       value = checkIntRange(value, 0, 100);
6551       gdata.setAmbientPercent(value);
6552       break;
6553     case T.zdepth:
6554       tm.zDepthToPercent(value);
6555       break;
6556     case T.zslab:
6557       tm.zSlabToPercent(value);
6558       break;
6559     case T.depth:
6560       tm.depthToPercent(value);
6561       break;
6562     case T.slab:
6563       tm.slabToPercent(value);
6564       break;
6565     case T.zshadepower:
6566       g.zShadePower = value = Math.max(value, 0);
6567       break;
6568     case T.ribbonaspectratio:
6569       g.ribbonAspectRatio = value;
6570       break;
6571     case T.pickingspinrate:
6572       g.pickingSpinRate = (value < 1 ? 1 : value);
6573       break;
6574     case T.animationfps:
6575       setAnimationFps(value);
6576       return;
6577     case T.percentvdwatom:
6578       setPercentVdwAtom(value);
6579       break;
6580     case T.hermitelevel:
6581       g.hermiteLevel = value;
6582       break;
6583     case T.ellipsoiddotcount: // 11.5.30
6584     case T.propertyatomnumbercolumncount:
6585     case T.propertyatomnumberfield: // 11.6.RC16
6586     case T.propertydatacolumncount:
6587     case T.propertydatafield: // 11.1.31
6588       // just save in the hashtable, not in global
6589       break;
6590     default:
6591       // stateversion is not tokenized
6592       if (!g.htNonbooleanParameterValues.containsKey(key)) {
6593         g.setUserVariable(key, SV.newI(value));
6594         return;
6595       }
6596     }
6597     g.setI(key, value);
6598   }
6599 
checkIntRange(int value, int min, int max)6600   private static int checkIntRange(int value, int min, int max) {
6601     return (value < min ? min : value > max ? max : value);
6602   }
6603 
checkFloatRange(float value, float min, float max)6604   private static float checkFloatRange(float value, float min, float max) {
6605     return (value < min ? min : value > max ? max : value);
6606   }
6607 
6608   @Override
setBooleanProperty(String key, boolean value)6609   public void setBooleanProperty(String key, boolean value) {
6610     if (key == null || key.length() == 0)
6611       return;
6612     if (key.charAt(0) == '_') {
6613       g.setB(key, value);
6614       return;
6615     }
6616     int tok = T.getTokFromName(key);
6617     switch (T.getParamType(tok)) {
6618     case T.strparam:
6619       setStringPropertyTok(key, tok, "");
6620       break;
6621     case T.intparam:
6622       setIntPropertyTok(key, tok, value ? 1 : 0);
6623       break;
6624     case T.floatparam:
6625       setFloatPropertyTok(key, tok, value ? 1 : 0);
6626       break;
6627     default:
6628       setBooleanPropertyTok(key, tok, value);
6629     }
6630   }
6631 
setBooleanPropertyTok(String key, int tok, boolean value)6632   private void setBooleanPropertyTok(String key, int tok, boolean value) {
6633     boolean doRepaint = true;
6634     switch (tok) {
6635     case T.checkcir:
6636       // 14.31.40
6637       g.checkCIR = value;
6638       if (value) {
6639         checkCIR(true);
6640       }
6641       break;
6642     case T.ciprule6full:
6643       // 14.29.14
6644       g.cipRule6Full = value;
6645       break;
6646     case T.autoplaymovie:
6647       // 14.29.2
6648       g.autoplayMovie = value;
6649       break;
6650     case T.allowaudio:
6651       // 14.29.2
6652       value = false;
6653       // cannot be set TRUE once set FALSE
6654       g.allowAudio = value;
6655       break;
6656     case T.nodelay:
6657       // 14.21.1
6658       g.noDelay = value;
6659       break;
6660     case T.nbocharges:
6661       // 14.8.2
6662       g.nboCharges = value;
6663       break;
6664     case T.hiddenlinesdashed:
6665       // 14.5.1
6666       g.hiddenLinesDashed = value;
6667       break;
6668     case T.multiplebondbananas:
6669       // 14.3.15
6670       g.multipleBondBananas = value;
6671       break;
6672     case T.modulateoccupancy:
6673       // 12.0.RC6
6674       g.modulateOccupancy = value;
6675       break;
6676     case T.legacyjavafloat:
6677       // 14.3.5
6678       g.legacyJavaFloat = value;
6679       break;
6680     case T.showmodvecs:
6681       // 14.3.5
6682       g.showModVecs = value;
6683       break;
6684     case T.showunitcelldetails:
6685       // 14.1.16
6686       g.showUnitCellDetails = value;
6687       break;
6688     case T.fractionalrelative:
6689       // REMOVED in 14.1.16
6690       // an odd quantity -- relates specifically to scripts commands
6691       // using fx, fy, fz, fxyz, ux, uy, uz, uxyz, and cell=
6692       // It should never have been set in state, as it has nothing to do with the state
6693       // Its use with cell= was never documented.
6694       // It makes no sense to change the unit cell and not change the
6695       // meaning of fx, fy, fz, fxyz, ux, uy, uz, uxyz, and cell=
6696       // Its presence caused unitcell [{origin} {a} {b} {c}] to fail.
6697 
6698       //g.fractionalRelative = value;
6699       doRepaint = false;
6700       break;
6701     case T.vectorscentered:
6702       // 14.1.15
6703       g.vectorsCentered = value;
6704       break;
6705     case T.cartoonblocks:
6706       // 14.11.0
6707       g.cartoonBlocks = value;
6708       break;
6709     case T.cartoonsteps:
6710       // 14.1.14
6711       g.cartoonSteps = value;
6712       break;
6713     case T.cartoonribose:
6714       // 14.1.8
6715       g.cartoonRibose = value;
6716       //      if (value && getBoolean(T.cartoonbaseedges))
6717       //        setBooleanPropertyTok("cartoonBaseEdges", T.cartoonbaseedges, false);
6718       break;
6719     case T.ellipsoidarrows:
6720       // 13.1.17 TRUE for little points on ellipsoids showing sign of
6721       // eigenvalues (in --> negative; out --> positive)
6722       g.ellipsoidArrows = value;
6723       break;
6724     case T.translucent:
6725       // 13.1.17 false -> translucent objects are opaque among themselves (Pymol transparency_mode 2)
6726       g.translucent = value;
6727       break;
6728     case T.cartoonladders:
6729       // 13.1.15
6730       g.cartoonLadders = value;
6731       break;
6732     case T.twistedsheets:
6733       boolean b = g.twistedSheets;
6734       g.twistedSheets = value;
6735       if (b != value)
6736         checkCoordinatesChanged();
6737       break;
6738     case T.celshading:
6739       // 13.1.13
6740       gdata.setCel(value);
6741       break;
6742     case T.cartoonsfancy:
6743       // 12.3.7
6744       g.cartoonFancy = value;
6745       break;
6746     case T.showtiming:
6747       // 12.3.6
6748       g.showTiming = value;
6749       break;
6750     case T.vectorsymmetry:
6751       // 12.3.2
6752       g.vectorSymmetry = value;
6753       break;
6754     case T.isosurfacekey:
6755       // 12.2.RC5
6756       g.isosurfaceKey = value;
6757       break;
6758     case T.partialdots:
6759       // Jmol 12.1.46
6760       g.partialDots = value;
6761       break;
6762     case T.legacyautobonding:
6763       g.legacyAutoBonding = value;
6764       break;
6765     case T.defaultstructuredssp:
6766       g.defaultStructureDSSP = value;
6767       break;
6768     case T.dsspcalchydrogen:
6769       g.dsspCalcHydrogen = value;
6770       break;
6771     case T.allowmodelkit:
6772       // 11.12.RC15
6773       g.allowModelkit = value;
6774       if (!value)
6775         setModelKitMode(false);
6776       break;
6777     case T.modelkitmode:
6778       setModelKitMode(value);
6779       break;
6780     case T.multiprocessor:
6781       // 12.0.RC6
6782       g.multiProcessor = value && (nProcessors > 1);
6783       break;
6784     case T.monitorenergy:
6785       // 12.0.RC6
6786       g.monitorEnergy = value;
6787       break;
6788     case T.hbondsrasmol:
6789       // 12.0.RC3
6790       g.hbondsRasmol = value;
6791       break;
6792     case T.minimizationrefresh:
6793       g.minimizationRefresh = value;
6794       break;
6795     case T.minimizationsilent:
6796       // 12.0.RC5
6797       g.minimizationSilent = value;
6798       break;
6799     //case T.usearcball:
6800     //g.useArcBall = value;
6801     //break;
6802     case T.iskiosk:
6803       // 11.9.29
6804       // 12.2.9, 12.3.9: no false here, because it's a one-time setting
6805       if (value) {
6806         isKiosk = true;
6807         g.disablePopupMenu = true;
6808         if (display != null)
6809           apiPlatform.setTransparentCursor(display);
6810       }
6811       break;
6812     // 11.9.28
6813     case T.waitformoveto:
6814       g.waitForMoveTo = value;
6815       break;
6816     case T.logcommands:
6817       g.logCommands = true;
6818       break;
6819     case T.loggestures:
6820       g.logGestures = true;
6821       break;
6822     case T.allowmultitouch:
6823       // 11.9.24
6824       g.allowMultiTouch = value;
6825       break;
6826     case T.preservestate:
6827       // 11.9.23
6828       g.preserveState = value;
6829       ms.setPreserveState(value);
6830       undoClear();
6831       break;
6832     case T.strutsmultiple:
6833       // 11.9.23
6834       g.strutsMultiple = value;
6835       break;
6836     case T.filecaching:
6837       // 11.9.21
6838       // not implemented -- application only -- CANNOT BE SET BY STATE
6839       break;
6840     case T.slabbyatom:
6841       // 11.9.19
6842       g.slabByAtom = value;
6843       break;
6844     case T.slabbymolecule:
6845       // 11.9.18
6846       g.slabByMolecule = value;
6847       break;
6848     case T.saveproteinstructurestate:
6849       // 11.9.15
6850       g.saveProteinStructureState = value;
6851       break;
6852     case T.allowgestures:
6853       g.allowGestures = value;
6854       break;
6855     case T.imagestate:
6856       // 11.8.RC6
6857       g.imageState = value;
6858       break;
6859     case T.useminimizationthread:
6860       // 11.7.40
6861       g.useMinimizationThread = value;
6862       break;
6863     // case Token.autoloadorientation:
6864     // // 11.7.30; removed in 12.0.RC10 -- use FILTER "NoOrient"
6865     // global.autoLoadOrientation = value;
6866     // break;
6867     case T.allowkeystrokes:
6868       // 11.7.24
6869       //      if (g.disablePopupMenu)
6870       //        value = false;
6871       g.allowKeyStrokes = value;
6872       break;
6873     case T.dragselected:
6874       // 11.7.24
6875       g.dragSelected = value;
6876       showSelected = false;
6877       break;
6878     case T.showkeystrokes:
6879       g.showKeyStrokes = value;
6880       break;
6881     case T.fontcaching:
6882       // 11.7.10
6883       g.fontCaching = value;
6884       break;
6885     case T.atompicking:
6886       // 11.6.RC13
6887       g.atomPicking = value;
6888       break;
6889     case T.bondpicking:
6890       // 11.6.RC13
6891       highlight(null);
6892       g.bondPicking = value;
6893       break;
6894     case T.selectallmodels:
6895       // 11.5.52
6896       g.selectAllModels = value;
6897       if (value)
6898         slm.setSelectionSubset(null);
6899       else
6900         am.setSelectAllSubset(false);
6901       break;
6902     case T.messagestylechime:
6903       // 11.5.39
6904       g.messageStyleChime = value;
6905       break;
6906     case T.pdbsequential:
6907       g.pdbSequential = value;
6908       break;
6909     case T.pdbaddhydrogens:
6910       g.pdbAddHydrogens = value;
6911       break;
6912     case T.pdbgetheader:
6913       g.pdbGetHeader = value;
6914       break;
6915     case T.ellipsoidaxes:
6916       g.ellipsoidAxes = value;
6917       break;
6918     case T.ellipsoidarcs:
6919       g.ellipsoidArcs = value;
6920       break;
6921     case T.ellipsoidball:
6922       g.ellipsoidBall = value;
6923       break;
6924     case T.ellipsoiddots:
6925       g.ellipsoidDots = value;
6926       break;
6927     case T.ellipsoidfill:
6928       g.ellipsoidFill = value;
6929       break;
6930     case T.fontscaling:
6931       // 11.5.4
6932       g.fontScaling = value;
6933       break;
6934     case T.syncmouse:
6935       // 11.3.56
6936       setSyncTarget(0, value);
6937       break;
6938     case T.syncscript:
6939       setSyncTarget(1, value);
6940       break;
6941     case T.wireframerotation:
6942       // 11.3.55
6943       g.wireframeRotation = value;
6944       break;
6945     case T.isosurfacepropertysmoothing:
6946       // 11.3.46
6947       g.isosurfacePropertySmoothing = value;
6948       break;
6949     case T.drawpicking:
6950       // 11.3.43
6951       g.drawPicking = value;
6952       break;
6953     case T.antialiasdisplay:
6954       // 11.3.36
6955     case T.antialiastranslucent:
6956     case T.antialiasimages:
6957       setAntialias(tok, value);
6958       break;
6959     case T.smartaromatic:
6960       // 11.3.29
6961       g.smartAromatic = value;
6962       break;
6963     case T.applysymmetrytobonds:
6964       // 11.1.29
6965       setApplySymmetryToBonds(value);
6966       break;
6967     case T.appendnew:
6968       // 11.1.22
6969       g.appendNew = value;
6970       break;
6971     case T.autofps:
6972       g.autoFps = value;
6973       break;
6974     case T.usenumberlocalization:
6975       // 11.1.21
6976       DF.setUseNumberLocalization(g.useNumberLocalization = value);
6977       break;
6978     case T.showfrank:
6979     case T.frank:
6980       key = "showFrank";
6981       setFrankOn(value);
6982       // 11.1.20
6983       break;
6984     case T.solvent:
6985       key = "solventProbe";
6986       g.dotSolvent = value;
6987       break;
6988     case T.solventprobe:
6989       g.dotSolvent = value;
6990       break;
6991     case T.allowrotateselected:
6992       // 11.1.14
6993       g.allowRotateSelected = value;
6994       break;
6995     case T.allowmoveatoms:
6996       // 12.1.21
6997       //setBooleanProperty("allowRotateSelected", value);
6998       //setBooleanProperty("dragSelected", value);
6999       g.allowMoveAtoms = value;
7000       showSelected = false;
7001       break;
7002     case T.showscript:
7003       // /11.1.13///
7004       setIntPropertyTok("showScript", tok, value ? 1 : 0);
7005       return;
7006     case T.allowembeddedscripts:
7007       // /11.1///
7008       g.allowEmbeddedScripts = value;
7009       break;
7010     case T.navigationperiodic:
7011       g.navigationPeriodic = value;
7012       break;
7013     case T.zshade:
7014       tm.setZShadeEnabled(value);
7015       return;
7016     case T.drawhover:
7017       if (haveDisplay)
7018         g.drawHover = value;
7019       break;
7020     case T.navigationmode:
7021       setNavigationMode(value);
7022       break;
7023     case T.navigatesurface:
7024       // was experimental; abandoned in 13.1.10
7025       return;//global.navigateSurface = value;
7026     //break;
7027     case T.hidenavigationpoint:
7028       g.hideNavigationPoint = value;
7029       break;
7030     case T.shownavigationpointalways:
7031       g.showNavigationPointAlways = value;
7032       break;
7033     case T.refreshing:
7034       // /11.0///
7035       setRefreshing(value);
7036       break;
7037     case T.jmolinjspecview:
7038       g.jmolInJSpecView = value;
7039       break;
7040     case T.justifymeasurements:
7041       g.justifyMeasurements = value;
7042       break;
7043     case T.ssbondsbackbone:
7044       g.ssbondsBackbone = value;
7045       break;
7046     case T.hbondsbackbone:
7047       g.hbondsBackbone = value;
7048       break;
7049     case T.hbondssolid:
7050       g.hbondsSolid = value;
7051       break;
7052     case T.specular:
7053       gdata.setSpecular(value);
7054       break;
7055     case T.slabenabled:
7056       // Eval.slab
7057       tm.setSlabEnabled(value); // refresh?
7058       return;
7059     case T.zoomenabled:
7060       tm.setZoomEnabled(value);
7061       return;
7062     case T.highresolution:
7063       g.highResolutionFlag = value;
7064       break;
7065     case T.tracealpha:
7066       g.traceAlpha = value;
7067       break;
7068     case T.zoomlarge:
7069       g.zoomLarge = value;
7070       tm.setZoomHeight(g.zoomHeight, value);
7071       break;
7072     case T.zoomheight:
7073       g.zoomHeight = value;
7074       tm.setZoomHeight(value, g.zoomLarge);
7075       break;
7076     case T.languagetranslation:
7077       GT.setDoTranslate(value);
7078       break;
7079     case T.hidenotselected:
7080       slm.setHideNotSelected(value);
7081       break;
7082     case T.scriptqueue:
7083       setScriptQueue(value);
7084       break;
7085     case T.dotsurface:
7086       g.dotSurface = value;
7087       break;
7088     case T.dotsselectedonly:
7089       g.dotsSelectedOnly = value;
7090       break;
7091     case T.selectionhalos:
7092       setSelectionHalosEnabled(value);
7093       break;
7094     case T.selecthydrogen:
7095       g.rasmolHydrogenSetting = value;
7096       break;
7097     case T.selecthetero:
7098       g.rasmolHeteroSetting = value;
7099       break;
7100     case T.showmultiplebonds:
7101       g.showMultipleBonds = value;
7102       break;
7103     case T.showhiddenselectionhalos:
7104       g.showHiddenSelectionHalos = value;
7105       break;
7106     case T.windowcentered:
7107       tm.setWindowCentered(value);
7108       break;
7109     case T.displaycellparameters:
7110       g.displayCellParameters = value;
7111       break;
7112     case T.testflag1:
7113       g.testFlag1 = value;
7114       break;
7115     case T.testflag2:
7116       g.testFlag2 = value;
7117       break;
7118     case T.testflag3:
7119       g.testFlag3 = value;
7120       break;
7121     case T.testflag4:
7122       jmolTest();
7123       g.testFlag4 = value;
7124       break;
7125     case T.ribbonborder:
7126       g.ribbonBorder = value;
7127       break;
7128     case T.cartoonbaseedges:
7129       g.cartoonBaseEdges = value;
7130       //      if (value && getBoolean(T.cartoonribose))
7131       //        setBooleanPropertyTok("cartoonRibose", T.cartoonribose, false);
7132       break;
7133     case T.cartoonrockets:
7134       g.cartoonRockets = value;
7135       break;
7136     case T.rocketbarrels:
7137       g.rocketBarrels = value;
7138       break;
7139     case T.greyscalerendering:
7140       gdata.setGreyscaleMode(g.greyscaleRendering = value);
7141       break;
7142     case T.measurementlabels:
7143       g.measurementLabels = value;
7144       break;
7145     case T.axeswindow:
7146     case T.axesmolecular:
7147     case T.axesunitcell:
7148       setAxesMode(tok);
7149       return;
7150     case T.axesorientationrasmol:
7151       // public; no need to set here
7152       setAxesOrientationRasmol(value);
7153       return;
7154     case T.colorrasmol:
7155       setStringPropertyTok("defaultcolorscheme", T.defaultcolorscheme,
7156           value ? "rasmol" : "jmol");
7157       return;
7158     case T.debugscript:
7159       setDebugScript(value);
7160       return;
7161     case T.perspectivedepth:
7162       setPerspectiveDepth(value);
7163       return;
7164     case T.autobond:
7165       // public - no need to set
7166       setAutoBond(value);
7167       return;
7168     case T.showaxes:
7169       setShowAxes(value);
7170       return;
7171     case T.showboundbox:
7172       setShowBbcage(value);
7173       return;
7174     case T.showhydrogens:
7175       setShowHydrogens(value);
7176       return;
7177     case T.showmeasurements:
7178       setShowMeasurements(value);
7179       return;
7180     case T.showunitcell:
7181       setShowUnitCell(value);
7182       return;
7183     case T.bondmodeor:
7184       doRepaint = false;
7185       g.bondModeOr = value;
7186       break;
7187     case T.zerobasedxyzrasmol:
7188       doRepaint = false;
7189       g.zeroBasedXyzRasmol = value;
7190       reset(true);
7191       break;
7192     case T.rangeselected:
7193       doRepaint = false;
7194       g.rangeSelected = value;
7195       break;
7196     case T.measureallmodels:
7197       doRepaint = false;
7198       g.measureAllModels = value;
7199       break;
7200     case T.statusreporting:
7201       doRepaint = false;
7202       // not part of the state
7203       sm.allowStatusReporting = value;
7204       break;
7205     case T.chaincasesensitive:
7206       doRepaint = false;
7207       g.chainCaseSensitive = value;
7208       break;
7209     case T.hidenameinpopup:
7210       doRepaint = false;
7211       g.hideNameInPopup = value;
7212       break;
7213     case T.disablepopupmenu:
7214       doRepaint = false;
7215       g.disablePopupMenu = value;
7216       break;
7217     case T.forceautobond:
7218       doRepaint = false;
7219       g.forceAutoBond = value;
7220       break;
7221     default:
7222       if (!g.htBooleanParameterFlags.containsKey(key.toLowerCase())) {
7223         g.setUserVariable(key, SV.getBoolean(value));
7224         return;
7225       }
7226     }
7227     g.setB(key, value);
7228     if (doRepaint)
7229       setTainted(true);
7230   }
7231 
7232   /*
7233    * public void setFileCacheDirectory(String fileOrDir) { if (fileOrDir ==
7234    * null) fileOrDir = ""; global._fileCache = fileOrDir; }
7235    *
7236    * String getFileCacheDirectory() { if (!global._fileCaching) return null;
7237    * return global._fileCache; }
7238    */
7239 
setModelKitMode(boolean value)7240   private void setModelKitMode(boolean value) {
7241     if (acm == null || !allowScripting)
7242       return;
7243     if (value || g.modelKitMode) {
7244       setPickingMode(null, value ? ActionManager.PICKING_ASSIGN_BOND
7245           : ActionManager.PICKING_IDENTIFY);
7246       setPickingMode(null, value ? ActionManager.PICKING_ASSIGN_ATOM
7247           : ActionManager.PICKING_IDENTIFY);
7248     }
7249     boolean isChange = (g.modelKitMode != value);
7250     g.modelKitMode = value;
7251     g.setB("modelkitmode", value); // in case there is a callback before this completes
7252     highlight(null);
7253     if (value) {
7254       ModelKitPopup kit = getModelkit(false);
7255       setNavigationMode(false);
7256       selectAll();
7257       // setShapeProperty(JmolConstants.SHAPE_LABELS, "color", "RED");
7258       kit.setProperty("atomType", "C");
7259       kit.setProperty("bondType", "p");
7260       if (!isApplet)
7261         popupMenu(10, 0, 'm'); // was 0?
7262       if (isChange)
7263         sm.setStatusModelKit(1);
7264       g.modelKitMode = true;
7265       if (ms.ac == 0)
7266         zap(false, true, true);
7267       else if (am.cmi >= 0 && getModelUndeletedAtomsBitSet(am.cmi).isEmpty()) {
7268         Map<String, Object> htParams = new Hashtable<String, Object>();
7269         htParams.put("appendToModelIndex", Integer.valueOf(am.cmi));
7270         loadDefaultModelKitModel(htParams);
7271       }
7272     } else {
7273       acm.setPickingMode(ActionManager.PICKING_MK_RESET);
7274       setStringProperty("pickingStyle", "toggle");
7275       setBooleanProperty("bondPicking", false);
7276       if (isChange) {
7277         sm.setStatusModelKit(0);
7278       }
7279     }
7280   }
7281 
setSmilesString(String s)7282   public void setSmilesString(String s) {
7283     if (s == null)
7284       g.removeParam("_smilesString");
7285     else
7286       g.setO("_smilesString", s);
7287   }
7288 
removeUserVariable(String key)7289   public void removeUserVariable(String key) {
7290     g.removeUserVariable(key);
7291     if (key.endsWith("callback"))
7292       sm.setCallbackFunction(key, null);
7293   }
7294 
jmolTest()7295   private void jmolTest() {
7296     /*
7297      * Vector v = new Vector(); Vector m = new Vector(); v.add(m);
7298      * m.add("MODEL     2");m.add(
7299      * "HETATM    1 H1   UNK     1       2.457   0.000   0.000  1.00  0.00           H  "
7300      * );m.add(
7301      * "HETATM    2 C1   UNK     1       1.385   0.000   0.000  1.00  0.00           C  "
7302      * );m.add(
7303      * "HETATM    3 C2   UNK     1      -1.385  -0.000   0.000  1.00  0.00           C  "
7304      * ); v.add(new String[] { "MODEL     2",
7305      * "HETATM    1 H1   UNK     1       2.457   0.000   0.000  1.00  0.00           H  "
7306      * ,
7307      * "HETATM    2 C1   UNK     1       1.385   0.000   0.000  1.00  0.00           C  "
7308      * ,
7309      * "HETATM    3 C2   UNK     1      -1.385  -0.000   0.000  1.00  0.00           C  "
7310      * , }); v.add(new String[] {"3","testing","C 0 0 0","O 0 1 0","N 0 0 1"} );
7311      * v.add("3\ntesting\nC 0 0 0\nO 0 1 0\nN 0 0 1\n"); loadInline(v, false);
7312      */
7313   }
7314 
showParameter(String key, boolean ifNotSet, int nMax)7315   public void showParameter(String key, boolean ifNotSet, int nMax) {
7316     String sv = "" + g.getParameterEscaped(key, nMax);
7317     if (ifNotSet || sv.indexOf("<not defined>") < 0)
7318       showString(key + " = " + sv, false);
7319   }
7320 
showString(String str, boolean isPrint)7321   public void showString(String str, boolean isPrint) {
7322     if (!isJS && isScriptQueued() && (!isSilent || isPrint)
7323         && !"\0".equals(str)) {
7324       Logger.warn(str); // warn here because we still want to be be able to turn this off
7325     }
7326     scriptEcho(str);
7327   }
7328 
getAllSettings(String prefix)7329   public String getAllSettings(String prefix) {
7330     return getStateCreator().getAllSettings(prefix);
7331   }
7332 
getBindingInfo(String qualifiers)7333   public String getBindingInfo(String qualifiers) {
7334     return (haveDisplay ? acm.getBindingInfo(qualifiers) : "");
7335   }
7336 
7337   // ////// flags and settings ////////
7338 
getIsosurfacePropertySmoothing(boolean asPower)7339   public int getIsosurfacePropertySmoothing(boolean asPower) {
7340     // Eval
7341     return (asPower ? g.isosurfacePropertySmoothingPower
7342         : g.isosurfacePropertySmoothing ? 1 : 0);
7343   }
7344 
setNavigationDepthPercent(float percent)7345   public void setNavigationDepthPercent(float percent) {
7346     tm.setNavigationDepthPercent(percent);
7347     refresh(REFRESH_REPAINT, "set navigationDepth");
7348   }
7349 
getShowNavigationPoint()7350   public boolean getShowNavigationPoint() {
7351     if (!g.navigationMode/* || !tm.canNavigate()*/)
7352       return false;
7353     return (tm.isNavigating() && !g.hideNavigationPoint
7354         || g.showNavigationPointAlways || getInMotion(true));
7355   }
7356 
7357   @Override
setPerspectiveDepth(boolean perspectiveDepth)7358   public void setPerspectiveDepth(boolean perspectiveDepth) {
7359     // setBooleanProperty
7360     // stateManager.setCrystallographicDefaults
7361     // app preferences dialog
7362     tm.setPerspectiveDepth(perspectiveDepth);
7363   }
7364 
7365   @Override
setAxesOrientationRasmol(boolean TF)7366   public void setAxesOrientationRasmol(boolean TF) {
7367     // app PreferencesDialog
7368     // stateManager
7369     // setBooleanproperty
7370     /*
7371      * *************************************************************** RasMol
7372      * has the +Y axis pointing down And rotations about the y axis are
7373      * left-handed setting this flag makes Jmol mimic this behavior
7374      *
7375      * All versions of Jmol prior to 11.5.51 incompletely implement this flag.
7376      * All versions of Jmol between 11.5.51 and 12.2.4 incorrectly implement this flag.
7377      * Really all it is just a flag to tell Eval to flip the sign of the Z
7378      * rotation when specified specifically as "rotate/spin Z 30".
7379      *
7380      * In principal, we could display the axis opposite as well, but that is
7381      * only aesthetic and not at all justified if the axis is molecular.
7382      * **************************************************************
7383      */
7384     g.setB("axesOrientationRasmol", TF);
7385     g.axesOrientationRasmol = TF;
7386     reset(true);
7387   }
7388 
setAxesScale(int tok, float val)7389   private void setAxesScale(int tok, float val) {
7390     val = checkFloatRange(val, -100, 100);
7391     if (tok == T.axesoffset)
7392       g.axesOffset = val;
7393     else
7394       g.axesScale = val;
7395     axesAreTainted = true;
7396   }
7397 
setAxesMode(int mode)7398   void setAxesMode(int mode) {
7399     g.axesMode = mode;
7400     axesAreTainted = true;
7401     switch (mode) {
7402     case T.axesunitcell:
7403       // stateManager
7404       // setBooleanproperty
7405       g.removeParam("axesmolecular");
7406       g.removeParam("axeswindow");
7407       g.setB("axesUnitcell", true);
7408       mode = 2;
7409       break;
7410     case T.axesmolecular:
7411       g.removeParam("axesunitcell");
7412       g.removeParam("axeswindow");
7413       g.setB("axesMolecular", true);
7414       mode = 1;
7415       break;
7416     case T.axeswindow:
7417       g.removeParam("axesunitcell");
7418       g.removeParam("axesmolecular");
7419       g.setB("axesWindow", true);
7420       mode = 0;
7421     }
7422     g.setI("axesMode", mode);
7423   }
7424 
7425   private boolean selectionHalosEnabled = false;
7426 
getSelectionHalosEnabled()7427   public boolean getSelectionHalosEnabled() {
7428     return selectionHalosEnabled;
7429   }
7430 
setSelectionHalosEnabled(boolean TF)7431   public void setSelectionHalosEnabled(boolean TF) {
7432     if (selectionHalosEnabled == TF)
7433       return;
7434     g.setB("selectionHalos", TF);
7435     shm.loadShape(JC.SHAPE_HALOS);
7436     selectionHalosEnabled = TF;
7437   }
7438 
getShowSelectedOnce()7439   public boolean getShowSelectedOnce() {
7440     boolean flag = showSelected;
7441     showSelected = false;
7442     return flag;
7443   }
7444 
setStrandCount(int type, int value)7445   private void setStrandCount(int type, int value) {
7446     value = checkIntRange(value, 0, 20);
7447     switch (type) {
7448     case JC.SHAPE_STRANDS:
7449       g.strandCountForStrands = value;
7450       break;
7451     case JC.SHAPE_MESHRIBBON:
7452       g.strandCountForMeshRibbon = value;
7453       break;
7454     default:
7455       g.strandCountForStrands = value;
7456       g.strandCountForMeshRibbon = value;
7457       break;
7458     }
7459     g.setI("strandCount", value);
7460     g.setI("strandCountForStrands", g.strandCountForStrands);
7461     g.setI("strandCountForMeshRibbon", g.strandCountForMeshRibbon);
7462   }
7463 
getStrandCount(int type)7464   public int getStrandCount(int type) {
7465     return (type == JC.SHAPE_STRANDS ? g.strandCountForStrands
7466         : g.strandCountForMeshRibbon);
7467   }
7468 
setNavigationMode(boolean TF)7469   public void setNavigationMode(boolean TF) {
7470     g.navigationMode = TF;
7471     tm.setNavigationMode(TF);
7472   }
7473 
7474   @Override
setAutoBond(boolean TF)7475   public void setAutoBond(boolean TF) {
7476     // setBooleanProperties
7477     g.setB("autobond", TF);
7478     g.autoBond = TF;
7479   }
7480 
makeConnections(float minDistance, float maxDistance, int order, int connectOperation, BS bsA, BS bsB, BS bsBonds, boolean isBonds, boolean addGroup, float energy)7481   public int[] makeConnections(float minDistance, float maxDistance, int order,
7482                                int connectOperation, BS bsA, BS bsB, BS bsBonds,
7483                                boolean isBonds, boolean addGroup,
7484                                float energy) {
7485     // eval
7486     clearModelDependentObjects();
7487     // removed in 12.3.2 and 12.2.1; cannot remember why this was important
7488     // we aren't removing atoms, just bonds. So who cares in terms of measurements?
7489     // clearAllMeasurements(); // necessary for serialization (??)
7490     clearMinimization();
7491     return ms.makeConnections(minDistance, maxDistance, order, connectOperation,
7492         bsA, bsB, bsBonds, isBonds, addGroup, energy);
7493   }
7494 
7495   @Override
rebond()7496   public void rebond() {
7497     // PreferencesDialog
7498     rebondState(false);
7499   }
7500 
rebondState(boolean isStateScript)7501   public void rebondState(boolean isStateScript) {
7502     // Eval CONNECT
7503     clearModelDependentObjects();
7504     ms.deleteAllBonds();
7505     boolean isLegacy = isStateScript && g.legacyAutoBonding;
7506     ms.autoBondBs4(null, null, null, null, getMadBond(), isLegacy);
7507     addStateScript((isLegacy
7508         ? "set legacyAutoBonding TRUE;connect;set legacyAutoBonding FALSE;"
7509         : "connect;"), false, true);
7510   }
7511 
7512   // ///////////////////////////////////////////////////////////////
7513   // delegated to stateManager
7514   // ///////////////////////////////////////////////////////////////
7515 
7516   @Override
setPercentVdwAtom(int value)7517   public void setPercentVdwAtom(int value) {
7518     g.setI("percentVdwAtom", value);
7519     g.percentVdwAtom = value;
7520     rd.value = value / 100f;
7521     rd.factorType = EnumType.FACTOR;
7522     rd.vdwType = VDW.AUTO;
7523     shm.setShapeSizeBs(JC.SHAPE_BALLS, 0, rd, null);
7524   }
7525 
7526   @Override
getMadBond()7527   public short getMadBond() {
7528     return (short) (g.bondRadiusMilliAngstroms * 2);
7529   }
7530 
7531   @Override
setShowHydrogens(boolean TF)7532   public void setShowHydrogens(boolean TF) {
7533     // PreferencesDialog
7534     // setBooleanProperty
7535     g.setB("showHydrogens", TF);
7536     g.showHydrogens = TF;
7537   }
7538 
setShowBbcage(boolean value)7539   public void setShowBbcage(boolean value) {
7540     setObjectMad10(JC.SHAPE_BBCAGE, "boundbox", (short) (value ? -4 : 0));
7541     g.setB("showBoundBox", value);
7542   }
7543 
getShowBbcage()7544   public boolean getShowBbcage() {
7545     return getObjectMad10(StateManager.OBJ_BOUNDBOX) != 0;
7546   }
7547 
setShowUnitCell(boolean value)7548   public void setShowUnitCell(boolean value) {
7549     setObjectMad10(JC.SHAPE_UCCAGE, "unitcell", (short) (value ? -2 : 0));
7550     g.setB("showUnitCell", value);
7551   }
7552 
getShowUnitCell()7553   public boolean getShowUnitCell() {
7554     return getObjectMad10(StateManager.OBJ_UNITCELL) != 0;
7555   }
7556 
setShowAxes(boolean value)7557   public void setShowAxes(boolean value) {
7558     setObjectMad10(JC.SHAPE_AXES, "axes", (short) (value ? -2 : 0));
7559     g.setB("showAxes", value);
7560   }
7561 
getShowAxes()7562   public boolean getShowAxes() {
7563     return getObjectMad10(StateManager.OBJ_AXIS1) != 0;
7564   }
7565 
7566   public boolean frankOn = true;
7567   public boolean noFrankEcho = true; // set when Echo bottom right renders
7568 
7569   @Override
setFrankOn(boolean TF)7570   public void setFrankOn(boolean TF) {
7571     if (isPreviewOnly)
7572       TF = false;
7573     frankOn = TF;
7574     setObjectMad10(JC.SHAPE_FRANK, "frank", (short) (TF ? 1 : 0));
7575   }
7576 
getShowFrank()7577   public boolean getShowFrank() {
7578     if (isPreviewOnly || isApplet && creatingImage)
7579       return false;
7580     // Java remote signed applet only?
7581     return (isSignedApplet && !isSignedAppletLocal && !isJS || frankOn);
7582   }
7583 
7584   @Override
setShowMeasurements(boolean TF)7585   public void setShowMeasurements(boolean TF) {
7586     // setbooleanProperty
7587     g.setB("showMeasurements", TF);
7588     g.showMeasurements = TF;
7589   }
7590 
setUnits(String units, boolean isDistance)7591   public void setUnits(String units, boolean isDistance) {
7592     // stateManager
7593     // Eval
7594     g.setUnits(units);
7595     if (isDistance) {
7596       g.setUnits(units);
7597       setShapeProperty(JC.SHAPE_MEASURES, "reformatDistances", null);
7598     } else {
7599 
7600     }
7601   }
7602 
7603   @Override
setRasmolDefaults()7604   public void setRasmolDefaults() {
7605     setDefaultsType("RasMol");
7606   }
7607 
7608   @Override
setJmolDefaults()7609   public void setJmolDefaults() {
7610     setDefaultsType("Jmol");
7611   }
7612 
setDefaultsType(String type)7613   private void setDefaultsType(String type) {
7614     if (type.equalsIgnoreCase("RasMol")) {
7615       stm.setRasMolDefaults();
7616       return;
7617     }
7618     if (type.equalsIgnoreCase("PyMOL")) {
7619       stm.setPyMOLDefaults();
7620       return;
7621     }
7622     stm.setJmolDefaults();
7623     setIntProperty("bondingVersion", Elements.RAD_COV_IONIC_OB1_100_1);
7624     shm.setShapeSizeBs(JC.SHAPE_BALLS, 0, rd, getAllAtoms());
7625   }
7626 
setAntialias(int tok, boolean TF)7627   private void setAntialias(int tok, boolean TF) {
7628     boolean isChanged = false;
7629     switch (tok) {
7630     case T.antialiasdisplay:
7631       isChanged = (g.antialiasDisplay != TF);
7632       g.antialiasDisplay = TF;
7633       break;
7634     case T.antialiastranslucent:
7635       isChanged = (g.antialiasTranslucent != TF);
7636       g.antialiasTranslucent = TF;
7637       break;
7638     case T.antialiasimages:
7639       g.antialiasImages = TF;
7640       return;
7641     }
7642     if (isChanged) {
7643       resizeImage(0, 0, false, false, true); // for antialiasdisplay
7644       refresh(REFRESH_SYNC_MASK, "Viewer:setAntialias()");
7645     }
7646     //    resizeImage(0, 0, false, false, true);
7647   }
7648 
7649   // //////////////////////////////////////////////////////////////
7650   // temp manager
7651   // //////////////////////////////////////////////////////////////
7652 
allocTempPoints(int size)7653   public P3[] allocTempPoints(int size) {
7654     // rockets cartoons renderer only
7655     return tempArray.allocTempPoints(size);
7656   }
7657 
freeTempPoints(P3[] tempPoints)7658   public void freeTempPoints(P3[] tempPoints) {
7659     // rockets, cartoons render only
7660     tempArray.freeTempPoints(tempPoints);
7661   }
7662 
allocTempScreens(int size)7663   public P3i[] allocTempScreens(int size) {
7664     // mesh and mps
7665     return tempArray.allocTempScreens(size);
7666   }
7667 
freeTempScreens(P3i[] tempScreens)7668   public void freeTempScreens(P3i[] tempScreens) {
7669     tempArray.freeTempScreens(tempScreens);
7670   }
7671 
allocTempEnum(int size)7672   public STR[] allocTempEnum(int size) {
7673     // mps renderer
7674     return tempArray.allocTempEnum(size);
7675   }
7676 
freeTempEnum(STR[] temp)7677   public void freeTempEnum(STR[] temp) {
7678     tempArray.freeTempEnum(temp);
7679   }
7680 
7681   // //////////////////////////////////////////////////////////////
7682   // font stuff
7683   // //////////////////////////////////////////////////////////////
getFont3D(String fontFace, String fontStyle, float fontSize)7684   public Font getFont3D(String fontFace, String fontStyle, float fontSize) {
7685     return gdata.getFont3DFSS(fontFace, fontStyle, fontSize);
7686   }
7687 
7688   // //////////////////////////////////////////////////////////////
7689   // Access to atom properties for clients
7690   // //////////////////////////////////////////////////////////////
7691 
getAtomGroupQuaternions(BS bsAtoms, int nMax)7692   public Quat[] getAtomGroupQuaternions(BS bsAtoms, int nMax) {
7693     return ms.getAtomGroupQuaternions(bsAtoms, nMax, getQuaternionFrame());
7694   }
7695 
7696   // //////////////////////////////////////////////////////////////
7697   // stereo support
7698   // //////////////////////////////////////////////////////////////
7699 
setStereoMode(int[] twoColors, STER stereoMode, float degrees)7700   public void setStereoMode(int[] twoColors, STER stereoMode, float degrees) {
7701     setFloatProperty("stereoDegrees", degrees);
7702     setBooleanProperty("greyscaleRendering", stereoMode.isBiColor());
7703     if (twoColors != null)
7704       tm.setStereoMode2(twoColors);
7705     else
7706       tm.setStereoMode(stereoMode);
7707   }
7708 
7709   // //////////////////////////////////////////////////////////////
7710   //
7711   // //////////////////////////////////////////////////////////////
7712 
7713   // /////////////// getProperty /////////////
7714 
7715   public boolean scriptEditorVisible;
7716 
7717   public JmolAppConsoleInterface appConsole;
7718   private JmolScriptEditorInterface scriptEditor;
7719   GenericMenuInterface jmolpopup;
7720   private ModelKitPopup modelkit;
7721   private Map<String, Object> headlessImageParams;
7722 
getChimeInfo(int tok)7723   public String getChimeInfo(int tok) {
7724     return getPropertyManager().getChimeInfo(tok, bsA());
7725   }
7726 
getModelFileInfo()7727   public String getModelFileInfo() {
7728     return getPropertyManager().getModelFileInfo(getVisibleFramesBitSet());
7729   }
7730 
getModelFileInfoAll()7731   public String getModelFileInfoAll() {
7732     return getPropertyManager().getModelFileInfo(null);
7733   }
7734 
showEditor(String[] file_text)7735   public void showEditor(String[] file_text) {
7736     JmolScriptEditorInterface scriptEditor = (JmolScriptEditorInterface) getProperty(
7737         "DATA_API", "getScriptEditor", Boolean.TRUE);
7738     if (scriptEditor == null)
7739       return;
7740     scriptEditor.show(file_text);
7741   }
7742 
7743   JmolPropertyManager pm;
7744 
getPropertyManager()7745   private JmolPropertyManager getPropertyManager() {
7746     if (pm == null)
7747       (pm = (JmolPropertyManager) Interface
7748           .getInterface("org.jmol.viewer.PropertyManager", this, "prop"))
7749               .setViewer(this);
7750     return pm;
7751   }
7752 
7753   // ////////////////////////////////////////////////
7754 
7755   boolean isTainted = true;
7756 
setTainted(boolean TF)7757   public void setTainted(boolean TF) {
7758     isTainted = axesAreTainted = (TF && (refreshing || creatingImage));
7759   }
7760 
checkObjectClicked(int x, int y, int modifiers)7761   Map<String, Object> checkObjectClicked(int x, int y, int modifiers) {
7762     return shm.checkObjectClicked(x, y, modifiers, getVisibleFramesBitSet(),
7763         g.drawPicking);
7764   }
7765 
checkObjectHovered(int x, int y)7766   public boolean checkObjectHovered(int x, int y) {
7767     return (x >= 0 && shm != null && shm.checkObjectHovered(x, y,
7768         getVisibleFramesBitSet(), getBondsPickable()));
7769   }
7770 
checkObjectDragged(int prevX, int prevY, int x, int y, int action)7771   boolean checkObjectDragged(int prevX, int prevY, int x, int y, int action) {
7772     int iShape = 0;
7773     switch (getPickingMode()) {
7774     case ActionManager.PICKING_LABEL:
7775       iShape = JC.SHAPE_LABELS;
7776       break;
7777     case ActionManager.PICKING_DRAW:
7778       iShape = JC.SHAPE_DRAW;
7779       break;
7780     }
7781     if (shm.checkObjectDragged(prevX, prevY, x, y, action,
7782         getVisibleFramesBitSet(), iShape)) {
7783       refresh(REFRESH_REPAINT, "checkObjectDragged");
7784       if (iShape == JC.SHAPE_DRAW)
7785         scriptEcho((String) getShapeProperty(JC.SHAPE_DRAW, "command"));
7786       return true;
7787     }
7788     return false;
7789   }
7790 
rotateAxisAngleAtCenter(JmolScriptEvaluator eval, P3 rotCenter, V3 rotAxis, float degreesPerSecond, float endDegrees, boolean isSpin, BS bsSelected)7791   public boolean rotateAxisAngleAtCenter(JmolScriptEvaluator eval, P3 rotCenter,
7792                                          V3 rotAxis, float degreesPerSecond,
7793                                          float endDegrees, boolean isSpin,
7794                                          BS bsSelected) {
7795     // Eval: rotate FIXED
7796     boolean isOK = tm.rotateAxisAngleAtCenter(eval, rotCenter, rotAxis,
7797         degreesPerSecond, endDegrees, isSpin, bsSelected);
7798     if (isOK)
7799       setSync();
7800     return isOK;
7801   }
7802 
rotateAboutPointsInternal(JmolScriptEvaluator eval, P3 point1, P3 point2, float degreesPerSecond, float endDegrees, boolean isSpin, BS bsSelected, V3 translation, Lst<P3> finalPoints, float[] dihedralList, M4 m4)7803   public boolean rotateAboutPointsInternal(JmolScriptEvaluator eval, P3 point1,
7804                                            P3 point2, float degreesPerSecond,
7805                                            float endDegrees, boolean isSpin,
7806                                            BS bsSelected, V3 translation,
7807                                            Lst<P3> finalPoints,
7808                                            float[] dihedralList, M4 m4) {
7809     // Eval: rotate INTERNAL
7810 
7811     if (eval == null)
7812       eval = this.eval;
7813 
7814     if (headless) {
7815       if (isSpin && endDegrees == Float.MAX_VALUE)
7816         return false;
7817       isSpin = false;
7818     }
7819 
7820     boolean isOK = tm.rotateAboutPointsInternal(eval, point1, point2,
7821         degreesPerSecond, endDegrees, false, isSpin, bsSelected, false,
7822         translation, finalPoints, dihedralList, m4);
7823     if (isOK)
7824       setSync();
7825     return isOK;
7826   }
7827 
startSpinningAxis(T3 pt1, T3 pt2, boolean isClockwise)7828   public void startSpinningAxis(T3 pt1, T3 pt2, boolean isClockwise) {
7829     // Draw.checkObjectClicked ** could be difficult
7830     // from draw object click
7831     if (tm.spinOn || tm.navOn) {
7832       tm.setSpinOff();
7833       tm.setNavOn(false);
7834       return;
7835     }
7836     tm.rotateAboutPointsInternal(null, pt1, pt2, g.pickingSpinRate,
7837         Float.MAX_VALUE, isClockwise, true, null, false, null, null, null,
7838         null);
7839   }
7840 
getModelDipole()7841   public V3 getModelDipole() {
7842     return ms.getModelDipole(am.cmi);
7843   }
7844 
calculateMolecularDipole(BS bsAtoms)7845   public V3 calculateMolecularDipole(BS bsAtoms) throws Exception {
7846     try {
7847       return ms.calculateMolecularDipole(am.cmi, bsAtoms);
7848     } catch (JmolAsyncException e) {
7849       if (eval != null)
7850         eval.loadFileResourceAsync(e.getFileName());
7851       return null;
7852     }
7853   }
7854 
setDefaultLattice(P3 p)7855   public void setDefaultLattice(P3 p) {
7856     // Eval -- handled separately
7857     if (!Float.isNaN(p.x + p.y + p.z))
7858       g.ptDefaultLattice.setT(p);
7859     g.setO("defaultLattice", Escape.eP(p));
7860   }
7861 
getDefaultLattice()7862   public P3 getDefaultLattice() {
7863     return g.ptDefaultLattice;
7864   }
7865 
7866   /**
7867    *
7868    * V3000, SDF, JSON, CD, XYZ, XYZVIB, XYZRN, CML, PDB, PQR
7869    *
7870    * @param atomExpression
7871    * @param doTransform
7872    * @param isModelKit
7873    * @param type
7874    * @return full file data
7875    *
7876    */
getModelExtract(Object atomExpression, boolean doTransform, boolean isModelKit, String type)7877   public String getModelExtract(Object atomExpression, boolean doTransform,
7878                                 boolean isModelKit, String type) {
7879     return getPropertyManager().getModelExtract(getAtomBitSet(atomExpression),
7880         doTransform, isModelKit, type, false);
7881   }
7882 
7883   @Override
getData(String atomExpression, String type)7884   public String getData(String atomExpression, String type) {
7885     // from GaussianDialog
7886     return getModelFileData(atomExpression, type, true);
7887   }
7888 
7889   /**
7890    * @param atomExpression
7891    *        -- will be wrapped in { } and evaluated
7892    * @param type
7893    *        -- lower case means "atom data only; UPPERCASE returns full file
7894    *        data
7895    * @param allTrajectories
7896    * @return full or atom-only data formatted as specified
7897    */
getModelFileData(String atomExpression, String type, boolean allTrajectories)7898   public String getModelFileData(String atomExpression, String type,
7899                                  boolean allTrajectories) {
7900     return getPropertyManager().getAtomData(atomExpression, type,
7901         allTrajectories);
7902   }
7903 
getModelCml(BS bs, int nAtomsMax, boolean addBonds, boolean doTransform)7904   public String getModelCml(BS bs, int nAtomsMax, boolean addBonds,
7905                             boolean doTransform) {
7906     return getPropertyManager().getModelCml(bs, nAtomsMax, addBonds,
7907         doTransform, false);
7908   }
7909 
getPdbAtomData(BS bs, OC out, boolean asPQR, boolean doTransform)7910   public String getPdbAtomData(BS bs, OC out, boolean asPQR,
7911                                boolean doTransform) {
7912     return getPropertyManager().getPdbAtomData(bs == null ? bsA() : bs, out,
7913         asPQR, doTransform, false);
7914   }
7915 
isJmolDataFrame()7916   public boolean isJmolDataFrame() {
7917     return ms.isJmolDataFrameForModel(am.cmi);
7918   }
7919 
setFrameTitle(int modelIndex, String title)7920   public void setFrameTitle(int modelIndex, String title) {
7921     ms.setFrameTitle(BSUtil.newAndSetBit(modelIndex), title);
7922   }
7923 
setFrameTitleObj(Object title)7924   public void setFrameTitleObj(Object title) {
7925     shm.loadShape(JC.SHAPE_ECHO);
7926     ms.setFrameTitle(getVisibleFramesBitSet(), title);
7927   }
7928 
getFrameTitle()7929   public String getFrameTitle() {
7930     return ms.getFrameTitle(am.cmi);
7931   }
7932 
setAtomProperty(BS bs, int tok, int iValue, float fValue, String sValue, float[] values, String[] list)7933   public void setAtomProperty(BS bs, int tok, int iValue, float fValue,
7934                               String sValue, float[] values, String[] list) {
7935     if (tok == T.vanderwaals)
7936       shm.deleteVdwDependentShapes(bs);
7937     clearMinimization();
7938     ms.setAtomProperty(bs, tok, iValue, fValue, sValue, values, list);
7939     switch (tok) {
7940     case T.atomx:
7941     case T.atomy:
7942     case T.atomz:
7943     case T.fracx:
7944     case T.fracy:
7945     case T.fracz:
7946     case T.unitx:
7947     case T.unity:
7948     case T.unitz:
7949     case T.element:
7950       refreshMeasures(true);
7951     }
7952   }
7953 
checkCoordinatesChanged()7954   public void checkCoordinatesChanged() {
7955     // note -- use of save/restore coordinates cannot
7956     // track connected objects
7957     ms.recalculatePositionDependentQuantities(null, null);
7958     refreshMeasures(true);
7959   }
7960 
setAtomCoords(BS bs, int tokType, Object xyzValues)7961   public void setAtomCoords(BS bs, int tokType, Object xyzValues) {
7962     if (bs.isEmpty())
7963       return;
7964     ms.setAtomCoords(bs, tokType, xyzValues);
7965     checkMinimization();
7966     sm.setStatusAtomMoved(bs);
7967   }
7968 
setAtomCoordsRelative(T3 offset, BS bs)7969   public void setAtomCoordsRelative(T3 offset, BS bs) {
7970     // Eval
7971     if (bs == null)
7972       bs = bsA();
7973     if (bs.isEmpty())
7974       return;
7975     ms.setAtomCoordsRelative(offset, bs);
7976     checkMinimization();
7977     sm.setStatusAtomMoved(bs);
7978   }
7979 
invertAtomCoordPt(P3 pt, BS bs)7980   public void invertAtomCoordPt(P3 pt, BS bs) {
7981     // Eval
7982     ms.invertSelected(pt, null, -1, bs);
7983     checkMinimization();
7984     sm.setStatusAtomMoved(bs);
7985   }
7986 
invertAtomCoordPlane(P4 plane, BS bs)7987   public void invertAtomCoordPlane(P4 plane, BS bs) {
7988     ms.invertSelected(null, plane, -1, bs);
7989     checkMinimization();
7990     sm.setStatusAtomMoved(bs);
7991   }
7992 
invertRingAt(int atomIndex, boolean isClick)7993   public void invertRingAt(int atomIndex, boolean isClick) {
7994     // [r50 here just sets the max ring size to 50
7995     BS bs = getAtomBitSet(
7996         "connected(atomIndex=" + atomIndex + ") and !within(SMARTS,'[r50,R]')");
7997     int nb = bs.cardinality();
7998     switch (nb) {
7999     case 0:
8000     case 1:
8001       // not enough non-ring atoms
8002       return;
8003     case 2:
8004       break;
8005     case 3:
8006     case 4:
8007       // three or four are not in a ring. So let's find the shortest two
8008       // branches and invert them.
8009       int[] lengths = new int[nb];
8010       int[] points = new int[nb];
8011       int ni = 0;
8012       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1), ni++) {
8013         lengths[ni] = getBranchBitSet(i, atomIndex, true).cardinality();
8014         points[ni] = i;
8015       }
8016       for (int j = 0; j < nb - 2; j++) {
8017         int max = Integer.MIN_VALUE;
8018         int imax = 0;
8019         for (int i = 0; i < nb; i++)
8020           if (lengths[i] >= max && bs.get(points[i])) {
8021             imax = points[i];
8022             max = lengths[i];
8023           }
8024         bs.clear(imax);
8025       }
8026     }
8027     if (isClick)
8028       undoMoveActionClear(atomIndex, AtomCollection.TAINT_COORD, true);
8029     invertSelected(null, null, atomIndex, bs);
8030     if (isClick)
8031       setStatusAtomPicked(atomIndex, "inverted: " + Escape.eBS(bs), null,
8032           false);
8033   }
8034 
invertSelected(P3 pt, P4 plane, int iAtom, BS bsAtoms)8035   public void invertSelected(P3 pt, P4 plane, int iAtom, BS bsAtoms) {
8036     // Eval
8037     if (bsAtoms == null)
8038       bsAtoms = bsA();
8039     if (bsAtoms.cardinality() == 0)
8040       return;
8041     ms.invertSelected(pt, plane, iAtom, bsAtoms);
8042     checkMinimization();
8043     sm.setStatusAtomMoved(bsAtoms);
8044   }
8045 
moveAtoms(M4 m4, M3 mNew, M3 rotation, V3 translation, P3 center, boolean isInternal, BS bsAtoms, boolean translationOnly)8046   public void moveAtoms(M4 m4, M3 mNew, M3 rotation, V3 translation, P3 center,
8047                         boolean isInternal, BS bsAtoms,
8048                         boolean translationOnly) {
8049     // from TransformManager exclusively
8050     if (bsAtoms.isEmpty())
8051       return;
8052     ms.moveAtoms(m4, mNew, rotation, translation, bsAtoms, center, isInternal,
8053         translationOnly);
8054     checkMinimization();
8055     sm.setStatusAtomMoved(bsAtoms);
8056   }
8057 
8058   private boolean movingSelected;
8059   private boolean showSelected;
8060 
moveSelected(int deltaX, int deltaY, int deltaZ, int x, int y, BS bsSelected, boolean isTranslation, boolean asAtoms, int modifiers)8061   public void moveSelected(int deltaX, int deltaY, int deltaZ, int x, int y,
8062                            BS bsSelected, boolean isTranslation,
8063                            boolean asAtoms, int modifiers) {
8064     // called by actionManager
8065     // cannot synchronize this -- it's from the mouse and the event queue
8066     if (deltaZ == 0)
8067       return;
8068     if (x == Integer.MIN_VALUE)
8069       setModelKitRotateBondIndex(Integer.MIN_VALUE);
8070     if (isJmolDataFrame())
8071       return;
8072     if (deltaX == Integer.MIN_VALUE) {
8073       showSelected = true;
8074       movableBitSet = setMovableBitSet(null, !asAtoms);
8075       shm.loadShape(JC.SHAPE_HALOS);
8076       refresh(REFRESH_REPAINT_NO_MOTION_ONLY, "moveSelected");
8077       return;
8078     }
8079     if (deltaX == Integer.MAX_VALUE) {
8080       if (!showSelected)
8081         return;
8082       showSelected = false;
8083       movableBitSet = null;
8084       refresh(REFRESH_REPAINT_NO_MOTION_ONLY, "moveSelected");
8085       return;
8086     }
8087     if (movingSelected)
8088       return;
8089     movingSelected = true;
8090     stopMinimization();
8091     // note this does not sync with applets
8092     if (x != Integer.MIN_VALUE && modelkit != null
8093         && modelkit.getProperty("rotateBondIndex") != null) {
8094       modelkit.actionRotateBond(deltaX, deltaY, x, y,
8095           (modifiers & Event.VK_SHIFT) != 0);
8096     } else {
8097       bsSelected = setMovableBitSet(bsSelected, !asAtoms);
8098       if (!bsSelected.isEmpty()) {
8099         if (isTranslation) {
8100           P3 ptCenter = ms.getAtomSetCenter(bsSelected);
8101           tm.finalizeTransformParameters();
8102           float f = (g.antialiasDisplay ? 2 : 1);
8103           P3i ptScreen = tm.transformPt(ptCenter);
8104           P3 ptScreenNew;
8105           if (deltaZ != Integer.MIN_VALUE)
8106             ptScreenNew = P3.new3(ptScreen.x, ptScreen.y,
8107                 ptScreen.z + deltaZ + 0.5f);
8108           else
8109             ptScreenNew = P3.new3(ptScreen.x + deltaX * f + 0.5f,
8110                 ptScreen.y + deltaY * f + 0.5f, ptScreen.z);
8111           P3 ptNew = new P3();
8112           tm.unTransformPoint(ptScreenNew, ptNew);
8113           // script("draw ID 'pt" + Math.random() + "' " + Escape.escape(ptNew));
8114           ptNew.sub(ptCenter);
8115           setAtomCoordsRelative(ptNew, bsSelected);
8116         } else {
8117           tm.rotateXYBy(deltaX, deltaY, bsSelected);
8118         }
8119       }
8120     }
8121     refresh(REFRESH_SYNC, ""); // should be syncing here
8122     movingSelected = false;
8123   }
8124 
8125   /**
8126    * from Sticks
8127    *
8128    * @param index
8129    * @param closestAtomIndex
8130    * @param x
8131    * @param y
8132    */
highlightBond(int index, int closestAtomIndex, int x, int y)8133   public void highlightBond(int index, int closestAtomIndex, int x, int y) {//, String msg) {
8134     if (!hoverEnabled)
8135       return;
8136     BS bs = null;
8137     if (index >= 0) {
8138       Bond b = ms.bo[index];
8139       int i = b.atom2.i;
8140       if (!ms.isAtomInLastModel(i))
8141         return;
8142       bs = BSUtil.newAndSetBit(i);
8143       bs.set(b.atom1.i);
8144     }
8145     highlight(bs);
8146     setModelkitProperty("bondIndex", Integer.valueOf(index));
8147     setModelkitProperty("screenXY", new int[] { x, y });
8148     String text = (String) setModelkitProperty("hoverLabel",
8149         Integer.valueOf(-2 - index));
8150     if (text != null)
8151       hoverOnPt(x, y, text, null, null);
8152     //    hoverOn(closestAtomIndex, false);
8153     refresh(REFRESH_SYNC_MASK, "highlightBond");
8154   }
8155 
8156   public int atomHighlighted = -1;
8157 
highlight(BS bs)8158   public void highlight(BS bs) {
8159     atomHighlighted = (bs != null && bs.cardinality() == 1 ? bs.nextSetBit(0)
8160         : -1);
8161 
8162     if (bs == null) {
8163       setCursor(GenericPlatform.CURSOR_DEFAULT);
8164     } else {
8165       shm.loadShape(JC.SHAPE_HALOS);
8166       setCursor(GenericPlatform.CURSOR_HAND);
8167     }
8168     setModelkitProperty("highlight", bs);
8169     setShapeProperty(JC.SHAPE_HALOS, "highlight", bs);
8170   }
8171 
refreshMeasures(boolean andStopMinimization)8172   public void refreshMeasures(boolean andStopMinimization) {
8173     setShapeProperty(JC.SHAPE_MEASURES, "refresh", null);
8174     if (andStopMinimization)
8175       stopMinimization();
8176   }
8177 
8178   /**
8179    * fills an array with data -- if nX < 0 and this would involve JavaScript,
8180    * then this reads a full set of Double[][] in one function call. Otherwise it
8181    * reads the values using individual function calls, which each return Double.
8182    *
8183    * If the functionName begins with "file:" then data are read from a file
8184    * specified after the colon. The sign of nX is not relevant in that case. The
8185    * file may contain mixed numeric and non-numeric values; the non-numeric
8186    * values will be skipped by Parser.parseFloatArray
8187    *
8188    * @param functionName
8189    * @param nX
8190    * @param nY
8191    * @return nX by nY array of floating values
8192    */
functionXY(String functionName, int nX, int nY)8193   public float[][] functionXY(String functionName, int nX, int nY) {
8194     String data = null;
8195     if (functionName.indexOf("file:") == 0)
8196       data = getFileAsString3(functionName.substring(5), false, null);
8197     else if (functionName.indexOf("data2d_") != 0)
8198       return sm.functionXY(functionName, nX, nY);
8199     nX = Math.abs(nX);
8200     nY = Math.abs(nY);
8201     float[][] fdata;
8202     if (data == null) {
8203       fdata = (float[][]) getDataObj(functionName, null,
8204           JmolDataManager.DATA_TYPE_AFF);
8205       if (fdata != null)
8206         return fdata;
8207       data = "";
8208     }
8209     fdata = new float[nX][nY];
8210     float[] f = new float[nX * nY];
8211     Parser.parseStringInfestedFloatArray(data, null, f);
8212     for (int i = 0, n = 0; i < nX; i++)
8213       for (int j = 0; j < nY; j++)
8214         fdata[i][j] = f[n++];
8215     return fdata;
8216   }
8217 
functionXYZ(String functionName, int nX, int nY, int nZ)8218   public float[][][] functionXYZ(String functionName, int nX, int nY, int nZ) {
8219     String data = null;
8220     if (functionName.indexOf("file:") == 0)
8221       data = getFileAsString3(functionName.substring(5), false, null);
8222     else if (functionName.indexOf("data3d_") != 0)
8223       return sm.functionXYZ(functionName, nX, nY, nZ);
8224     nX = Math.abs(nX);
8225     nY = Math.abs(nY);
8226     nZ = Math.abs(nZ);
8227     float[][][] xyzdata;
8228     if (data == null) {
8229       xyzdata = (float[][][]) getDataObj(functionName, null,
8230           JmolDataManager.DATA_TYPE_AFF);
8231       if (xyzdata != null)
8232         return xyzdata;
8233       data = "";
8234     }
8235     xyzdata = new float[nX][nY][nZ];
8236     float[] f = new float[nX * nY * nZ];
8237     Parser.parseStringInfestedFloatArray(data, null, f);
8238     for (int i = 0, n = 0; i < nX; i++)
8239       for (int j = 0; j < nY; j++)
8240         for (int k = 0; k < nZ; k++)
8241           xyzdata[i][j][k] = f[n++];
8242     return xyzdata;
8243   }
8244 
8245   @Override
extractMolData(String what)8246   public String extractMolData(String what) {
8247     if (what == null) {
8248       int i = am.cmi;
8249       if (i < 0 || ms.ac == 0)
8250         return null;
8251       what = getModelNumberDotted(i);
8252     }
8253     return getModelExtract(what, true, false, "V2000");
8254   }
8255 
8256   /**
8257    *
8258    * @param type
8259    *        C13 or H1
8260    * @return null
8261    */
getNMRPredict(String type)8262   public String getNMRPredict(String type) {
8263     type = type.toUpperCase();
8264     if (type.equals("H") || type.equals("1H") || type.equals(""))
8265       type = "H1";
8266     else if (type.equals("C") || type.equals("13C"))
8267       type = "C13";
8268     if (!type.equals("NONE")) {
8269       if (!type.equals("C13") && !type.equals("H1"))
8270         return "Type must be H1 or C13";
8271       String molFile = getModelExtract("selected", true, false, "V2000");
8272       int pt = molFile.indexOf("\n");
8273       if (pt < 0)
8274         return null;
8275       molFile = "Jmol " + version_date + molFile.substring(pt);
8276       if (isApplet) {
8277         //TODO -- can do this if connected
8278         showUrl(g.nmrUrlFormat + molFile);
8279         return "opening " + g.nmrUrlFormat;
8280       }
8281     }
8282     syncScript("true", "*", 0);
8283     syncScript(type + "Simulate:", ".", 0);
8284     return "sending request to JSpecView";
8285   }
8286 
getHelp(String what)8287   public void getHelp(String what) {
8288     if (g.helpPath.indexOf("?") < 0) {
8289       if (what.length() > 0 && what.indexOf("?") != 0)
8290         what = "?search=" + PT.rep(what, " ", "%20");
8291       what += (what.length() == 0 ? "?ver=" : "&ver=") + JC.majorVersion;
8292     } else {
8293       what = "&" + what;
8294     }
8295     showUrl(g.helpPath + what);
8296   }
8297 
getChemicalInfo(String smiles, String info, BS bsAtoms)8298   public String getChemicalInfo(String smiles, String info, BS bsAtoms) {
8299     info = info.toLowerCase();
8300     char type = '/';
8301     switch (";inchi;inchikey;stdinchi;stdinchikey;name;image;drawing;names;"
8302         .indexOf(";" + info + ";")) {
8303     //       0     6        15       24          36   41    47      55
8304     //       0         1         2         3         4         5
8305     //       0123456789012345678901234567890123456789012345678901234567890
8306     case 0: // inchi
8307       type = 'I';
8308       break;
8309     case 6: // inchikey
8310       type = 'K';
8311       break;
8312     case 15: // stdinchi
8313       type = 'T';
8314       break;
8315     case 24: // stdinchikey
8316       type = 'S';
8317       break;
8318     case 36: // name
8319       type = 'M';
8320       break;
8321     case 41: // image
8322     case 47: // drawing
8323       type = '2';
8324       break;
8325     case 55: // names
8326       type = 'N';
8327       break;
8328     }
8329     String s = (String) setLoadFormat("_" + smiles, type, false);
8330     if (type == '2') {
8331       fm.loadImage(s, "\1" + smiles, false);
8332       return s;
8333     }
8334     if (type == '/') {
8335       if (PT.isOneOf(info, JC.CACTUS_FILE_TYPES))
8336         s += "file?format=" + info;
8337       else
8338         s += PT.rep(info, " ", "%20");
8339     }
8340     s = getFileAsString4(s, -1, false, false, false, "file");
8341     if (type == 'M' && s.indexOf("\n") > 0)
8342       s = s.substring(0, s.indexOf("\n"));
8343     else if (info.equals("jme"))
8344       s = getPropertyManager().fixJMEFormalCharges(bsAtoms, s);
8345     return s;
8346   }
8347 
8348   // ///////////////////////////////////////////////////////////////
8349   // delegated to stateManager
8350   // ///////////////////////////////////////////////////////////////
8351 
8352   /*
8353    * Moved from the consoles to vwr, since this could be of general interest,
8354    * it's more a property of Eval/Viewer, and the consoles are really just a
8355    * mechanism for getting user input and sending results, not saving a history
8356    * of it all. Ultimately I hope to integrate the mouse picking and possibly
8357    * periodic updates of position into this history to get a full history. We'll
8358    * see! BH 9/2006
8359    */
8360 
8361   /**
8362    * Adds one or more commands to the command history
8363    *
8364    * @param command
8365    *        the command to add
8366    */
addCommand(String command)8367   public void addCommand(String command) {
8368     if (autoExit || !haveDisplay || !getPreserveState())
8369       return;
8370     commandHistory.addCommand(PT.replaceAllCharacters(command, "\r\n\t", " "));
8371   }
8372 
pushState()8373   public void pushState() {
8374     if (autoExit || !haveDisplay || !getPreserveState())
8375       return;
8376     commandHistory.pushState(getStateInfo());
8377   }
8378 
popState()8379   public void popState() {
8380     if (autoExit || !haveDisplay || !getPreserveState())
8381       return;
8382     String state = commandHistory.popState();
8383     if (state != null)
8384       evalStringQuiet(state);
8385   }
8386 
8387   /**
8388    * Removes one command from the command history
8389    *
8390    * @return command removed
8391    */
removeCommand()8392   public String removeCommand() {
8393     return commandHistory.removeCommand();
8394   }
8395 
8396   /**
8397    * Options include: ; all n == Integer.MAX_VALUE ; n prev n >= 1 ; next n ==
8398    * -1 ; set max to -2 - n n <= -3 ; just clear n == -2 ; clear and turn off;
8399    * return "" n == 0 ; clear and turn on; return "" n == Integer.MIN_VALUE;
8400    *
8401    * @param howFarBack
8402    *        number of lines (-1 for next line)
8403    * @return one or more lines of command history
8404    */
8405   @Override
getSetHistory(int howFarBack)8406   public String getSetHistory(int howFarBack) {
8407     return commandHistory.getSetHistory(howFarBack);
8408   }
8409 
historyFind(String cmd, int dir)8410   public String historyFind(String cmd, int dir) {
8411     return commandHistory.find(cmd, dir);
8412   }
8413 
setHistory(String fileName)8414   public void setHistory(String fileName) {
8415     commandHistory.getSetHistory(Integer.MIN_VALUE);
8416     commandHistory
8417         .addCommand(getFileAsString4(fileName, -1, false, false, true, null));
8418   }
8419 
8420   // ///////////////////////////////////////////////////////////////
8421   // image and file export
8422   // ///////////////////////////////////////////////////////////////
8423 
getOutputChannel(String localName, String[] fullPath)8424   public OC getOutputChannel(String localName, String[] fullPath) {
8425     return getOutputManager().getOutputChannel(localName, fullPath);
8426   }
8427 
8428   @Override
writeTextFile(String fileName, String data)8429   public String writeTextFile(String fileName, String data) {
8430     Map<String, Object> params = new Hashtable<String, Object>();
8431     params.put("fileName", fileName);
8432     params.put("type", "txt");
8433     params.put("text", data);
8434     return outputToFile(params);
8435   }
8436 
8437   /**
8438    *
8439    * @param text
8440    *        null here clips image; String pastes text
8441    *
8442    * @return "OK image to clipboard: [width] * [height] or "OK text to
8443    *         clipboard: [length]
8444    */
8445   @Override
clipImageOrPasteText(String text)8446   public String clipImageOrPasteText(String text) {
8447     if (!haveAccess(ACCESS.ALL))
8448       return "no";
8449     return getOutputManager().clipImageOrPasteText(text);
8450   }
8451 
8452   @Override
getClipboardText()8453   public String getClipboardText() {
8454     if (!haveAccess(ACCESS.ALL))
8455       return "no";
8456     try {
8457       return getOutputManager().getClipboardText();
8458     } catch (Error er) {
8459       // unsigned applet will not have this interface
8460       return GT.$("clipboard is not accessible -- use signed applet");
8461     }
8462   }
8463 
8464   public boolean creatingImage;
8465 
8466   /**
8467    *
8468    * from eval write command only includes option to write set of files
8469    *
8470    * @param params
8471    * @return message starting with "OK" or an error message
8472    */
processWriteOrCapture(Map<String, Object> params)8473   public String processWriteOrCapture(Map<String, Object> params) {
8474     return getOutputManager().processWriteOrCapture(params);
8475   }
8476 
createZip(String fileName, String type, Map<String, Object> params)8477   public Object createZip(String fileName, String type,
8478                           Map<String, Object> params) {
8479     String state = getStateInfo();
8480     Object data = params.get("data");
8481     if (fileName != null)
8482       params.put("fileName", fileName);
8483     params.put("type", type);
8484     params.put("text", state);
8485     if (data instanceof String[])
8486       params.put("scripts", data);
8487     else if (data instanceof Lst)
8488       params.put("imageData", data);
8489     return getOutputManager().outputToFile(params);
8490   }
8491 
8492   @Override
outputToFile(Map<String, Object> params)8493   public String outputToFile(Map<String, Object> params) {
8494     return getOutputManager().outputToFile(params);
8495   }
8496 
setSyncTarget(int mode, boolean TF)8497   private void setSyncTarget(int mode, boolean TF) {
8498     switch (mode) {
8499     case 0:
8500       sm.syncingMouse = TF;
8501       break;
8502     case 1:
8503       sm.syncingScripts = TF;
8504       break;
8505     case 2:
8506       sm.syncSend(TF ? SYNC_GRAPHICS_MESSAGE : SYNC_NO_GRAPHICS_MESSAGE, "*",
8507           0);
8508       if (Float.isNaN(tm.stereoDegrees))
8509         setFloatProperty("stereoDegrees",
8510             TransformManager.DEFAULT_STEREO_DEGREES);
8511       if (TF) {
8512         setBooleanProperty("_syncMouse", false);
8513         setBooleanProperty("_syncScript", false);
8514       }
8515       return;
8516     }
8517     // if turning both off, sync the orientation now
8518     if (!sm.syncingScripts && !sm.syncingMouse)
8519       setSync();
8520   }
8521 
8522   public final static String SYNC_GRAPHICS_MESSAGE = "GET_GRAPHICS";
8523   public final static String SYNC_NO_GRAPHICS_MESSAGE = "SET_GRAPHICS_OFF";
8524 
8525   @Override
syncScript(String script, String applet, int port)8526   public void syncScript(String script, String applet, int port) {
8527     sm.syncScript(script, applet, port);
8528   }
8529 
8530   @Override
getModelIndexFromId(String id)8531   public int getModelIndexFromId(String id) {
8532     // from JSpecView peak pick and model "ID"
8533     return ms.getModelIndexFromId(id);
8534   }
8535 
setSyncDriver(int mode)8536   public void setSyncDriver(int mode) {
8537     sm.setSyncDriver(mode);
8538   }
8539 
setProteinType(STR type, BS bs)8540   public void setProteinType(STR type, BS bs) {
8541     ms.setProteinType(bs == null ? bsA() : bs, type);
8542   }
8543 
getVanderwaalsMar(int i)8544   public int getVanderwaalsMar(int i) {
8545     return (defaultVdw == VDW.USER ? userVdwMars[i]
8546         : Elements.getVanderwaalsMar(i, defaultVdw));
8547   }
8548 
getVanderwaalsMarType(int atomicAndIsotopeNumber, VDW type)8549   public int getVanderwaalsMarType(int atomicAndIsotopeNumber, VDW type) {
8550     if (type == null)
8551       type = defaultVdw;
8552     else
8553       switch (type) {
8554       case AUTO:
8555       case AUTO_BABEL:
8556       case AUTO_JMOL:
8557       case AUTO_RASMOL:
8558         if (defaultVdw != VDW.AUTO)
8559           type = defaultVdw;
8560         break;
8561       default:
8562         break;
8563       }
8564     if (type == VDW.USER && bsUserVdws == null)
8565       type = VDW.JMOL;
8566     return (type == VDW.USER ? userVdwMars[atomicAndIsotopeNumber & 127]
8567         : Elements.getVanderwaalsMar(atomicAndIsotopeNumber, type));
8568   }
8569 
setVdwStr(String name)8570   void setVdwStr(String name) {
8571     VDW type = VDW.getVdwType(name);
8572     if (type == null)
8573       type = VDW.AUTO;
8574     // only allowed types here are VDW_JMOL, VDW_BABEL, VDW_RASMOL, VDW_USER, VDW_AUTO
8575     switch (type) {
8576     case JMOL:
8577     case BABEL:
8578     case RASMOL:
8579     case AUTO:
8580     case USER:
8581       break;
8582     default:
8583       type = VDW.JMOL;
8584     }
8585     if (type != defaultVdw && type == VDW.USER && bsUserVdws == null)
8586       setUserVdw(defaultVdw);
8587     defaultVdw = type;
8588     g.setO("defaultVDW", type.getVdwLabel());
8589   }
8590 
8591   BS bsUserVdws;
8592   float[] userVdws;
8593   int[] userVdwMars;
8594 
setUserVdw(VDW mode)8595   void setUserVdw(VDW mode) {
8596     userVdwMars = new int[Elements.elementNumberMax];
8597     userVdws = new float[Elements.elementNumberMax];
8598     bsUserVdws = new BS();
8599     if (mode == VDW.USER)
8600       mode = VDW.JMOL;
8601     for (int i = 1; i < Elements.elementNumberMax; i++) {
8602       userVdwMars[i] = Elements.getVanderwaalsMar(i, mode);
8603       userVdws[i] = userVdwMars[i] / 1000f;
8604     }
8605   }
8606 
getDefaultVdwNameOrData(int mode, VDW type, BS bs)8607   public String getDefaultVdwNameOrData(int mode, VDW type, BS bs) {
8608     // called by getDataState and via Viewer: Eval.calculate,
8609     // Eval.show, StateManager.getLoadState, Viewer.setDefaultVdw
8610     switch (mode) {
8611     case Integer.MIN_VALUE:
8612       // iMode Integer.MIN_VALUE -- just the name
8613       return defaultVdw.getVdwLabel();
8614     case Integer.MAX_VALUE:
8615       // iMode = Integer.MAX_VALUE -- user, only selected
8616       if ((bs = bsUserVdws) == null)
8617         return "";
8618       type = VDW.USER;
8619       break;
8620     }
8621     if (type == null || type == VDW.AUTO)
8622       type = defaultVdw;
8623     if (type == VDW.USER && bsUserVdws == null)
8624       setUserVdw(defaultVdw);
8625 
8626     return getDataManager().getDefaultVdwNameOrData(type, bs);
8627   }
8628 
deleteAtoms(BS bsAtoms, boolean fullModels)8629   public int deleteAtoms(BS bsAtoms, boolean fullModels) {
8630     int atomIndex = (bsAtoms == null ? -1 : bsAtoms.nextSetBit(0));
8631     if (atomIndex < 0)
8632       return 0;
8633     clearModelDependentObjects();
8634     Atom a = ms.at[atomIndex];
8635     if (a == null)
8636       return 0;
8637     int mi = a.mi;
8638     if (!fullModels) {
8639       sm.modifySend(atomIndex, a.mi, 4,
8640           "deleting atom " + a.getAtomName());
8641       ms.deleteAtoms(bsAtoms);
8642       int n = slm.deleteAtoms(bsAtoms);
8643       setTainted(true);
8644       sm.modifySend(atomIndex, mi, -4, "OK");
8645       return n;
8646     }
8647     return deleteModels(mi, bsAtoms);
8648   }
8649 
8650   /**
8651    * called by ZAP {atomExpression} when atoms are present or the command is
8652    * specific for a model, such as ZAP 2.1
8653    *
8654    * @param modelIndex
8655    * @param bsAtoms
8656    * @return number of atoms deleted
8657    */
deleteModels(int modelIndex, BS bsAtoms)8658   public int deleteModels(int modelIndex, BS bsAtoms) {
8659     clearModelDependentObjects();
8660     // fileManager.addLoadScript("zap " + Escape.escape(bs));
8661     sm.modifySend(-1, modelIndex, 5,
8662         "deleting model " + getModelNumberDotted(modelIndex));
8663     int currentModel = am.cmi;
8664     setCurrentModelIndexClear(0, false);
8665     am.setAnimationOn(false);
8666     BS bsD0 = BSUtil.copy(slm.bsDeleted);
8667     BS bsModels = (bsAtoms == null ? BSUtil.newAndSetBit(modelIndex)
8668         : ms.getModelBS(bsAtoms, false));
8669     BS bsDeleted = ms.deleteModels(bsModels);
8670     if (bsDeleted == null) {
8671       setCurrentModelIndexClear(currentModel, false);
8672       return 0;
8673     }
8674     slm.processDeletedModelAtoms(bsDeleted);
8675     if (eval != null)
8676       eval.deleteAtomsInVariables(bsDeleted);
8677     setAnimationRange(0, 0);
8678     clearRepaintManager(-1);
8679     am.clear();
8680     am.initializePointers(1);
8681     setCurrentModelIndexClear(ms.mc > 1 ? -1 : 0, ms.mc > 1);
8682     hoverAtomIndex = -1;
8683     setFileLoadStatus(FIL.DELETED, null, null, null, null, null);
8684     refreshMeasures(true);
8685     if (bsD0 != null)
8686       bsDeleted.andNot(bsD0);
8687     sm.modifySend(-1, modelIndex, -5, "OK");
8688     return BSUtil.cardinalityOf(bsDeleted);
8689   }
8690 
deleteBonds(BS bsDeleted)8691   public void deleteBonds(BS bsDeleted) {
8692     int modelIndex = ms.bo[bsDeleted.nextSetBit(0)].atom1.mi;
8693     sm.modifySend(-1, modelIndex, 2, "delete bonds " + Escape.eBond(bsDeleted));
8694     ms.deleteBonds(bsDeleted, false);
8695     sm.modifySend(-1, modelIndex, -2, "OK");
8696   }
8697 
deleteModelAtoms(int modelIndex, int firstAtomIndex, int nAtoms, BS bsModelAtoms)8698   public void deleteModelAtoms(int modelIndex, int firstAtomIndex, int nAtoms,
8699                                BS bsModelAtoms) {
8700     // called from ModelCollection.deleteModel
8701     sm.modifySend(-1, modelIndex, 1,
8702         "delete atoms " + Escape.eBS(bsModelAtoms));
8703     BSUtil.deleteBits(tm.bsFrameOffsets, bsModelAtoms);
8704     getDataManager().deleteModelAtoms(firstAtomIndex, nAtoms, bsModelAtoms);
8705     sm.modifySend(-1, modelIndex, -1, "OK");
8706   }
8707 
getQuaternionFrame()8708   public char getQuaternionFrame() {
8709     return g.quaternionFrame.charAt(g.quaternionFrame.length() == 2 ? 1 : 0);
8710   }
8711 
8712   /**
8713    *
8714    * NOTE: This method is called from within a j2sNative block in
8715    * awtjs2d.Platform.java as well as from FileManager.loadImage
8716    *
8717    * @param image
8718    *        could be a byte array
8719    * @param nameOrError
8720    * @param echoName
8721    *        if this is an echo rather than the background
8722    * @param sco
8723    *        delivered in JavaScript from Platform.java
8724    * @return false
8725    */
loadImageData(Object image, String nameOrError, String echoName, Object sco)8726   public boolean loadImageData(Object image, String nameOrError,
8727                                String echoName, Object sco) {
8728     ScriptContext sc = (ScriptContext) sco;
8729     if (image == null && nameOrError != null)
8730       scriptEcho(nameOrError);
8731     if (echoName == null) {
8732       setBackgroundImage((image == null ? null : nameOrError), image);
8733     } else if (echoName.startsWith("\1")) {
8734       sm.showImage(echoName, image);
8735     } else if (echoName.startsWith("\0")) {
8736       if (image != null) {
8737         setWindowDimensions(new float[] { apiPlatform.getImageWidth(image),
8738             apiPlatform.getImageHeight(image) });
8739       }
8740     } else {
8741       shm.loadShape(JC.SHAPE_ECHO);
8742       setShapeProperty(JC.SHAPE_ECHO, "text", nameOrError);
8743       if (image != null)
8744         setShapeProperty(JC.SHAPE_ECHO, "image", image);
8745     }
8746     if (isJS && sc != null) {
8747       sc.mustResumeEval = true;
8748       eval.resumeEval(sc);
8749     }
8750     return false;
8751   }
8752 
cd(String dir)8753   public String cd(String dir) {
8754     if (dir == null) {
8755       dir = ".";
8756     } else if (dir.length() == 0) {
8757       setStringProperty("defaultDirectory", "");
8758       dir = ".";
8759     }
8760     dir = fm.getDefaultDirectory(
8761         dir + (dir.equals("=") ? "" : dir.endsWith("/") ? "X.spt" : "/X.spt"));
8762     if (dir.length() > 0)
8763       setStringProperty("defaultDirectory", dir);
8764     String path = fm.getFilePath(dir + "/", true, false);
8765     if (path.startsWith("file:/"))
8766       FileManager.setLocalPath(this, dir, false);
8767     return dir;
8768   }
8769 
8770   // //// Error handling
8771 
setErrorMessage(String errMsg, String errMsgUntranslated)8772   public String setErrorMessage(String errMsg, String errMsgUntranslated) {
8773     errorMessageUntranslated = errMsgUntranslated;
8774     if (errMsg != null)
8775       eval.stopScriptThreads();
8776     return (errorMessage = errMsg);
8777   }
8778 
8779   @Override
getErrorMessage()8780   public String getErrorMessage() {
8781     return errorMessage;
8782   }
8783 
8784   @Override
getErrorMessageUn()8785   public String getErrorMessageUn() {
8786     return errorMessageUntranslated == null ? errorMessage
8787         : errorMessageUntranslated;
8788   }
8789 
8790   private int currentShapeID = -1;
8791   private String currentShapeState;
8792 
setShapeErrorState(int shapeID, String state)8793   public void setShapeErrorState(int shapeID, String state) {
8794     currentShapeID = shapeID;
8795     currentShapeState = state;
8796   }
8797 
getShapeErrorState()8798   public String getShapeErrorState() {
8799     if (currentShapeID < 0)
8800       return "";
8801     shm.releaseShape(currentShapeID);
8802     clearRepaintManager(currentShapeID);
8803     return JC.getShapeClassName(currentShapeID, false) + " "
8804         + currentShapeState;
8805   }
8806 
handleError(Error er, boolean doClear)8807   public void handleError(Error er, boolean doClear) {
8808     // almost certainly out of memory; could be missing Jar file
8809     try {
8810       if (doClear)
8811         zapMsg("" + er); // get some breathing room
8812       undoClear();
8813       if (Logger.getLogLevel() == 0)
8814         Logger.setLogLevel(Logger.LEVEL_INFO);
8815       setCursor(GenericPlatform.CURSOR_DEFAULT);
8816       setBooleanProperty("refreshing", true);
8817       fm.setPathForAllFiles("");
8818       Logger.error("vwr handling error condition: " + er + "  ");
8819       notifyError("Error", "doClear=" + doClear + "; " + er, "" + er);
8820     } catch (Throwable e1) {
8821       try {
8822         Logger.error("Could not notify error " + er + ": due to " + e1);
8823       } catch (Throwable er2) {
8824         // tough luck.
8825       }
8826     }
8827   }
8828 
8829   // / User-defined functions
8830 
8831   final static Map<String, JmolScriptFunction> staticFunctions = new Hashtable<String, JmolScriptFunction>();
8832   Map<String, JmolScriptFunction> localFunctions;
8833 
getFunctions(boolean isStatic)8834   public Map<String, JmolScriptFunction> getFunctions(boolean isStatic) {
8835     return (isStatic ? staticFunctions : localFunctions);
8836   }
8837 
removeFunction(String name)8838   public void removeFunction(String name) {
8839     name = name.toLowerCase();
8840     JmolScriptFunction function = getFunction(name);
8841     if (function == null)
8842       return;
8843     staticFunctions.remove(name);
8844     localFunctions.remove(name);
8845   }
8846 
getFunction(String name)8847   public JmolScriptFunction getFunction(String name) {
8848     if (name == null)
8849       return null;
8850     JmolScriptFunction function = (isStaticFunction(name) ? staticFunctions
8851         : localFunctions).get(name);
8852     return (function == null || function.geTokens() == null ? null : function);
8853   }
8854 
isStaticFunction(String name)8855   static boolean isStaticFunction(String name) {
8856     return name.startsWith("static_");
8857   }
8858 
isFunction(String name)8859   public boolean isFunction(String name) {
8860     return (isStaticFunction(name) ? staticFunctions : localFunctions)
8861         .containsKey(name);
8862   }
8863 
clearFunctions()8864   public void clearFunctions() {
8865     staticFunctions.clear();
8866     localFunctions.clear();
8867   }
8868 
addFunction(JmolScriptFunction function)8869   public void addFunction(JmolScriptFunction function) {
8870     String name = function.getName();
8871     (isStaticFunction(name) ? staticFunctions : localFunctions).put(name,
8872         function);
8873   }
8874 
getFunctionCalls(String selectedFunction)8875   public String getFunctionCalls(String selectedFunction) {
8876     return getStateCreator().getFunctionCalls(selectedFunction);
8877   }
8878 
8879   /**
8880    * Simple method to ensure that the image creator (which writes files) was in
8881    * fact opened by this vwr and not by some manipulation of the applet. When
8882    * the image creator is used it requires both a vwr object and that vwr's
8883    * private key. But the private key is private, so it is not possible to
8884    * create a useable image creator without working through a vwr's own methods.
8885    * Bob Hanson, 9/20/2009
8886    *
8887    * @param privateKey
8888    * @return true if privateKey matches
8889    *
8890    */
8891 
checkPrivateKey(double privateKey)8892   public boolean checkPrivateKey(double privateKey) {
8893     return privateKey == this.privateKey;
8894   }
8895 
bindAction(String desc, String name)8896   public void bindAction(String desc, String name) {
8897     if (haveDisplay)
8898       acm.bind(desc, name);
8899   }
8900 
unBindAction(String desc, String name)8901   public void unBindAction(String desc, String name) {
8902     if (haveDisplay)
8903       acm.unbindAction(desc, name);
8904   }
8905 
calculateStruts(BS bs1, BS bs2)8906   public int calculateStruts(BS bs1, BS bs2) {
8907     return ms.calculateStruts(bs1 == null ? bsA() : bs1,
8908         bs2 == null ? bsA() : bs2);
8909   }
8910 
8911   /**
8912    * This flag if set FALSE:
8913    *
8914    * 1) turns UNDO off for the application 2) turns history off 3) prevents
8915    * saving of inlinedata for later LOAD "" commands 4) turns off the saving of
8916    * changed atom properties 5) does not guarantee accurate state representation
8917    * 6) disallows generation of the state
8918    *
8919    * It is useful in situations such as web sites where memory is an issue and
8920    * there is no need for such.
8921    *
8922    *
8923    * @return TRUE or FALSE
8924    */
getPreserveState()8925   public boolean getPreserveState() {
8926     return (g.preserveState && scm != null);
8927   }
8928 
8929   boolean isKiosk;
8930 
isKiosk()8931   boolean isKiosk() {
8932     return isKiosk;
8933   }
8934 
hasFocus()8935   public boolean hasFocus() {
8936     return (haveDisplay && (isKiosk || apiPlatform.hasFocus(display)));
8937   }
8938 
setFocus()8939   public void setFocus() {
8940     if (haveDisplay && !apiPlatform.hasFocus(display))
8941       apiPlatform.requestFocusInWindow(display);
8942   }
8943 
stopMinimization()8944   void stopMinimization() {
8945     if (minimizer != null) {
8946       minimizer.setProperty("stop", null);
8947     }
8948   }
8949 
clearMinimization()8950   void clearMinimization() {
8951     if (minimizer != null)
8952       minimizer.setProperty("clear", null);
8953   }
8954 
getMinimizationInfo()8955   public String getMinimizationInfo() {
8956     return (minimizer == null ? "" : (String) minimizer.getProperty("log", 0));
8957   }
8958 
checkMinimization()8959   private void checkMinimization() {
8960     refreshMeasures(true);
8961     if (!g.monitorEnergy)
8962       return;
8963     try {
8964       minimize(null, 0, 0, getFrameAtoms(), null, 0, MIN_SILENT);
8965     } catch (Exception e) {
8966     }
8967     echoMessage(getP("_minimizationForceField") + " Energy = "
8968         + getP("_minimizationEnergy"));
8969   }
8970 
8971   public static final int MIN_SILENT = 1;
8972   public static final int MIN_HAVE_FIXED = 2;
8973   public static final int MIN_QUICK = 4;
8974   public static final int MIN_ADDH = 8;
8975   public static final int MIN_NO_RANGE = 16;
8976 
8977   /**
8978    *
8979    * @param eval
8980    * @param steps
8981    *        Integer.MAX_VALUE --> use defaults
8982    * @param crit
8983    *        -1 --> use defaults
8984    * @param bsSelected
8985    * @param bsFixed
8986    * @param rangeFixed
8987    * @param flags
8988    * @throws Exception
8989    */
minimize(JmolScriptEvaluator eval, int steps, float crit, BS bsSelected, BS bsFixed, float rangeFixed, int flags)8990   public void minimize(JmolScriptEvaluator eval, int steps, float crit,
8991                        BS bsSelected, BS bsFixed, float rangeFixed, int flags)
8992       throws Exception {
8993 
8994     boolean isSilent = (flags & MIN_SILENT) == MIN_SILENT;
8995     boolean isQuick = (flags & MIN_QUICK) == MIN_QUICK;
8996     boolean hasRange = (flags & MIN_NO_RANGE) == 0;
8997     boolean addHydrogen = (flags & MIN_ADDH) == MIN_ADDH;
8998     // We only work on atoms that are in frame
8999 
9000     String ff = g.forceField;
9001     BS bsInFrame = getFrameAtoms();
9002 
9003     if (bsSelected == null)
9004       bsSelected = getModelUndeletedAtomsBitSet(getVisibleFramesBitSet().nextSetBit(0));
9005     else if (!isQuick)
9006       bsSelected.and(bsInFrame);
9007     if (isQuick) {
9008       getAuxiliaryInfoForAtoms(bsSelected).put("dimension", "3D");
9009       bsInFrame = bsSelected;
9010     }
9011 
9012     if (rangeFixed <= 0)
9013       rangeFixed = JC.MINIMIZE_FIXED_RANGE;
9014 
9015     // we allow for a set of atoms to be fixed,
9016     // but that is only used by default
9017 
9018     BS bsMotionFixed = BSUtil
9019         .copy(bsFixed == null ? slm.getMotionFixedAtoms() : bsFixed);
9020     boolean haveFixed = (bsMotionFixed.cardinality() > 0);
9021     if (haveFixed)
9022       bsSelected.andNot(bsMotionFixed);
9023 
9024     // We always fix any atoms that
9025     // are in the visible frame set and are within 5 angstroms
9026     // and are not already selected
9027 
9028     BS bsNearby = (hasRange ? new BS()
9029         : ms.getAtomsWithinRadius(rangeFixed, bsSelected, true, null));
9030     bsNearby.andNot(bsSelected);
9031     if (haveFixed) {
9032       bsMotionFixed.and(bsNearby);
9033     } else {
9034       bsMotionFixed = bsNearby;
9035     }
9036     bsMotionFixed.and(bsInFrame);
9037     flags |= ((haveFixed ? MIN_HAVE_FIXED : 0)
9038         | (getBooleanProperty("minimizationSilent") ? MIN_SILENT : 0));
9039     if (isQuick && getBoolean(T.testflag2))
9040       return;
9041     if (isQuick) {
9042       {
9043         // carry out a preliminary UFF no-hydrogen calculation
9044         // to clean up benzene rings and amides.
9045         try {
9046           if (!isSilent)
9047             Logger.info("Minimizing " + bsSelected.cardinality() + " atoms");
9048           getMinimizer(true).minimize(steps, crit, bsSelected, bsMotionFixed,
9049               flags, "UFF");
9050         } catch (Exception e) {
9051           Logger.error("Minimization error: " + e.toString());
9052           e.printStackTrace();
9053         }
9054 
9055       }
9056     }
9057     if (addHydrogen) {
9058       BS bsH = addHydrogens(bsSelected, flags);
9059       if (!isQuick)
9060         bsSelected.or(bsH);
9061     }
9062 
9063     int n = bsSelected.cardinality();
9064     if (ff.equals("MMFF") && n > g.minimizationMaxAtoms) {
9065       scriptStatusMsg(
9066           "Too many atoms for minimization (" + n + ">" + g.minimizationMaxAtoms
9067               + "); use 'set minimizationMaxAtoms' to increase this limit",
9068           "minimization: too many atoms");
9069       return;
9070     }
9071     try {
9072       if (!isSilent)
9073         Logger.info("Minimizing " + bsSelected.cardinality() + " atoms");
9074       getMinimizer(true).minimize(steps, crit, bsSelected, bsMotionFixed, flags,
9075           (isQuick ? "MMFF" : ff));
9076       if (isQuick) {
9077         g.forceField = "MMFF";
9078         setHydrogens(bsSelected);
9079         showString("Minimized by Jmol",  false);
9080       }
9081     } catch (JmolAsyncException e) {
9082       if (eval != null)
9083         eval.loadFileResourceAsync(e.getFileName());
9084     } catch (Exception e) {
9085       Logger.error("Minimization error: " + e.toString());
9086       e.printStackTrace();
9087     }
9088   }
9089 
setHydrogens(BS bsAtoms)9090   private void setHydrogens(BS bsAtoms) {
9091     int[] nTotal = new int[1];
9092     P3[][] hatoms = ms.calculateHydrogens(bsAtoms, nTotal, null,
9093         AtomCollection.CALC_H_IGNORE_H | AtomCollection.CALC_H_QUICK);
9094     for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1)) {
9095       P3[] pts = hatoms[i];
9096       if (pts == null || pts.length == 0)
9097         continue;
9098       Atom a = ms.at[i];
9099       Bond[] b = a.bonds;
9100       for (int j = 0, pt = 0, n = a.getBondCount(); j < n; j++) {
9101         Atom h = b[j].getOtherAtom(a);
9102         if (h.getAtomicAndIsotopeNumber() == 1) {
9103           P3 p = pts[pt++];
9104           ms.setAtomCoord(h.i, p.x, p.y, p.z);
9105         }
9106       }
9107     }
9108     ms.resetMolecules();
9109   }
9110 
setMotionFixedAtoms(BS bs)9111   public void setMotionFixedAtoms(BS bs) {
9112     slm.setMotionFixedAtoms(bs);
9113   }
9114 
getMotionFixedAtoms()9115   public BS getMotionFixedAtoms() {
9116     return slm.getMotionFixedAtoms();
9117   }
9118 
9119   //  void rotateArcBall(int x, int y, float factor) {
9120   //    tm.rotateArcBall(x, y, factor);
9121   //    refresh(REFRESH_SYNC, sm.syncingMouse ? "Mouse: rotateArcBall " + x + " "
9122   //        + y + " " + factor : "");
9123   //  }
9124 
getAtomicPropertyState(SB commands, byte type, BS bs, String name, float[] data)9125   void getAtomicPropertyState(SB commands, byte type, BS bs, String name,
9126                               float[] data) {
9127     getStateCreator().getAtomicPropertyStateBuffer(commands, type, bs, name,
9128         data);
9129   }
9130 
getCenterAndPoints(Lst<Object[]> atomSets, boolean addCenter)9131   public P3[][] getCenterAndPoints(Lst<Object[]> atomSets, boolean addCenter) {
9132     return ms.getCenterAndPoints(atomSets, addCenter);
9133   }
9134 
writeFileData(String fileName, String type, int modelIndex, Object[] parameters)9135   public String writeFileData(String fileName, String type, int modelIndex,
9136                               Object[] parameters) {
9137     return getOutputManager().writeFileData(fileName, type, modelIndex,
9138         parameters);
9139   }
9140 
getPdbData(int modelIndex, String type, BS bsAtoms, Object[] parameters, OC oc, boolean getStructure)9141   public String getPdbData(int modelIndex, String type, BS bsAtoms,
9142                            Object[] parameters, OC oc, boolean getStructure) {
9143     // plot command
9144     return getPropertyManager().getPdbData(modelIndex, type,
9145         bsAtoms == null ? bsA() : bsAtoms, parameters, oc, getStructure);
9146   }
9147 
getGroupsWithin(int nResidues, BS bs)9148   public BS getGroupsWithin(int nResidues, BS bs) {
9149     return ms.getGroupsWithin(nResidues, bs);
9150   }
9151 
9152   // parallel processing
9153 
9154   public static int nProcessors = 1;
9155 
9156   static {
9157     /**
9158      * @j2sIgnore
9159      *
9160      */
9161     {
9162       nProcessors = Runtime.getRuntime().availableProcessors();
9163     }
9164 
9165   }
9166 
9167   public boolean displayLoadErrors = true;
9168 
9169   /**
9170    *
9171    * @param shapeID
9172    * @param madOrMad10
9173    *        for axes, unitcell, and boundbox 10*mad; otherwise milliangstrom
9174    *        diameter
9175    * @param bsSelected
9176    */
setShapeSize(int shapeID, int madOrMad10, BS bsSelected)9177   public void setShapeSize(int shapeID, int madOrMad10, BS bsSelected) {
9178     // might be atoms or bonds
9179     if (bsSelected == null)
9180       bsSelected = bsA();
9181     shm.setShapeSizeBs(shapeID, madOrMad10, null, bsSelected);
9182   }
9183 
setShapeProperty(int shapeID, String propertyName, Object value)9184   public void setShapeProperty(int shapeID, String propertyName, Object value) {
9185     // Eval, BondCollection, StateManager, local
9186     if (shapeID >= 0)
9187       shm.setShapePropertyBs(shapeID, propertyName, value, null);
9188   }
9189 
getShapeProperty(int shapeType, String propertyName)9190   public Object getShapeProperty(int shapeType, String propertyName) {
9191     return shm.getShapePropertyIndex(shapeType, propertyName,
9192         Integer.MIN_VALUE);
9193   }
9194 
getShapePropertyAsInt(int shapeID, String propertyName)9195   private int getShapePropertyAsInt(int shapeID, String propertyName) {
9196     Object value = getShapeProperty(shapeID, propertyName);
9197     return value == null || !(value instanceof Integer) ? Integer.MIN_VALUE
9198         : ((Integer) value).intValue();
9199   }
9200 
setModelVisibility()9201   public void setModelVisibility() {
9202     if (shm != null) // necessary for file chooser
9203       shm.setModelVisibility();
9204   }
9205 
resetShapes(boolean andCreateNew)9206   public void resetShapes(boolean andCreateNew) {
9207     shm.resetShapes();
9208     if (andCreateNew) {
9209       shm.loadDefaultShapes(ms);
9210       clearRepaintManager(-1);
9211     }
9212   }
9213 
9214   private boolean isParallel;
9215 
setParallel(boolean TF)9216   public boolean setParallel(boolean TF) {
9217     return (isParallel = g.multiProcessor && TF);
9218   }
9219 
isParallel()9220   public boolean isParallel() {
9221     return g.multiProcessor && isParallel;
9222   }
9223 
undoClear()9224   void undoClear() {
9225     actionStates.clear();
9226     actionStatesRedo.clear();
9227   }
9228 
9229   /**
9230    *
9231    * @param action
9232    *        Token.undo or Token.redo
9233    * @param n
9234    *        number of steps to go back/forward; 0 for all; -1 for clear; -2 for
9235    *        clear BOTH
9236    *
9237    */
undoMoveAction(int action, int n)9238   public void undoMoveAction(int action, int n) {
9239     getStateCreator().undoMoveAction(action, n);
9240   }
9241 
undoMoveActionClear(int taintedAtom, int type, boolean clearRedo)9242   public void undoMoveActionClear(int taintedAtom, int type,
9243                                   boolean clearRedo) {
9244     // called by actionManager
9245     if (g.preserveState)
9246       getStateCreator().undoMoveActionClear(taintedAtom, type, clearRedo);
9247   }
9248 
moveAtomWithHydrogens(int atomIndex, int deltaX, int deltaY, int deltaZ, BS bsAtoms)9249   protected void moveAtomWithHydrogens(int atomIndex, int deltaX, int deltaY,
9250                                        int deltaZ, BS bsAtoms) {
9251     // called by actionManager
9252     stopMinimization();
9253     if (bsAtoms == null) {
9254       Atom atom = ms.at[atomIndex];
9255       bsAtoms = BSUtil.newAndSetBit(atomIndex);
9256       Bond[] bonds = atom.bonds;
9257       if (bonds != null)
9258         for (int i = 0; i < bonds.length; i++) {
9259           Atom atom2 = bonds[i].getOtherAtom(atom);
9260           if (atom2.getElementNumber() == 1)
9261             bsAtoms.set(atom2.i);
9262         }
9263     }
9264     moveSelected(deltaX, deltaY, deltaZ, Integer.MIN_VALUE, Integer.MIN_VALUE,
9265         bsAtoms, true, true, 0);
9266   }
9267 
isModelPDB(int i)9268   public boolean isModelPDB(int i) {
9269     return ms.am[i].isBioModel;
9270   }
9271 
9272   @Override
deleteMeasurement(int i)9273   public void deleteMeasurement(int i) {
9274     setShapeProperty(JC.SHAPE_MEASURES, "delete", Integer.valueOf(i));
9275   }
9276 
9277   @Override
getSmiles(BS bs)9278   public String getSmiles(BS bs) throws Exception {
9279     return getSmilesOpt(bs, -1, -1,
9280         (bs == null && Logger.debugging ? JC.SMILES_GEN_ATOM_COMMENT : 0)
9281         ,
9282         null);
9283   }
9284 
9285   @Override
getOpenSmiles(BS bs)9286   public String getOpenSmiles(BS bs) throws Exception {
9287     return getSmilesOpt(bs, -1, -1,
9288         JC.SMILES_TYPE_OPENSMILES
9289             | (bs == null && Logger.debugging ? JC.SMILES_GEN_ATOM_COMMENT : 0)
9290             ,
9291         "/openstrict///");
9292   }
9293 
getBioSmiles(BS bs)9294   public String getBioSmiles(BS bs) throws Exception {
9295     return getSmilesOpt(bs, -1, -1,
9296         JC.SMILES_GEN_BIO_ALLOW_UNMATCHED_RINGS
9297             | JC.SMILES_GEN_BIO_COV_CROSSLINK | JC.SMILES_GEN_BIO_COMMENT
9298             | (Logger.debugging ? JC.SMILES_GEN_ATOM_COMMENT : 0),
9299         null);
9300   }
9301 
9302   /**
9303    * returns the SMILES string for a sequence or atom set does not include
9304    * attached protons on groups
9305    *
9306    * @param bsSelected
9307    *        selected atom set or null for current or specified range
9308    * @param index1
9309    *        when bsSeleced == null, first atomIndex or -1 for current
9310    * @param index2
9311    *        when bsSeleced == null, end atomIndex or -1 for current
9312    * @param flags
9313    *        see JC.SMILES_xxxx
9314    * @param options
9315    *        e.g. /strict,open/
9316    * @return SMILES string
9317    * @throws Exception
9318    */
getSmilesOpt(BS bsSelected, int index1, int index2, int flags, String options)9319   public String getSmilesOpt(BS bsSelected, int index1, int index2, int flags,
9320                              String options)
9321       throws Exception {
9322     String bioComment = ((flags
9323         & JC.SMILES_GEN_BIO_COMMENT) == JC.SMILES_GEN_BIO_COMMENT
9324             ? getJmolVersion() + " " + getModelName(am.cmi)
9325             : options);
9326     Atom[] atoms = ms.at;
9327     if (bsSelected == null) {
9328       if (index1 < 0 || index2 < 0) {
9329         bsSelected = bsA();
9330       } else {
9331         if ((flags & JC.SMILES_GEN_BIO) == JC.SMILES_GEN_BIO) {
9332           if (index1 > index2) {
9333             int i = index1;
9334             index1 = index2;
9335             index2 = i;
9336           }
9337           index1 = atoms[index1].group.firstAtomIndex;
9338           index2 = atoms[index2].group.lastAtomIndex;
9339         }
9340         bsSelected = new BS();
9341         bsSelected.setBits(index1, index2 + 1);
9342       }
9343     }
9344     flags |= (isModel2D(bsSelected) ? JC.SMILES_2D : 0);
9345     SmilesMatcherInterface sm = getSmilesMatcher();
9346     if (JC.isSmilesCanonical(options)) {
9347       String smiles = sm.getSmiles(atoms, ms.ac, bsSelected, "/noAromatic/",
9348           flags);
9349       return getChemicalInfo(smiles, "smiles", null).trim();
9350     }
9351     return sm.getSmiles(atoms, ms.ac, bsSelected, bioComment, flags);
9352   }
9353 
isModel2D(BS bs)9354   private boolean isModel2D(BS bs) {
9355     Model m = getModelForAtomIndex(bs.nextSetBit(0));
9356     return (m != null && "2D".equals(m.auxiliaryInfo.get("dimension")));
9357   }
9358 
alert(String msg)9359   public void alert(String msg) {
9360     prompt(msg, null, null, true);
9361   }
9362 
prompt(String label, String data, String[] list, boolean asButtons)9363   public String prompt(String label, String data, String[] list,
9364                        boolean asButtons) {
9365     return (isKiosk ? "null"
9366         : apiPlatform.prompt(label, data, list, asButtons));
9367   }
9368 
9369   /**
9370    * Ask for new file name when saving or opening a file in Java and saving a file in JavaScript.
9371    * JavaScript use of FileReader goes through loadFileAsync
9372    *
9373    * @param type
9374    * @param fileName
9375    * @param params
9376    * @return new file name
9377    */
dialogAsk(String type, String fileName, Map<String, Object> params)9378   public String dialogAsk(String type, String fileName,
9379                           Map<String, Object> params) {
9380     /**
9381      * @j2sNative
9382      *
9383      *            return prompt(type, fileName);
9384      *
9385      */
9386     {
9387       // may have #NOCARTOONS#; and/or "#APPEND#; prepended
9388       return (isKiosk || !haveAccess(ACCESS.ALL) ? null
9389           : sm.dialogAsk(type, fileName, params));
9390     }
9391   }
9392 
9393   public int stateScriptVersionInt = Integer.MAX_VALUE;
9394 
9395   private JmolRendererInterface jsExporter3D;
9396 
initializeExporter(Map<String, Object> params)9397   public JmolRendererInterface initializeExporter(Map<String, Object> params) {
9398     boolean isJS = params.get("type").equals("JS");
9399     if (isJS) {
9400       if (jsExporter3D != null) {
9401         jsExporter3D.initializeOutput(this, privateKey, params);
9402         return jsExporter3D;
9403       }
9404     } else {
9405       String fileName = (String) params.get("fileName");
9406       String[] fullPath = (String[]) params.get("fullPath");
9407       OC out = getOutputChannel(fileName, fullPath);
9408       if (out == null)
9409         return null;
9410       params.put("outputChannel", out);
9411     }
9412     JmolRendererInterface export3D = (JmolRendererInterface) Interface
9413         .getOption("export.Export3D", this, "export");
9414     if (export3D == null)
9415       return null;
9416     Object exporter = export3D.initializeExporter(this, privateKey, gdata,
9417         params);
9418     if (isJS && exporter != null)
9419       jsExporter3D = export3D;
9420     return (exporter == null ? null : export3D);
9421   }
9422 
getMouseEnabled()9423   public boolean getMouseEnabled() {
9424     return refreshing && !creatingImage;
9425   }
9426 
9427   @Override
calcAtomsMinMax(BS bs, BoxInfo boxInfo)9428   public void calcAtomsMinMax(BS bs, BoxInfo boxInfo) {
9429     ms.calcAtomsMinMax(bs, boxInfo);
9430   }
9431 
9432   /**
9433    * used in autocompletion in console using TAB
9434    *
9435    * @param map
9436    * @param c
9437    */
9438   @SuppressWarnings("unchecked")
getObjectMap(Map<String, ?> map, char c)9439   public void getObjectMap(Map<String, ?> map, char c) {
9440     switch (c) {
9441     case '{':
9442       if (getScriptManager() != null) {
9443         Map<String, Object> m = (Map<String, Object>) map;
9444         if (definedAtomSets != null)
9445           m.putAll(definedAtomSets);
9446         T.getTokensType(m, T.predefinedset);
9447       }
9448       return;
9449     case '$':
9450     case '0':
9451       shm.getObjectMap(map, c == '$');
9452       return;
9453     }
9454   }
9455 
setPicked(int atomIndex, boolean andReset)9456   public void setPicked(int atomIndex, boolean andReset) {
9457     SV pickedSet = null;
9458     SV pickedList = null;
9459     if (atomIndex >= 0) {
9460       if (andReset)
9461         setPicked(-1, false);
9462       g.setI("_atompicked", atomIndex);
9463       pickedSet = (SV) g.getParam("picked", true);
9464       pickedList = (SV) g.getParam("pickedList", true);
9465     }
9466     if (pickedSet == null || pickedSet.tok != T.bitset) {
9467       pickedSet = SV.newV(T.bitset, new BS());
9468       pickedList = SV.getVariableList(new Lst<Object>());
9469       g.setUserVariable("picked", pickedSet);
9470       g.setUserVariable("pickedList", pickedList);
9471     }
9472     if (atomIndex < 0)
9473       return;
9474     SV.getBitSet(pickedSet, false).set(atomIndex);
9475     SV p = pickedList.pushPop(null, null);
9476     // don't allow double click
9477     if (p.tok == T.bitset)
9478       pickedList.pushPop(null, p);
9479     if (p.tok != T.bitset || !((BS) p.value).get(atomIndex))
9480       pickedList.pushPop(null,
9481           SV.newV(T.bitset, BSUtil.newAndSetBit(atomIndex)));
9482   }
9483 
9484   /**
9485    * Run a script using the script function script("xxxxxx") using direct script
9486    * tokens for script ( "xxxxxxx" )
9487    *
9488    */
9489   @Override
runScript(String script)9490   public String runScript(String script) {
9491     return "" + evaluateExpression(new T[][] { new T[] { T.tokenScript,
9492         T.tokenLeftParen, SV.newS(script), T.tokenRightParen } });
9493   }
9494 
9495   /**
9496    * formerly runScript(), this method really can ONLY be called by the viewer
9497    * being run from an already-running script. If it is invoked by a separate
9498    * thread, it can wreak havoc on any queued thread, since they are not thread
9499    * safe.
9500    *
9501    * @param script
9502    * @return output of the script.
9503    */
9504   @Override
runScriptCautiously(String script)9505   public String runScriptCautiously(String script) {
9506     // from isosurface reading JVXL file with slab
9507     SB outputBuffer = new SB();
9508     try {
9509       if (getScriptManager() == null)
9510         return null;
9511       eval.runScriptBuffer(script, outputBuffer, false);
9512     } catch (Exception e) {
9513       return eval.getErrorMessage();
9514     }
9515     return outputBuffer.toString();
9516   }
9517 
setFrameDelayMs(long millis)9518   public void setFrameDelayMs(long millis) {
9519     ms.setFrameDelayMs(millis, getVisibleFramesBitSet());
9520   }
9521 
getBaseModelBitSet()9522   public BS getBaseModelBitSet() {
9523     return ms.getModelAtomBitSetIncludingDeleted(getJDXBaseModelIndex(am.cmi),
9524         true);
9525   }
9526 
9527   public Map<String, Object> timeouts;
9528 
clearTimeouts()9529   public void clearTimeouts() {
9530     if (timeouts != null)
9531       TimeoutThread.clear(timeouts);
9532   }
9533 
setTimeout(String name, int mSec, String script)9534   public void setTimeout(String name, int mSec, String script) {
9535     if (!haveDisplay || headless || autoExit)
9536       return;
9537     if (name == null) {
9538       clearTimeouts();
9539       return;
9540     }
9541     if (timeouts == null) {
9542       timeouts = new Hashtable<String, Object>();
9543     }
9544     TimeoutThread.setTimeout(this, timeouts, name, mSec, script);
9545   }
9546 
triggerTimeout(String name)9547   public void triggerTimeout(String name) {
9548     if (!haveDisplay || timeouts == null)
9549       return;
9550     TimeoutThread.trigger(timeouts, name);
9551   }
9552 
clearTimeout(String name)9553   public void clearTimeout(String name) {
9554     setTimeout(name, 0, null);
9555   }
9556 
showTimeout(String name)9557   public String showTimeout(String name) {
9558     return (haveDisplay ? TimeoutThread.showTimeout(timeouts, name) : "");
9559   }
9560 
getOrCalcPartialCharges(BS bsSelected, BS bsIgnore)9561   public float[] getOrCalcPartialCharges(BS bsSelected, BS bsIgnore)
9562       throws JmolAsyncException {
9563     if (bsSelected == null)
9564       bsSelected = bsA();
9565     bsSelected = BSUtil.copy(bsSelected);
9566     BSUtil.andNot(bsSelected, bsIgnore);
9567     BSUtil.andNot(bsSelected, ms.bsPartialCharges);
9568     if (!bsSelected.isEmpty())
9569       calculatePartialCharges(bsSelected);
9570     return ms.getPartialCharges();
9571   }
9572 
calculatePartialCharges(BS bsSelected)9573   public void calculatePartialCharges(BS bsSelected) throws JmolAsyncException {
9574     if (bsSelected == null || bsSelected.isEmpty())
9575       bsSelected = getFrameAtoms();
9576     if (bsSelected.isEmpty())
9577       return;
9578     //    // this forces an array if it does not exist
9579     //    setAtomProperty(BSUtil.newAndSetBit(pt), T.partialcharge, 0, 1f, null,
9580     //        null, null);
9581     Logger.info("Calculating MMFF94 partial charges for "
9582         + bsSelected.cardinality() + " atoms");
9583     getMinimizer(true).calculatePartialCharges(ms, bsSelected, null);
9584   }
9585 
setCurrentModelID(String id)9586   public void setCurrentModelID(String id) {
9587     int modelIndex = am.cmi;
9588     if (modelIndex >= 0)
9589       ms.setInfo(modelIndex, "modelID", id);
9590   }
9591 
cacheClear()9592   public void cacheClear() {
9593     // script: reset cache
9594     fm.cacheClear();
9595     ligandModelSet = null;
9596     ligandModels = null;
9597     ms.clearCache();
9598   }
9599 
9600   /**
9601    * JSInterface -- allows saving files in memory for later retrieval
9602    *
9603    * @param key
9604    * @param data
9605    *
9606    */
9607 
cachePut(String key, Object data)9608   public void cachePut(String key, Object data) {
9609     // PyMOL reader and isosurface
9610     // HTML5/JavaScript load ?  and  script ?
9611     Logger.info("Viewer cachePut " + key);
9612     fm.cachePut(key, data);
9613   }
9614 
cacheFileByName(String fileName, boolean isAdd)9615   public int cacheFileByName(String fileName, boolean isAdd) {
9616     // cache command in script
9617     if (fileName == null) {
9618       cacheClear();
9619       return -1;
9620     }
9621     return fm.cacheFileByNameAdd(fileName, isAdd);
9622   }
9623 
clearThreads()9624   public void clearThreads() {
9625     if (eval != null)
9626       eval.stopScriptThreads();
9627     stopMinimization();
9628     tm.clearThreads();
9629     setAnimationOn(false);
9630   }
9631 
getEvalContextAndHoldQueue(JmolScriptEvaluator eval)9632   public ScriptContext getEvalContextAndHoldQueue(JmolScriptEvaluator eval) {
9633     if (eval == null || !(isJS || testAsync))
9634       return null;
9635     eval.pushContextDown(ScriptEval.CONTEXT_HOLD_QUEUE);
9636     ScriptContext sc = eval.getThisContext();
9637     sc.setMustResume();
9638     sc.isJSThread = true;
9639     queueOnHold = true;
9640     return sc;
9641   }
9642 
getDefaultPropertyParam(int propertyID)9643   public String getDefaultPropertyParam(int propertyID) {
9644     return getPropertyManager().getDefaultPropertyParam(propertyID);
9645   }
9646 
getPropertyNumber(String name)9647   public int getPropertyNumber(String name) {
9648     return getPropertyManager().getPropertyNumber(name);
9649   }
9650 
checkPropertyParameter(String name)9651   public boolean checkPropertyParameter(String name) {
9652     return getPropertyManager().checkPropertyParameter(name);
9653   }
9654 
extractProperty(Object property, Object args, int pt)9655   public Object extractProperty(Object property, Object args, int pt) {
9656     return getPropertyManager().extractProperty(property, args, pt, null,
9657         false);
9658   }
9659 
9660   //// requiring ScriptEvaluator:
9661 
addHydrogens(BS bsAtoms, int flags)9662   public BS addHydrogens(BS bsAtoms, int flags) {
9663     boolean isSilent = ((flags & MIN_SILENT) == MIN_SILENT);
9664     boolean isQuick = ((flags & MIN_QUICK) == MIN_QUICK);
9665     boolean doAll = (bsAtoms == null);
9666     if (bsAtoms == null)
9667       bsAtoms = getModelUndeletedAtomsBitSet(
9668           getVisibleFramesBitSet().length() - 1);
9669     BS bsB = new BS();
9670     if (bsAtoms.isEmpty())
9671       return bsB;
9672 //    if (!ms.isAtomInLastModel(bsAtoms.nextSetBit(0)))
9673 //      return bsB;
9674     Lst<Atom> vConnections = new Lst<Atom>();
9675     P3[] pts = getAdditionalHydrogens(bsAtoms, vConnections,
9676         flags | (doAll ? AtomCollection.CALC_H_DOALL : 0));
9677     boolean wasAppendNew = false;
9678     wasAppendNew = g.appendNew;
9679     if (pts.length > 0) {
9680       clearModelDependentObjects();
9681       try {
9682         bsB = (isQuick ? ms.addHydrogens(vConnections, pts)
9683             : addHydrogensInline(bsAtoms, vConnections, pts));
9684       } catch (Exception e) {
9685         System.out.println(e.toString());
9686         // ignore
9687       }
9688       if (wasAppendNew)
9689         g.appendNew = true;
9690     }
9691     if (!isSilent)
9692       scriptStatus(GT.i(GT.$("{0} hydrogens added"), pts.length));
9693     return bsB;
9694   }
9695 
addHydrogensInline(BS bsAtoms, Lst<Atom> vConnections, P3[] pts)9696   public BS addHydrogensInline(BS bsAtoms, Lst<Atom> vConnections, P3[] pts)
9697       throws Exception {
9698     if (getScriptManager() == null)
9699       return null;
9700     return scm.addHydrogensInline(bsAtoms, vConnections, pts);
9701   }
9702 
9703   @Override
evalFunctionFloat(Object func, Object params, float[] values)9704   public float evalFunctionFloat(Object func, Object params, float[] values) {
9705     return (getScriptManager() == null ? 0
9706         : eval.evalFunctionFloat(func, params, values));
9707   }
9708 
evalParallel(ScriptContext context, ShapeManager shapeManager)9709   public boolean evalParallel(ScriptContext context,
9710                               ShapeManager shapeManager) {
9711     displayLoadErrors = false;
9712     boolean isOK = getScriptManager() != null && eval.evalParallel(context,
9713         (shapeManager == null ? this.shm : shapeManager));
9714     displayLoadErrors = true;
9715     return isOK;
9716   }
9717 
9718   /**
9719    * synchronized here trapped the eventQueue; see also
9720    * evaluateExpressionAsVariable
9721    *
9722    */
9723   @Override
evaluateExpression(Object stringOrTokens)9724   public Object evaluateExpression(Object stringOrTokens) {
9725     return (getScriptManager() == null ? null
9726         : eval.evaluateExpression(stringOrTokens, false, false));
9727   }
9728 
9729   @Override
evaluateExpressionAsVariable(Object stringOrTokens)9730   public SV evaluateExpressionAsVariable(Object stringOrTokens) {
9731     return (getScriptManager() == null ? null
9732         : (SV) eval.evaluateExpression(stringOrTokens, true, false));
9733   }
9734 
getAtomBitSet(Object atomExpression)9735   public BS getAtomBitSet(Object atomExpression) {
9736     // SMARTS searching
9737     // getLigandInfo
9738     // used in interaction with JSpecView
9739     // used for set picking SELECT
9740 
9741     if (atomExpression instanceof BS)
9742       return slm.excludeAtoms((BS) atomExpression, false);
9743     getScriptManager();
9744     return getAtomBitSetEval(eval, atomExpression);
9745   }
9746 
getScriptContext(String why)9747   public ScriptContext getScriptContext(String why) {
9748     return (getScriptManager() == null ? null : eval.getScriptContext(why));
9749   }
9750 
getAtomDefs(Map<String, Object> names)9751   public String getAtomDefs(Map<String, Object> names) {
9752     Lst<String> keys = new Lst<String>();
9753     for (Map.Entry<String, ?> e : names.entrySet())
9754       if (e.getValue() instanceof BS)
9755         keys.addLast("{" + e.getKey() + "} <"
9756             + ((BS) e.getValue()).cardinality() + " atoms>\n");
9757     int n = keys.size();
9758     String[] k = new String[n];
9759     keys.toArray(k);
9760     Arrays.sort(k);
9761     SB sb = new SB();
9762     for (int i = 0; i < n; i++)
9763       sb.append(k[i]);
9764     return sb.append("\n").toString();
9765   }
9766 
setCGO(Lst<Object> info)9767   public void setCGO(Lst<Object> info) {
9768     shm.loadShape(JC.SHAPE_CGO);
9769     shm.setShapePropertyBs(JC.SHAPE_CGO, "setCGO", info, null);
9770   }
9771 
setModelSet(ModelSet modelSet)9772   public void setModelSet(ModelSet modelSet) {
9773     this.ms = mm.modelSet = modelSet;
9774   }
9775 
setObjectProp(String id, int tokCommand)9776   public String setObjectProp(String id, int tokCommand) {
9777     // for PyMOL session scene setting
9778     getScriptManager();
9779     if (id == null)
9780       id = "*";
9781     return (eval == null ? null : eval.setObjectPropSafe(id, tokCommand));
9782   }
9783 
setDihedrals(float[] dihedralList, BS[] bsBranches, float rate)9784   public void setDihedrals(float[] dihedralList, BS[] bsBranches, float rate) {
9785     if (bsBranches == null)
9786       bsBranches = ms.getBsBranches(dihedralList);
9787     ms.setDihedrals(dihedralList, bsBranches, rate);
9788   }
9789 
9790   private boolean chainCaseSpecified;
9791 
9792   /**
9793    * Create a unique integer for any chain string. Note that if there are any
9794    * chains that are more than a single character, chainCaseSensitive is
9795    * automatically set TRUE
9796    *
9797    *
9798    * @param id
9799    *        < 256 is just the character of a single-character upper-case chain
9800    *        id, upper or lower case query;
9801    *
9802    *        >= 256 < 300 is lower case found in structure
9803    *
9804    * @param isAssign
9805    *        from a file reader, not a select query
9806    *
9807    * @return i
9808    */
getChainID(String id, boolean isAssign)9809   public int getChainID(String id, boolean isAssign) {
9810     // if select :a and there IS chain "a" in a structure,
9811     // then we return that id, and all is good. Chain selectivity
9812     // is inforced, and we will find it.
9813     Integer iboxed = (Integer) chainMap.get(id);
9814     if (iboxed != null)
9815       return iboxed.intValue();
9816     int i = id.charAt(0);
9817     if (id.length() > 1) {
9818       i = 300 + chainList.size();
9819     } else if ((isAssign || chainCaseSpecified) && 97 <= i && i <= 122) { // lower case
9820       i += 159; // starts at 256
9821     }
9822     if (i >= 256) {
9823       iboxed = (Integer) chainMap.get(id);
9824       if (iboxed != null)
9825         return iboxed.intValue();
9826       //this will force chainCaseSensitive when it is necessary
9827       chainCaseSpecified |= isAssign;
9828       chainList.addLast(id);
9829     }
9830     // if select :a and there is NO chain "a" in the structure,
9831     // there still might be an "A" and we cannot check for
9832     // chain case sensitivity yet, as we are parsing the script,
9833     // not processing it. So we just store this one as 97-122.
9834 
9835     iboxed = Integer.valueOf(i);
9836     chainMap.put(iboxed, id);
9837     chainMap.put(id, iboxed);
9838     return i;
9839   }
9840 
getChainIDStr(int id)9841   public String getChainIDStr(int id) {
9842     return (String) chainMap.get(Integer.valueOf(id));
9843   }
9844 
getScriptQueueInfo()9845   public Boolean getScriptQueueInfo() {
9846     return (scm != null && scm.isQueueProcessing() ? Boolean.TRUE
9847         : Boolean.FALSE);
9848   }
9849 
9850   JmolNMRInterface nmrCalculation;
9851 
getNMRCalculation()9852   public JmolNMRInterface getNMRCalculation() {
9853     return (nmrCalculation == null
9854         ? (nmrCalculation = (JmolNMRInterface) Interface
9855             .getOption("quantum.NMRCalculation", this, "script"))
9856                 .setViewer(this)
9857         : nmrCalculation);
9858   }
9859 
getDistanceUnits(String s)9860   public String getDistanceUnits(String s) {
9861     if (s == null)
9862       s = getDefaultMeasurementLabel(2);
9863     int pt = s.indexOf("//");
9864     return (pt < 0 ? g.measureDistanceUnits : s.substring(pt + 2));
9865   }
9866 
calculateFormalCharges(BS bs)9867   public int calculateFormalCharges(BS bs) {
9868     return ms.fixFormalCharges(bs == null ? bsA() : bs);
9869   }
9870 
setModulation(BS bs, boolean isOn, P3 t1, boolean isQ)9871   public void setModulation(BS bs, boolean isOn, P3 t1, boolean isQ) {
9872     if (isQ)
9873       g.setO("_modt", Escape.eP(t1));
9874     ms.setModulation(bs == null ? getAllAtoms() : bs, isOn, t1, isQ);
9875     refreshMeasures(true);
9876   }
9877 
checkInMotion(int state)9878   public void checkInMotion(int state) {
9879     switch (state) {
9880     case 0: // off
9881       setTimeout("_SET_IN_MOTION_", 0, null);
9882       break;
9883     case 1: // start 1-second timer (by default)
9884       if (!inMotion)
9885         setTimeout("_SET_IN_MOTION_", g.hoverDelayMs * 2, "!setInMotion");
9886       break;
9887     case 2: // trigger, from a timeout thread
9888       setInMotion(true);
9889       refresh(REFRESH_SYNC_MASK, "timeoutThread set in motion");
9890       break;
9891     }
9892   }
9893 
9894   /**
9895    * check motion for rendering during mouse movement, spin, vibration, and
9896    * animation
9897    *
9898    * @param tok
9899    * @return TRUE if allowed
9900    */
checkMotionRendering(int tok)9901   public boolean checkMotionRendering(int tok) {
9902     if (!getInMotion(true) && !tm.spinOn && !tm.vibrationOn && !am.animationOn)
9903       return true;
9904     if (g.wireframeRotation)
9905       return false;
9906     int n = 0;
9907     switch (tok) {
9908     case T.bonds:
9909     case T.atoms:
9910       n = 2;
9911       break;
9912     case T.ellipsoid:
9913       n = 3;
9914       break;
9915     case T.geosurface:
9916       n = 4;
9917       break;
9918     case T.cartoon:
9919       n = 5;
9920       break;
9921     case T.mesh:
9922       n = 6;
9923       break;
9924     case T.translucent:
9925       n = 7;
9926       break;
9927     case T.antialiasdisplay:
9928       n = 8;
9929       break;
9930     }
9931     return g.platformSpeed >= n;
9932   }
9933 
9934   // ///////////////////////////////////////////////////////////////
9935   // delegated to JmolFileAdapter
9936   // ///////////////////////////////////////////////////////////////
9937 
openExportChannel(double privateKey, String fileName, boolean asWriter)9938   public OC openExportChannel(double privateKey, String fileName,
9939                               boolean asWriter)
9940       throws IOException {
9941     return getOutputManager().openOutputChannel(privateKey, fileName, asWriter,
9942         false);
9943   }
9944 
9945   /*default*/String logFileName;
9946 
9947   @Override
log(String data)9948   public void log(String data) {
9949     if (data != null)
9950       getOutputManager().logToFile(data);
9951   }
9952 
getLogFileName()9953   public String getLogFileName() {
9954     return (logFileName == null ? "" : logFileName);
9955   }
9956 
getCommands(Map<String, BS> htDefine, Map<String, BS> htMore, String select)9957   public String getCommands(Map<String, BS> htDefine, Map<String, BS> htMore,
9958                             String select) {
9959     return getStateCreator().getCommands(htDefine, htMore, select);
9960   }
9961 
allowCapture()9962   public boolean allowCapture() {
9963     return !isApplet || isSignedApplet;
9964   }
9965 
compileExpr(String expr)9966   public T[] compileExpr(String expr) {
9967     Object o = (getScriptManager() == null ? null
9968         : eval.evaluateExpression(expr, false, true));
9969     return (o instanceof T[] ? (T[]) o : new T[] { T.o(T.string, expr) });
9970   }
9971 
checkSelect(Map<String, SV> h, T[] value)9972   public boolean checkSelect(Map<String, SV> h, T[] value) {
9973     return getScriptManager() != null && eval.checkSelect(h, value);
9974   }
9975 
getAnnotationInfo(SV d, String match, int type)9976   public String getAnnotationInfo(SV d, String match, int type) {
9977     return getAnnotationParser(type == T.dssr).getAnnotationInfo(this, d, match,
9978         type, am.cmi);
9979   }
9980 
getAtomValidation(String type, Atom atom)9981   public Lst<Float> getAtomValidation(String type, Atom atom) {
9982     return getAnnotationParser(false).getAtomValidation(this, type, atom);
9983   }
9984 
dragMinimizeAtom(final int iAtom)9985   void dragMinimizeAtom(final int iAtom) {
9986     stopMinimization();
9987     BS bs = (getMotionFixedAtoms().isEmpty()
9988         ? ms.getAtoms((ms.isAtomPDB(iAtom) ? T.group : T.molecule),
9989             BSUtil.newAndSetBit(iAtom))
9990         : BSUtil.setAll(ms.ac));
9991     try {
9992       minimize(null, Integer.MAX_VALUE, 0, bs, null, 0, 0);
9993     } catch (Exception e) {
9994       if (!async)
9995         return;
9996       final Viewer me = this;
9997       @SuppressWarnings("unused")
9998       Runnable r = new Runnable() {
9999         @Override
10000         public void run() {
10001           me.dragMinimizeAtom(iAtom);
10002         }
10003       };
10004       /**
10005        * @j2sNative
10006        *
10007        *            setTimeout(function(){r.run()}, 100);
10008        *
10009        */
10010       {
10011       }
10012     }
10013   }
10014 
10015   BioResolver jbr;
10016 
getJBR()10017   public BioResolver getJBR() {
10018     return (jbr == null
10019         ? jbr = ((BioResolver) Interface
10020             .getInterface("org.jmol.modelsetbio.BioResolver", this, "file"))
10021                 .setViewer(this)
10022         : jbr);
10023   }
10024 
checkMenuUpdate()10025   public void checkMenuUpdate() {
10026     if (jmolpopup != null)
10027       jmolpopup.jpiUpdateComputedMenus();
10028   }
10029 
10030   private JmolChimeMessenger jcm;
10031 
getChimeMessenger()10032   public JmolChimeMessenger getChimeMessenger() {
10033     return (jcm == null
10034         ? jcm = ((JmolChimeMessenger) Interface
10035             .getInterface("org.jmol.viewer.ChimeMessenger", this, "script"))
10036                 .set(this)
10037         : jcm);
10038   }
10039 
getAuxiliaryInfoForAtoms(Object atomExpression)10040   public Map<String, Object> getAuxiliaryInfoForAtoms(Object atomExpression) {
10041     return ms
10042         .getAuxiliaryInfo(ms.getModelBS(getAtomBitSet(atomExpression), false));
10043   }
10044 
10045   private JSJSONParser jsonParser;
10046 
getJSJSONParser()10047   private JSJSONParser getJSJSONParser() {
10048     return (jsonParser == null
10049         ? jsonParser = (JSJSONParser) Interface
10050             .getInterface("javajs.util.JSJSONParser", this, "script")
10051         : jsonParser);
10052   }
10053 
parseJSON(String str)10054   public Object parseJSON(String str) {
10055     return (str == null ? null
10056         : (str = str.trim()).startsWith("{") ? parseJSONMap(str)
10057             : parseJSONArray(str));
10058   }
10059 
parseJSONMap(String jsonMap)10060   public Map<String, Object> parseJSONMap(String jsonMap) {
10061     return getJSJSONParser().parseMap(jsonMap, true);
10062   }
10063 
10064   @SuppressWarnings("unchecked")
parseJSONArray(String jsonArray)10065   public Lst<Object> parseJSONArray(String jsonArray) {
10066     return (Lst<Object>) getJSJSONParser().parse(jsonArray, true);
10067   }
10068 
10069   /**
10070    * Retrieve a Symmetry object, possibly re-using an old one.
10071    *
10072    * @return org.jmol.symmetry.Symmetry object
10073    */
getSymTemp()10074   public SymmetryInterface getSymTemp() {
10075     return Interface.getSymmetry(this, "ms");
10076   }
10077 
setWindowDimensions(float[] dims)10078   public void setWindowDimensions(float[] dims) {
10079     resizeInnerPanel((int) dims[0], (int) dims[1]);
10080   }
10081 
10082   private Triangulator triangulator;
10083 
getTriangulator()10084   public Triangulator getTriangulator() {
10085     return (triangulator == null
10086         ? (triangulator = (Triangulator) Interface.getUtil("Triangulator", this,
10087             "script"))
10088         : triangulator);
10089   }
10090 
getCurrentModelAuxInfo()10091   public Map<String, Object> getCurrentModelAuxInfo() {
10092     return (am.cmi >= 0 ? ms.getModelAuxiliaryInfo(am.cmi) : null);
10093   }
10094 
startNBO(String options)10095   public void startNBO(String options) {
10096     Map<String, Object> htParams = new Hashtable<String, Object>();
10097     htParams.put("service", "nbo");
10098     htParams.put("action", "showPanel");
10099     htParams.put("options", options);
10100     sm.processService(htParams);
10101   }
10102 
10103   /**
10104    * startup -U nbo option
10105    *
10106    * @param plugin
10107    */
startPlugin(String plugin)10108   public void startPlugin(String plugin) {
10109 
10110     // for now, just NBO; need a way to bootstrap this
10111 
10112     if ("nbo".equalsIgnoreCase(plugin))
10113       startNBO("all");
10114   }
10115 
10116   private NBOParser nboParser;
10117 
connectNBO(String type)10118   public void connectNBO(String type) {
10119     if (am.cmi < 0)
10120       return;
10121     getNBOParser().connectNBO(am.cmi, type);
10122   }
10123 
getNBOParser()10124   private NBOParser getNBOParser() {
10125     return (nboParser == null ? nboParser = ((NBOParser) Interface.getInterface(
10126         "org.jmol.adapter.readers.quantum.NBOParser", this, "script")).set(this)
10127         : nboParser);
10128   }
10129 
getNBOAtomLabel(Atom atom)10130   public String getNBOAtomLabel(Atom atom) {
10131     return getNBOParser().getNBOAtomLabel(atom);
10132   }
10133 
calculateChirality(BS bsAtoms)10134   public String calculateChirality(BS bsAtoms) {
10135     if (bsAtoms == null)
10136       bsAtoms = bsA();
10137     return ms.calculateChiralityForAtoms(bsAtoms, true);
10138   }
10139 
getSubstructureSetArray(String pattern, BS bsSelected, int flags)10140   public BS[] getSubstructureSetArray(String pattern, BS bsSelected, int flags)
10141       throws Exception {
10142     return getSmilesMatcher().getSubstructureSetArray(pattern, ms.at, ms.ac,
10143         bsSelected, null, flags);
10144   }
10145 
getSubstructureSetArrayForNodes(String pattern, Node[] nodes, int flags)10146   public BS[] getSubstructureSetArrayForNodes(String pattern, Node[] nodes,
10147                                               int flags)
10148       throws Exception {
10149     return getSmilesMatcher().getSubstructureSetArray(pattern, nodes,
10150         nodes.length, null, null, flags);
10151   }
10152 
getSmilesAtoms(String smiles)10153   public Node[] getSmilesAtoms(String smiles) throws Exception {
10154     return getSmilesMatcher().getAtoms(smiles);
10155   }
10156 
calculateChiralityForSmiles(String smiles)10157   public String[] calculateChiralityForSmiles(String smiles) {
10158     try {
10159       return Interface.getSymmetry(this, "ms")
10160           .calculateCIPChiralityForSmiles(this, smiles);
10161     } catch (Exception e) {
10162       return null;
10163     }
10164   }
10165 
getPdbID()10166   public String getPdbID() {
10167     return (ms.getInfo(am.cmi, "isPDB") == Boolean.TRUE
10168         ? (String) ms.getInfo(am.cmi, "pdbID")
10169         : null);
10170   }
10171 
10172   /**
10173    * get a value from the current model's Model.auxiliaryInfo
10174    *
10175    * @param key
10176    * @return value, or null if there is no SINGLE current model
10177    */
getModelInfo(String key)10178   public Object getModelInfo(String key) {
10179     return ms.getInfo(am.cmi, key);
10180   }
10181 
notifyScriptEditor(int msWalltime, Object[] data)10182   public void notifyScriptEditor(int msWalltime, Object[] data) {
10183     if (scriptEditor != null) {
10184       scriptEditor.notify(msWalltime, data);
10185     }
10186   }
10187 
sendConsoleMessage(String msg)10188   public void sendConsoleMessage(String msg) {
10189     if (appConsole != null)
10190       appConsole.sendConsoleMessage(msg);
10191   }
10192 
10193   /**
10194    *
10195    * @param nameOrData
10196    *        could be name or [name,value]
10197    * @return value
10198    */
getModelkitProperty(Object nameOrData)10199   public Object getModelkitProperty(Object nameOrData) {
10200     return (modelkit == null ? null : modelkit.getProperty(nameOrData));
10201   }
10202 
setModelkitProperty(String key, Object value)10203   public Object setModelkitProperty(String key, Object value) {
10204     return (modelkit == null ? null : modelkit.setProperty(key, value));
10205   }
10206 
10207   /**
10208    * A general method for retrieving symmetry information with full capability
10209    * of the symop() scripting function.
10210    *
10211    * @param iatom
10212    *        atom index specifying the model set and used for pt1 if that is null
10213    *        and also for matching element type.
10214    * @param xyz
10215    *        the desired Jones-Faithful representation of the symmetry operation
10216    *        or null
10217    * @param iOp
10218    *        the desired symmetry operation [1-n] or 0
10219    * @param translation [i j k] translational addition to symop
10220    * @param pt1
10221    *        the starting point, or null if to use iatom or otherwise unnecessary
10222    * @param pt2
10223    *        the target point, if this is a point-to-point determination, or the
10224    *        offset if not and options is nonzero
10225    * @param type
10226    *        a token type such as T.list or T.array
10227    * @param desc
10228    *        if type == T.nada (0), a name evaluating to a type, or one of the
10229    *        special names: "info", "description", "matrix", "axispoint", or
10230    *        "time" (as in time-reversal); otherwise, if type == T.draw, the root
10231    *        id given to a returned DRAW command set
10232    * @param scaleFactor
10233    *        if nonzero and type == T.draw, a scaling factor to be applied to the
10234    *        rotational vector
10235    * @param nth
10236    *        in the case of a point-to-point determination, the nth matching
10237    *        operator, or 0 for "all"
10238    * @param options
10239    *        if nonzero, a option, currently just T.offset, indicating that pt1
10240    *        is an {i j k} offset from cell 555
10241    * @return string, Object[], or Lst<Object[]>
10242    */
getSymmetryInfo(int iatom, String xyz, int iOp, P3 translation, P3 pt1, P3 pt2, int type, String desc, float scaleFactor, int nth, int options)10243   public Object getSymmetryInfo(int iatom, String xyz, int iOp, P3 translation, P3 pt1,
10244                                 P3 pt2, int type, String desc,
10245                                 float scaleFactor, int nth, int options) {
10246     try {
10247       return getSymTemp().getSymmetryInfoAtom(ms, iatom, xyz, iOp, translation, pt1,
10248           pt2, desc, type, scaleFactor, nth, options);
10249     } catch (Exception e) {
10250       System.out.println("Exception in Viewer.getSymmetryInfo: " + e);
10251       if (!isJS)
10252         e.printStackTrace();
10253       return null;
10254     }
10255   }
10256 
10257   /**
10258    *
10259    * @param i
10260    *        Integer.MIN_VALUE initializes the bond index
10261    */
setModelKitRotateBondIndex(int i)10262   public void setModelKitRotateBondIndex(int i) {
10263     if (modelkit != null) {
10264       modelkit.setProperty("rotateBondIndex", Integer.valueOf(i));
10265     }
10266   }
10267 
10268   private Map<String, Object> macros;
10269 
10270   /**
10271    * retrieve macros.json from the directory
10272    *
10273    * @param key
10274    * @return the macro path
10275    */
10276   @SuppressWarnings("unchecked")
getMacro(String key)10277   public String getMacro(String key) {
10278     if (macros == null || macros.isEmpty()) {
10279       try {
10280         String s = getAsciiFileOrNull(g.macroDirectory + "/macros.json");
10281         macros = (Map<String, Object>) parseJSON(s);
10282       } catch (Exception e) {
10283         macros = new Hashtable<String, Object>();
10284       }
10285       //    {
10286       //        aflow:{path:"https://chemapps.stolaf.edu/jmol/macros/AFLOW.spt", title:"AFLOW macros"},
10287       //        bz:{path:"https://chemapps.stolaf.edu/jmol/macros/bz.spt", title: "Brillouin Zone/Wigner-Seitz macros"},
10288       //        topology:{path:"https://chemapps.stolaf.edu/jmol/macros/topology.spt", title: "Topology CIF macros"},
10289       //        topond:{path:"https://chemapps.stolaf.edu/jmol/macros/topond.spt", title: "CRYSTAL/TOPOND macros"},
10290       //        crystal:{path:"https://chemapps.stolaf.edu/jmol/macros/crystal.spt", title: "CRYSTAL macros"}
10291       //     };
10292     }
10293     if (key == null) {
10294       SB s = new SB();
10295       for (String k : macros.keySet()) {
10296         Object a = macros.get(k);
10297         s.append(k).append("\t").appendO(a).append("\n");
10298       }
10299       return s.toString();
10300     }
10301     key = key.toLowerCase();
10302     return macros.containsKey(key)
10303         ? ((Map<String, Object>) macros.get(key)).get("path").toString()
10304         : null;
10305   }
10306 
getInchi(BS atoms, String molData, String options)10307   public String getInchi(BS atoms, String molData, String options) {
10308     try {
10309       JmolInChI inch = this.apiPlatform.getInChI();
10310       if (atoms == null && molData == null)
10311         return "";
10312       if (molData != null) {
10313         if (molData.startsWith("$") || molData.startsWith(":")) {
10314           molData = getFileAsString4(molData, -1, false, false, true, "script");
10315         } else if (!molData.startsWith("InChI=") && molData.indexOf(" ") < 0) {
10316           // assume SMILES
10317           molData = getFileAsString4("$" + molData, -1, false, false, true,
10318               "script");
10319         }
10320       }
10321       return inch.getInchi(this, atoms, molData, options);
10322     } catch (Throwable t) {
10323       return "";
10324     }
10325   }
10326 
10327   private int consoleFontScale = 1;
10328 
getConsoleFontScale()10329   public int getConsoleFontScale() {
10330     return consoleFontScale;
10331   }
10332 
setConsoleFontScale(int scale)10333   public void setConsoleFontScale(int scale) {
10334     consoleFontScale = scale;
10335   }
10336 
confirm(String msg, String msgNo)10337   public int confirm(String msg, String msgNo) {
10338     return apiPlatform.confirm(msg, msgNo);
10339   }
10340 }
10341