1 /* $Author: hansonr $
2  * $Date: 2010-04-22 13:16:44 -0500 (Thu, 22 Apr 2010) $
3  * $Revision: 12904 $
4  *
5  * Copyright (C) 2002-2005  The Jmol Development Team
6  *
7  * Contact: jmol-developers@lists.sf.net
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 package org.jmol.viewer;
25 
26 import java.util.Arrays;
27 import java.util.Date;
28 import java.util.Hashtable;
29 import java.util.Map;
30 
31 import org.jmol.api.JmolDataManager;
32 import org.jmol.api.JmolModulationSet;
33 import org.jmol.api.JmolScriptFunction;
34 import org.jmol.api.SymmetryInterface;
35 import org.jmol.c.PAL;
36 import org.jmol.c.STR;
37 import org.jmol.c.VDW;
38 import org.jmol.modelset.Atom;
39 import org.jmol.modelset.AtomCollection;
40 import org.jmol.modelset.Bond;
41 import org.jmol.modelset.BondSet;
42 import org.jmol.modelset.Measurement;
43 import org.jmol.modelset.Model;
44 import org.jmol.modelset.ModelSet;
45 import org.jmol.modelset.StateScript;
46 import org.jmol.modelset.Text;
47 import org.jmol.modelset.TickInfo;
48 import org.jmol.script.SV;
49 import org.jmol.script.T;
50 import org.jmol.shape.AtomShape;
51 import org.jmol.shape.Axes;
52 import org.jmol.shape.Balls;
53 import org.jmol.shape.Echo;
54 import org.jmol.shape.FontLineShape;
55 import org.jmol.shape.Frank;
56 import org.jmol.shape.Halos;
57 import org.jmol.shape.Hover;
58 import org.jmol.shape.Labels;
59 import org.jmol.shape.Measures;
60 import org.jmol.shape.Shape;
61 import org.jmol.shape.Sticks;
62 import org.jmol.util.BSUtil;
63 import org.jmol.util.C;
64 import org.jmol.util.ColorEncoder;
65 import org.jmol.util.Edge;
66 import org.jmol.util.Escape;
67 import org.jmol.util.Font;
68 import org.jmol.util.GData;
69 import org.jmol.util.Logger;
70 import org.jmol.util.Vibration;
71 
72 import javajs.util.BS;
73 import javajs.util.Lst;
74 import javajs.util.P3;
75 import javajs.util.PT;
76 import javajs.util.SB;
77 
78 /**
79  * StateCreator handles all aspects of working with the "state" as
80  * generally defined, including
81  *
82  *  -- creating the state script
83  *
84  *  -- general output, including logging
85  *
86  *  -- handling undo/redo
87  *
88  *  -- processing SYNC directives
89  *
90  *
91  * Called by reflection only; all state generation script here, for
92  * modularization in JavaScript
93  *
94  *
95  *
96  */
97 public class StateCreator extends JmolStateCreator {
98 
StateCreator()99   public StateCreator() {
100 
101     // by reflection only!
102 
103   }
104 
105   private Viewer vwr;
106 
107   @Override
setViewer(Viewer vwr)108   void setViewer(Viewer vwr) {
109     this.vwr = vwr;
110   }
111 
112 
113   /////////////////// creating the state script ////////////////////
114 
115   @Override
getStateScript(String type, int width, int height)116   String getStateScript(String type, int width, int height) {
117     //System.out.println("vwr getStateInfo " + type);
118     boolean isAll = (type == null || type.equalsIgnoreCase("all"));
119     SB s = new SB();
120     SB sfunc = (isAll ? new SB().append("function _setState() {\n") : null);
121     if (isAll) {
122       s.append(JC.STATE_VERSION_STAMP + Viewer.getJmolVersion() + ";\n");
123       if (vwr.isApplet) {
124         app(s, "# fullName = " + PT.esc(vwr.fullName));
125         app(s, "# documentBase = " + PT.esc(Viewer.appletDocumentBase));
126         app(s, "# codeBase = " + PT.esc(Viewer.appletCodeBase));
127         s.append("\n");
128       }
129     }
130 
131     GlobalSettings global = vwr.g;
132     // window state
133     if (isAll || type.equalsIgnoreCase("windowState"))
134       s.append(getWindowState(sfunc, width, height));
135     //if (isAll)
136     //s.append(getFunctionCalls(null)); // removed in 12.1.16; unnecessary in state
137     // file state
138     if (isAll || type.equalsIgnoreCase("fileState"))
139       s.append(getFileState(sfunc));
140     // all state scripts (definitions, dataFrames, calculations, configurations,
141     // rebonding
142     if (isAll || type.equalsIgnoreCase("definedState"))
143       s.append(getDefinedState(sfunc, true));
144     // numerical values
145     if (isAll || type.equalsIgnoreCase("variableState"))
146       s.append(getParameterState(global, sfunc)); // removed in 12.1.16; unnecessary in state // ARGH!!!
147     if (isAll || type.equalsIgnoreCase("dataState"))
148       s.append(getDataState(sfunc));
149     // connections, atoms, bonds, labels, echos, shapes
150     if (isAll || type.equalsIgnoreCase("modelState"))
151       s.append(getModelState(sfunc, true,
152           vwr.getBooleanProperty("saveProteinStructureState")));
153     // color scheme
154     if (isAll || type.equalsIgnoreCase("colorState"))
155       s.append(getColorState(vwr.cm, sfunc));
156     // frame information
157     if (isAll || type.equalsIgnoreCase("frameState"))
158       s.append(getAnimState(vwr.am, sfunc));
159     // orientation and slabbing
160     if (isAll || type.equalsIgnoreCase("perspectiveState"))
161       s.append(getViewState(vwr.tm, sfunc));
162     // display and selections
163     if (isAll || type.equalsIgnoreCase("selectionState"))
164       s.append(getSelectionState(vwr.slm, sfunc));
165     if (sfunc != null) {
166       app(sfunc, "set refreshing true");
167       app(sfunc, "set antialiasDisplay " + global.antialiasDisplay);
168       app(sfunc, "set antialiasTranslucent " + global.antialiasTranslucent);
169       app(sfunc, "set antialiasImages " + global.antialiasImages);
170       if (vwr.tm.spinOn)
171         app(sfunc, "spin on");
172       sfunc.append("}\n\n_setState;\n");
173     }
174     if (isAll)
175       s.appendSB(sfunc);
176     return s.toString();
177   }
178 
getDataState(SB sfunc)179   private String getDataState(SB sfunc) {
180     SB commands = new SB();
181     boolean haveData = false;
182     String atomProps = getAtomicPropertyState(-1, null);
183     if (atomProps.length() > 0) {
184       haveData = true;
185       commands.append(atomProps);
186     }
187     if (vwr.userVdws != null) {
188       String info = vwr.getDefaultVdwNameOrData(0, VDW.USER,
189           vwr.bsUserVdws);
190       if (info.length() > 0) {
191         haveData = true;
192         commands.append(info);
193       }
194     }
195     if (vwr.nmrCalculation != null)
196       haveData |= vwr.nmrCalculation.getState(commands);
197     if (vwr.dm != null)
198       haveData |= vwr.dm.getDataState(this, commands);
199     if (!haveData)
200       return "";
201 
202     String cmd = "";
203     if (sfunc != null) {
204       sfunc.append("  _setDataState;\n");
205       cmd = "function _setDataState() {\n";
206       commands.append("}\n\n");
207     }
208     return cmd + commands.toString();
209   }
210 
getDefinedState(SB sfunc, boolean isAll)211   private String getDefinedState(SB sfunc, boolean isAll) {
212     ModelSet ms = vwr.ms;
213     int len = ms.stateScripts.size();
214     if (len == 0)
215       return "";
216 
217     boolean haveDefs = false;
218     SB commands = new SB();
219     String cmd;
220     for (int i = 0; i < len; i++) {
221       StateScript ss = ms.stateScripts.get(i);
222       if (ss.inDefinedStateBlock && (cmd = ss.toString()).length() > 0) {
223         app(commands, cmd);
224         haveDefs = true;
225       }
226     }
227     if (!haveDefs)
228       return "";
229     cmd = "";
230     if (isAll && sfunc != null) {
231       sfunc.append("  _setDefinedState;\n");
232       cmd = "function _setDefinedState() {\n\n";
233     }
234     if (sfunc != null)
235       commands.append("\n}\n\n");
236     return cmd + commands.toString();
237   }
238 
239   @Override
getModelState(SB sfunc, boolean isAll, boolean withProteinStructure)240   String getModelState(SB sfunc, boolean isAll, boolean withProteinStructure) {
241     SB commands = new SB();
242     if (isAll && sfunc != null) {
243       sfunc.append("  _setModelState;\n");
244       commands.append("function _setModelState() {\n");
245     }
246     String cmd;
247 
248     // connections
249 
250     ModelSet ms = vwr.ms;
251     Bond[] bonds = ms.bo;
252     Model[] models = ms.am;
253     int modelCount = ms.mc;
254 
255     if (isAll) {
256 
257       int len = ms.stateScripts.size();
258       for (int i = 0; i < len; i++) {
259         StateScript ss = ms.stateScripts.get(i);
260         if (!ss.inDefinedStateBlock && (cmd = ss.toString()).length() > 0) {
261           app(commands, cmd);
262         }
263       }
264 
265       SB sb = new SB();
266       for (int i = 0; i < ms.bondCount; i++)
267         if (!models[bonds[i].atom1.mi].isModelKit)
268           if (bonds[i].isHydrogen() || (bonds[i].order & Edge.BOND_NEW) != 0) {
269             Bond bond = bonds[i];
270             int index = bond.atom1.i;
271             if (bond.atom1.group.isAdded(index))
272               index = -1 - index;
273             sb.appendI(index).appendC('\t').appendI(bond.atom2.i).appendC('\t')
274                 .appendI(bond.order & ~Edge.BOND_NEW).appendC('\t')
275                 .appendF(bond.mad / 1000f).appendC('\t')
276                 .appendF(bond.getEnergy()).appendC('\t')
277                 .append(Edge.getBondOrderNameFromOrder(bond.order))
278                 .append(";\n");
279           }
280       if (sb.length() > 0)
281         commands.append("data \"connect_atoms\"\n").appendSB(sb)
282             .append("end \"connect_atoms\";\n");
283       commands.append("\n");
284     }
285 
286     // bond visibility
287 
288     if (ms.haveHiddenBonds) {
289       BondSet bs = new BondSet();
290       for (int i = ms.bondCount; --i >= 0;)
291         if (bonds[i].mad != 0
292             && (bonds[i].shapeVisibilityFlags & Bond.myVisibilityFlag) == 0)
293           bs.set(i);
294       if (bs.isEmpty())
295         ms.haveHiddenBonds = false;
296       else
297         commands.append("  hide ").append(Escape.eBond(bs)).append(";\n");
298     }
299 
300     // shape construction
301 
302     vwr.setModelVisibility();
303 
304     // unnecessary. Removed in 11.5.35 -- oops!
305 
306     if (withProteinStructure)
307       commands.append(ms
308           .getProteinStructureState(null, isAll ? T.all : T.state));
309 
310     // introduced in 14.4.2
311     for (int i = 0; i < modelCount; i++)
312       if (models[i].mat4 != null)
313         commands.append("  frame orientation " + ms.getModelNumberDotted(i)
314             + Escape.matrixToScript(models[i].mat4) + ";\n");
315 
316     getShapeStatePriv(commands, isAll, Integer.MAX_VALUE);
317 
318     if (isAll) {
319       boolean needOrientations = false;
320       for (int i = 0; i < modelCount; i++)
321         if (models[i].isJmolDataFrame) {
322           needOrientations = true;
323           break;
324         }
325       SB sb = new SB();
326       for (int i = 0; i < modelCount; i++) {
327         Model m = models[i];
328         sb.setLength(0);
329         String s = (String) ms.getInfo(i, "modelID");
330         if (s != null && !s.equals(ms.getInfo(i, "modelID0")))
331           sb.append("  frame ID ").append(PT.esc(s)).append(";\n");
332         String t = ms.frameTitles[i];
333         if (t != null && t.length() > 0)
334           sb.append("  frame title ").append(PT.esc(t)).append(";\n");
335         if (needOrientations && m.orientation != null
336             && !ms.isTrajectorySubFrame(i))
337           sb.append("  ").append(m.orientation.getMoveToText(false))
338               .append(";\n");
339         if (m.frameDelay != 0 && !ms.isTrajectorySubFrame(i))
340           sb.append("  frame delay ").appendF(m.frameDelay / 1000f)
341               .append(";\n");
342         if (m.simpleCage != null) {
343           sb.append("  unitcell ")
344               .append(Escape.eAP(m.simpleCage.getUnitCellVectors()))
345               .append(";\n");
346           getShapeStatePriv(sb, isAll, JC.SHAPE_UCCAGE);
347         }
348         if (sb.length() > 0)
349           commands.append("  frame " + ms.getModelNumberDotted(i) + ";\n")
350               .appendSB(sb);
351       }
352 
353       boolean loadUC = false;
354       if (ms.unitCells != null) {
355         boolean haveModulation = false;
356         for (int i = 0; i < modelCount; i++) {
357           SymmetryInterface symmetry = ms.getUnitCell(i);
358           if (symmetry == null)
359             continue;
360           sb.setLength(0);
361           if (symmetry.getState(sb)) {
362             loadUC = true;
363             commands.append("  frame ").append(ms.getModelNumberDotted(i))
364                 .appendSB(sb).append(";\n");
365           }
366           haveModulation |= (vwr.ms.getLastVibrationVector(i, T.modulation) >= 0);
367         }
368         if (loadUC)
369           vwr.shm.loadShape(JC.SHAPE_UCCAGE); // just in case
370         getShapeStatePriv(commands, isAll, JC.SHAPE_UCCAGE);
371         if (haveModulation) {
372           Map<String, BS> temp = new Hashtable<String, BS>();
373           int ivib;
374           for (int i = modelCount; --i >= 0;) {
375             if ((ivib = vwr.ms.getLastVibrationVector(i, T.modulation)) >= 0)
376               for (int j = models[i].firstAtomIndex; j <= ivib; j++) {
377                 JmolModulationSet mset = ms.getModulation(j);
378                 if (mset != null)
379                   BSUtil.setMapBitSet(temp, j, j, mset.getState());
380               }
381           }
382           commands.append(getCommands(temp, null, "select"));
383         }
384       }
385       commands.append("  set fontScaling " + vwr.getBoolean(T.fontscaling)
386           + ";\n");
387       //      if (vwr.getBoolean(T.modelkitmode))
388       //      commands.append("  set modelKitMode true;\n");
389     }
390     if (sfunc != null)
391       commands.append("\n}\n\n");
392     return commands.toString();
393   }
394 
getWindowState(SB sfunc, int width, int height)395   private String getWindowState(SB sfunc, int width, int height) {
396     GlobalSettings global = vwr.g;
397     SB str = new SB();
398     if (sfunc != null) {
399       sfunc
400           .append("  initialize;\n  set refreshing false;\n  _setWindowState;\n");
401       str.append("\nfunction _setWindowState() {\n");
402     }
403     if (width != 0)
404       str.append("# preferredWidthHeight ").appendI(width).append(" ").appendI(
405           height).append(";\n");
406     str.append("# width ")
407         .appendI(width == 0 ? vwr.getScreenWidth() : width).append(
408             ";\n# height ").appendI(
409             height == 0 ? vwr.getScreenHeight() : height).append(";\n");
410     app(str, "stateVersion = " + JC.versionInt);
411     app(str, "background " + Escape.escapeColor(global.objColors[0]));
412     for (int i = 1; i < StateManager.OBJ_MAX; i++)
413       if (global.objColors[i] != 0)
414         app(str, StateManager.getObjectNameFromId(i) + "Color = \""
415             + Escape.escapeColor(global.objColors[i]) + '"');
416     if (global.backgroundImageFileName != null) {
417       app(str, "background IMAGE "
418           + (global.backgroundImageFileName.startsWith(";base64,") ? "" : "/*file*/")
419           + PT.esc(global.backgroundImageFileName));
420     }
421     str.append(getLightingState(false));
422     //app(str, "statusReporting  = " + global.statusReporting);
423     if (sfunc != null)
424       str.append("}\n\n");
425     return str.toString();
426   }
427 
428   @Override
getLightingState(boolean isAll)429   String getLightingState(boolean isAll) {
430     SB str = new SB();
431     GData g = vwr.gdata;
432     app(str, "set ambientPercent " + g.getAmbientPercent());
433     app(str, "set diffusePercent " + g.getDiffusePercent());
434     app(str, "set specular " + g.getSpecular());
435     app(str, "set specularPercent " + g.getSpecularPercent());
436     app(str, "set specularPower " + g.getSpecularPower());
437     int se = g.getSpecularExponent();
438     int pe = g.getPhongExponent();
439     app(str, (Math.pow(2, se) == pe ? "set specularExponent " + se :  "set phongExponent " + pe));
440     app(str, "set celShading " + g.getCel());
441     app(str, "set celShadingPower " + g.getCelPower());
442     app(str, "set zShadePower " + vwr.g.zShadePower);
443     if (isAll)
444       getZshadeState(str, vwr.tm, true);
445     return str.toString();
446   }
447 
getFileState(SB sfunc)448   private String getFileState(SB sfunc) {
449     SB commands = new SB();
450     if (sfunc != null) {
451       sfunc.append("  _setFileState;\n");
452       commands.append("function _setFileState() {\n\n");
453     }
454     if (commands.indexOf("append") < 0
455         && vwr.getModelSetFileName().equals(JC.ZAP_TITLE))
456       commands.append("  zap;\n");
457     appendLoadStates(commands);
458     if (sfunc != null)
459       commands.append("\n}\n\n");
460     return commands.toString();
461   }
462 
appendLoadStates(SB cmds)463   private void appendLoadStates(SB cmds) {
464     Map<String, Boolean> ligandModelSet = vwr.ligandModelSet;
465     if (ligandModelSet != null) {
466       for (String key : ligandModelSet.keySet()) {
467         String data = (String) vwr.ligandModels.get(key + "_data");
468         if (data != null)
469           cmds.append("  ").append(
470               Escape.encapsulateData("ligand_" + key, data.trim() + "\n", JmolDataManager.DATA_TYPE_STRING));
471         data = (String) vwr.ligandModels.get(key + "_file");
472         if (data != null)
473           cmds.append("  ").append(
474               Escape.encapsulateData("file_" + key, data.trim() + "\n", JmolDataManager.DATA_TYPE_STRING));
475       }
476     }
477     SB commands = new SB();
478     ModelSet ms = vwr.ms;
479     Model[] models = ms.am;
480     int modelCount = ms.mc;
481     for (int i = 0; i < modelCount; i++) {
482       if (ms.isJmolDataFrameForModel(i) || ms.isTrajectorySubFrame(i))
483         continue;
484       Model m = models[i];
485       int pt = commands.indexOf(m.loadState);
486       if (pt < 0 || pt != commands.lastIndexOf(m.loadState))
487         commands.append(models[i].loadState);
488       if (models[i].isModelKit) {
489         BS bs = ms.getModelAtomBitSetIncludingDeleted(i, false);
490         if (ms.tainted != null) {
491           if (ms.tainted[AtomCollection.TAINT_COORD] != null)
492             ms.tainted[AtomCollection.TAINT_COORD].andNot(bs);
493           if (ms.tainted[AtomCollection.TAINT_ELEMENT] != null)
494             ms.tainted[AtomCollection.TAINT_ELEMENT].andNot(bs);
495         }
496         m.loadScript = new SB();
497         getInlineData(commands, vwr.getModelExtract(bs, false, true, "MOL"),
498             i > 0, null, null);
499       } else {
500         commands.appendSB(m.loadScript);
501         Lst<String> auxFiles = (Lst<String>) m.auxiliaryInfo.get("auxFiles");
502         if (auxFiles != null) {
503           for (int j = 0; j < auxFiles.size(); j++)
504             commands.append(";#FILE1=" + PT.esc(auxFiles.get(j)) + ";");
505         }
506       }
507     }
508     String s = commands.toString();
509     // add a zap command before the first load command.
510     if (s.indexOf("data \"append ") < 0) {
511       int i = s.indexOf("load /*data*/");
512       int j = s.indexOf("load /*file*/");
513       if (j >= 0 && j < i)
514         i = j;
515       if ((j = s.indexOf("load \"@")) >= 0 && j < i)
516         i = j;
517       if (i >= 0)
518         s = s.substring(0, i) + "zap;" + s.substring(i);
519     }
520     cmds.append(s);
521   }
522 
523   @Override
getInlineData(SB loadScript, String strModel, boolean isAppend, Integer appendToModelIndex, String loadFilter)524   public void getInlineData(SB loadScript, String strModel, boolean isAppend, Integer appendToModelIndex, String loadFilter) {
525     String tag = (isAppend ? "append"
526         + (appendToModelIndex != null && appendToModelIndex.intValue() != vwr.ms.mc - 1 ?  " modelindex=" + appendToModelIndex : "")
527         : "model") + " inline";
528     loadScript.append("load /*data*/ data \"")
529         .append(tag).append("\"\n")
530         .append(strModel).append("end \"").append(tag)
531         .append(loadFilter == null || loadFilter.length() == 0 ? "" : " filter" + PT.esc(loadFilter))
532         .append("\";");
533   }
534 
getColorState(ColorManager cm, SB sfunc)535   private String getColorState(ColorManager cm, SB sfunc) {
536     SB s = new SB();
537     int n = getCEState(cm.ce, s);
538     //String colors = getColorSchemeList(getColorSchemeArray(USER));
539     //if (colors.length() > 0)
540     //s.append("userColorScheme = " + colors + ";\n");
541     if (n > 0 && sfunc != null)
542       sfunc.append("\n  _setColorState\n");
543     return (n > 0 && sfunc != null ? "function _setColorState() {\n"
544         + s.append("}\n\n").toString() : s.toString());
545   }
546 
getCEState(ColorEncoder p, SB s)547   private int getCEState(ColorEncoder p, SB s) {
548     int n = 0;
549     for (Map.Entry<String, int[]> entry : p.schemes.entrySet()) {
550       String name = entry.getKey();
551       if (name.length() > 0 & n++ >= 0)
552         s.append("color \"" + name + "="
553             + ColorEncoder.getColorSchemeList(entry.getValue()) + "\";\n");
554     }
555     return n;
556   }
557 
getAnimState(AnimationManager am, SB sfunc)558   private String getAnimState(AnimationManager am, SB sfunc) {
559     int modelCount = vwr.ms.mc;
560     if (modelCount < 2)
561       return "";
562     SB commands = new SB();
563     if (sfunc != null) {
564       sfunc.append("  _setFrameState;\n");
565       commands.append("function _setFrameState() {\n");
566     }
567     commands.append("# frame state;\n");
568     commands.append("# modelCount ").appendI(modelCount).append(";\n# first ")
569         .append(vwr.getModelNumberDotted(0)).append(";\n# last ")
570         .append(vwr.getModelNumberDotted(modelCount - 1)).append(";\n");
571     if (am.backgroundModelIndex >= 0)
572       app(commands,
573           "set backgroundModel "
574               + vwr.getModelNumberDotted(am.backgroundModelIndex));
575     if (vwr.tm.bsFrameOffsets != null) {
576       app(commands, "frame align " + Escape.eBS(vwr.tm.bsFrameOffsets));
577     } else if (vwr.ms.translations != null) {
578       for (int i = modelCount; --i >= 0;) {
579         P3 t = (vwr.ms.getTranslation(i));
580         if (t != null)
581           app(commands, "frame " + vwr.ms.getModelNumberDotted(i) + " align "
582               + t);
583       }
584     }
585     app(commands,
586         "frame RANGE " + am.getModelSpecial(AnimationManager.FRAME_FIRST) + " "
587             + am.getModelSpecial(AnimationManager.FRAME_LAST));
588     app(commands, "animation DIRECTION "
589         + (am.animationDirection == 1 ? "+1" : "-1"));
590     app(commands, "animation FPS " + am.animationFps);
591     app(commands, "animation MODE " + T.nameOf(am.animationReplayMode) + " "
592         + am.firstFrameDelay + " " + am.lastFrameDelay);
593     if (am.morphCount > 0)
594       app(commands, "animation MORPH " + am.morphCount);
595     boolean showModel = true;
596     if (am.animationFrames != null) {
597       app(commands, "anim frames " + Escape.eAI(am.animationFrames));
598       int i = am.caf;
599       app(commands, "frame " + (i + 1));
600       showModel = (am.cmi != am.modelIndexForFrame(i));
601     }
602     if (showModel) {
603       String s = am.getModelSpecial(AnimationManager.MODEL_CURRENT);
604       app(commands, s.equals("0") ? "frame *" : "model " + s);
605     }
606     app(commands, "animation "
607         + (!am.animationOn ? "OFF" : am.currentDirection == 1 ? "PLAY"
608             : "PLAYREV"));
609     if (am.animationOn && am.animationPaused)
610       app(commands, "animation PAUSE");
611     if (sfunc != null)
612       commands.append("}\n\n");
613     return commands.toString();
614   }
615 
616   /**
617    * note that these are not user variables, only global jmol parameters
618    *
619    * @param global
620    * @param sfunc
621    * @return String
622    */
getParameterState(GlobalSettings global, SB sfunc)623   private String getParameterState(GlobalSettings global, SB sfunc) {
624     String[] list = new String[global.htBooleanParameterFlags.size()
625         + global.htNonbooleanParameterValues.size()];
626     SB commands = new SB();
627     boolean isState = (sfunc != null);
628     if (isState) {
629       sfunc.append("  _setParameterState;\n");
630       commands.append("function _setParameterState() {\n\n");
631     }
632     int n = 0;
633     //booleans
634     for (String key : global.htBooleanParameterFlags.keySet())
635       if (GlobalSettings.doReportProperty(key))
636         list[n++] = "set " + key + " "
637             + global.htBooleanParameterFlags.get(key);
638     for (String key : global.htNonbooleanParameterValues.keySet())
639       if (GlobalSettings.doReportProperty(key)) {
640         Object value = global.htNonbooleanParameterValues.get(key);
641         if (key.charAt(0) == '=') {
642           //save as =xxxx if you don't want "set" to be there first
643           // (=color [element], =frame ...; set unitcell) -- see Viewer.java
644           key = key.substring(1);
645         } else {
646           // one error here is that defaultLattice is being saved as the
647           // escaped string set defaultLattice "{...}", which actually is not read
648           // and was being improperly read as "{1 1 1}".
649           // leaving it here as it is, now always setting  {0 0 0}
650           // otherwise we will break states
651           key = (key.indexOf("default") == 0 ? " " : "") + "set " + key;
652           value = Escape.e(value);
653         }
654         list[n++] = key + " " + value;
655       }
656     switch (global.axesMode) {
657     case T.axesunitcell:
658       list[n++] = "set axes unitcell";
659       break;
660     case T.axesmolecular:
661       list[n++] = "set axes molecular";
662       break;
663     default:
664       list[n++] = "set axes window";
665     }
666 
667     Arrays.sort(list, 0, n);
668     for (int i = 0; i < n; i++)
669       if (list[i] != null)
670         app(commands, list[i]);
671 
672     String s = StateManager.getVariableList(global.htUserVariables, 0, false,
673         true);
674     if (s.length() > 0) {
675       commands.append("\n#user-defined atom sets; \n");
676       commands.append(s);
677     }
678 
679     // label defaults
680 
681     if (vwr.shm.getShape(JC.SHAPE_LABELS) != null)
682       commands
683           .append(getDefaultLabelState((Labels) vwr.shm.shapes[JC.SHAPE_LABELS]));
684 
685     // structure defaults
686 
687     if (global.haveSetStructureList) {
688       Map<STR, float[]> slist = global.structureList;
689       commands.append("struture HELIX set "
690           + Escape.eAF(slist.get(STR.HELIX)));
691       commands.append("struture SHEET set "
692           + Escape.eAF(slist.get(STR.SHEET)));
693       commands.append("struture TURN set "
694           + Escape.eAF(slist.get(STR.TURN)));
695     }
696     if (sfunc != null)
697       commands.append("\n}\n\n");
698     return commands.toString();
699   }
700 
getDefaultLabelState(Labels l)701   private String getDefaultLabelState(Labels l) {
702     SB s = new SB().append("\n# label defaults;\n");
703     app(s, "select none");
704     app(s, Shape.getColorCommand("label", l.defaultPaletteID,
705         l.defaultColix, l.translucentAllowed));
706     app(s, "background label " + Shape.encodeColor(l.defaultBgcolix));
707     app(s, "set labelOffset " + JC.getXOffset(l.defaultOffset)
708         + " " + (JC.getYOffset(l.defaultOffset)));
709     String align = JC.getHorizAlignmentName(l.defaultAlignment);
710     app(s, "set labelAlignment " + (align.length() < 5 ? "left" : align));
711     String pointer = JC.getPointerName(l.defaultPointer);
712     app(s, "set labelPointer "
713         + (pointer.length() == 0 ? "off" : pointer));
714     if ((l.defaultZPos & JC.LABEL_ZPOS_FRONT) != 0)
715       app(s, "set labelFront");
716     else if ((l.defaultZPos & JC.LABEL_ZPOS_GROUP) != 0)
717       app(s, "set labelGroup");
718     app(s, Shape.getFontCommand("label", Font
719         .getFont3D(l.defaultFontId)));
720     return s.toString();
721   }
722 
getSelectionState(SelectionManager sm, SB sfunc)723   private String getSelectionState(SelectionManager sm, SB sfunc) {
724     SB commands = new SB();
725     if (sfunc != null) {
726       sfunc.append("  _setSelectionState;\n");
727       commands.append("function _setSelectionState() {\n");
728     }
729     if (vwr.ms.trajectory != null)
730       app(commands, vwr.ms.trajectory.getState());
731     Map<String, BS> temp = new Hashtable<String, BS>();
732     String cmd = null;
733     addBs(commands, "hide ", sm.bsHidden);
734     addBs(commands, "subset ", sm.bsSubset);
735     addBs(commands, "delete ", sm.bsDeleted);
736     addBs(commands, "fix ", sm.bsFixed);
737     temp.put("-", vwr.slm.getSelectedAtomsNoSubset());
738     cmd = getCommands(temp, null, "select");
739     if (cmd == null)
740       app(commands, "select none");
741     else
742       commands.append(cmd);
743     app(commands, "set hideNotSelected " + sm.hideNotSelected);
744     commands.append((String) vwr.getShapeProperty(JC.SHAPE_STICKS,
745         "selectionState"));
746     if (vwr.getSelectionHalosEnabled())
747       app(commands, "SelectionHalos ON");
748     if (sfunc != null)
749       commands.append("}\n\n");
750     return commands.toString();
751   }
752 
getViewState(TransformManager tm, SB sfunc)753   private String getViewState(TransformManager tm, SB sfunc) {
754     SB commands = new SB();
755     String moveToText = tm.getMoveToText(0, false);
756     // finalizes transform parameters, in case that has not been done
757     if (sfunc != null) {
758       sfunc.append("  _setPerspectiveState;\n");
759       commands.append("function _setPerspectiveState() {\n");
760     }
761     app(commands, "set perspectiveModel " + tm.perspectiveModel);
762     app(commands, "set scaleAngstromsPerInch "
763         + tm.scale3DAngstromsPerInch);
764     app(commands, "set perspectiveDepth " + tm.perspectiveDepth);
765     app(commands, "set visualRange " + tm.visualRangeAngstroms);
766     if (!tm.isWindowCentered())
767       app(commands, "set windowCentered false");
768     app(commands, "set cameraDepth " + tm.cameraDepth);
769     boolean navigating = (tm.mode == TransformManager.MODE_NAVIGATION);
770     if (navigating)
771       app(commands, "set navigationMode true");
772     app(commands, vwr.ms.getBoundBoxCommand(false));
773     app(commands, "center " + Escape.eP(tm.fixedRotationCenter));
774     commands.append(vwr.getOrientationText(T.name, null, null).toString());
775 
776     app(commands, moveToText);
777 // stereo mode should not be in the state - just a display option
778 //    if (tm.stereoMode != STER.NONE)
779 //      app(commands, "stereo "
780 //          + (tm.stereoColors == null ? tm.stereoMode.getName() : Escape
781 //              .escapeColor(tm.stereoColors[0])
782 //              + " " + Escape.escapeColor(tm.stereoColors[1])) + " "
783 //          + tm.stereoDegrees);
784     if (!navigating && !tm.zoomEnabled)
785       app(commands, "zoom off");
786     commands.append("  slab ").appendI(tm.slabPercentSetting).append(";depth ")
787         .appendI(tm.depthPercentSetting).append(
788             tm.slabEnabled && !navigating ? ";slab on" : "").append(";\n");
789     commands.append("  set slabRange ").appendF(tm.slabRange).append(";\n");
790     if (tm.slabPlane != null)
791       commands.append("  slab plane ").append(Escape.eP4(tm.slabPlane)).append(
792           ";\n");
793     if (tm.depthPlane != null)
794       commands.append("  depth plane ").append(Escape.eP4(tm.depthPlane))
795           .append(";\n");
796     getZshadeState(commands, tm, false);
797     commands.append(getSpinState(true)).append("\n");
798     if (vwr.ms.modelSetHasVibrationVectors() && tm.vibrationOn)
799       app(commands, "set vibrationPeriod " + tm.vibrationPeriod
800           + ";vibration on");
801     boolean slabInternal = (tm.depthPlane != null || tm.slabPlane != null);
802     if (navigating) {
803       commands.append(tm.getNavigationState());
804     }
805     if (!tm.slabEnabled && slabInternal)
806       commands.append("  slab off;\n");
807     if (sfunc != null)
808       commands.append("}\n\n");
809     return commands.toString();
810   }
811 
getZshadeState(SB s, TransformManager tm, boolean isAll)812   private void getZshadeState(SB s, TransformManager tm, boolean isAll) {
813 
814     if (isAll) {
815       app(s,"set zDepth " + tm.zDepthPercentSetting);
816       app(s,"set zSlab " + tm.zSlabPercentSetting);
817       if (!tm.zShadeEnabled)
818         app(s,"set zShade false");
819     }
820     if (tm.zShadeEnabled)
821       app(s, "set zShade true");
822     try {
823       if (tm.zSlabPoint != null)
824         app(s,"set zSlab " + Escape.eP(tm.zSlabPoint));
825     } catch (Exception e) {
826       // don't care
827     }
828   }
829 
830 
831   /**
832    * @param isAll
833    * @return spin state
834    */
835   @Override
getSpinState(boolean isAll)836   String getSpinState(boolean isAll) {
837     TransformManager tm = vwr.tm;
838     String s = "  set spinX " + (int) tm.spinX + "; set spinY "
839         + (int) tm.spinY + "; set spinZ " + (int) tm.spinZ + "; set spinFps "
840         + (int) tm.spinFps + ";";
841     if (!Float.isNaN(tm.navFps))
842       s += "  set navX " + (int) tm.navX + "; set navY " + (int) tm.navY
843           + "; set navZ " + (int) tm.navZ + "; set navFps " + (int) tm.navFps
844           + ";";
845     if (tm.navOn)
846       s += " navigation on;";
847     if (!tm.spinOn)
848       return s;
849     String prefix = (tm.isSpinSelected ? "\n  select "
850         + Escape.eBS(vwr.bsA()) + ";\n  rotateSelected"
851         : "\n ");
852     if (tm.isSpinInternal) {
853       P3 pt = P3.newP(tm.internalRotationCenter);
854       pt.sub(tm.rotationAxis);
855       s += prefix + " spin " + tm.rotationRate + " "
856           + Escape.eP(tm.internalRotationCenter) + " " + Escape.eP(pt);
857     } else if (tm.isSpinFixed) {
858       s += prefix + " spin axisangle " + Escape.eP(tm.rotationAxis) + " "
859           + tm.rotationRate;
860     } else {
861       s += " spin on";
862     }
863     return s + ";";
864   }
865 
866   //// info
867 
868   //// utility methods
869 
870   @Override
getCommands(Map<String, BS> htDefine, Map<String, BS> htMore, String selectCmd)871   String getCommands(Map<String, BS> htDefine, Map<String, BS> htMore,
872                             String selectCmd) {
873     SB s = new SB();
874     String setPrev = getCommands2(htDefine, s, null, selectCmd);
875     if (htMore != null)
876       getCommands2(htMore, s, setPrev, "select");
877     return s.toString();
878   }
879 
getCommands2(Map<String, BS> ht, SB s, String setPrev, String selectCmd)880   private String getCommands2(Map<String, BS> ht, SB s, String setPrev,
881                                      String selectCmd) {
882     if (ht == null)
883       return "";
884     for (Map.Entry<String, BS> entry : ht.entrySet()) {
885       String key = entry.getKey();
886       String set = Escape.eBS(entry.getValue());
887       if (set.length() < 5) // nothing selected
888         continue;
889       set = selectCmd + " " + set;
890       if (!set.equals(setPrev))
891         app(s, set);
892       setPrev = set;
893       if (key.indexOf("-") != 0) // - for key means none required
894         app(s, key);
895     }
896     return setPrev;
897   }
898 
app(SB s, String cmd)899   private void app(SB s, String cmd) {
900     if (cmd.length() != 0)
901       s.append("  ").append(cmd).append(";\n");
902   }
903 
addBs(SB sb, String key, BS bs)904   private void addBs(SB sb, String key, BS bs) {
905     if (bs == null || bs.length() == 0)
906       return;
907     app(sb, key + Escape.eBS(bs));
908   }
909 
getFontState(String myType, Font font3d)910   private String getFontState(String myType, Font font3d) {
911     int objId = StateManager.getObjectIdFromName(myType
912         .equalsIgnoreCase("axes") ? "axis" : myType);
913     if (objId < 0)
914       return "";
915     int mad = vwr.getObjectMad10(objId);
916     SB s = new SB().append("\n");
917     app(s, myType
918         + (mad == 0 ? " off" : mad == 1 ? " on" : mad == -1 ? " dotted"
919             : mad < 20 ? " " + mad : " " + (mad / 20000f)));
920     if (s.length() < 3)
921       return "";
922     String fcmd = Shape.getFontCommand(myType, font3d);
923     if (fcmd.length() > 0)
924       fcmd = "  " + fcmd + ";\n";
925     return (s + fcmd);
926   }
927 
appendTickInfo(String myType, SB sb, TickInfo t)928   private void appendTickInfo(String myType, SB sb, TickInfo t) {
929     sb.append("  ");
930     sb.append(myType);
931     addTickInfo(sb, t, false);
932     sb.append(";\n");
933   }
934 
addTickInfo(SB sb, TickInfo tickInfo, boolean addFirst)935   private static void addTickInfo(SB sb, TickInfo tickInfo, boolean addFirst) {
936     sb.append(" ticks ").append(tickInfo.type).append(" ").append(
937         Escape.eP(tickInfo.ticks));
938     boolean isUnitCell = (tickInfo.scale != null && Float
939         .isNaN(tickInfo.scale.x));
940     if (isUnitCell)
941       sb.append(" UNITCELL");
942     if (tickInfo.tickLabelFormats != null)
943       sb.append(" format ")
944           .append(Escape.eAS(tickInfo.tickLabelFormats, false));
945     if (!isUnitCell && tickInfo.scale != null)
946       sb.append(" scale ").append(Escape.eP(tickInfo.scale));
947     if (addFirst && !Float.isNaN(tickInfo.first) && tickInfo.first != 0)
948       sb.append(" first ").appendF(tickInfo.first);
949     if (tickInfo.reference != null) // not implemented
950       sb.append(" point ").append(Escape.eP(tickInfo.reference));
951   }
952 
getMeasurementState(Measures shape)953   private String getMeasurementState(Measures shape) {
954 
955     Lst<Measurement> mList = shape.measurements;
956     int measurementCount = shape.measurementCount;
957     Font font3d = Measures.font3d;
958     TickInfo ti = shape.defaultTickInfo;
959     SB commands = new SB();
960     app(commands, "measures delete");
961     for (int i = 0; i < measurementCount; i++) {
962       Measurement m = mList.get(i);
963       boolean isProperty = (m.property != null);
964       if (isProperty && Float.isNaN(m.value))
965         continue;
966       int count = m.count;
967       SB sb = new SB().append("measure");
968       if (m.thisID != null)
969         sb.append(" ID ").append(PT.esc(m.thisID));
970       if (m.mad != 0)
971         sb.append(" radius ").appendF(
972             m.thisID == null || m.mad > 0 ? m.mad / 2000f : 0);
973       if (m.colix != 0)
974         sb.append(" color ").append(Escape.escapeColor(C.getArgb(m.colix)));
975       if (m.text != null) {
976         if (m.text.font != null)
977           sb.append(" font ").append(m.text.font.getInfo());
978         if (m.text.align != JC.TEXT_ALIGN_NONE)
979           sb.append(" align ").append(JC.getHorizAlignmentName(m.text.align));
980         if (m.text.pymolOffset != null)
981           sb.append(" offset ").append(Escape.eAF(m.text.pymolOffset));
982       }
983       TickInfo tickInfo = m.tickInfo;
984       if (tickInfo != null)
985         addTickInfo(sb, tickInfo, true);
986       for (int j = 1; j <= count; j++)
987         sb.append(" ").append(m.getLabel(j, true, true));
988       if (isProperty)
989         sb.append(" " + m.property + " value " + (Float.isNaN(m.value) ? 0f : m.value))
990         .append(" " + PT.esc(m.getString()));
991       //sb.append("; # " + shape.getInfoAsString(i));
992       app(commands, sb.toString());
993     }
994     app(commands, Shape.getFontCommand("measures", font3d));
995     int nHidden = 0;
996     Map<String, BS> temp = new Hashtable<String, BS>();
997     BS bs = BS.newN(measurementCount);
998     for (int i = 0; i < measurementCount; i++) {
999       Measurement m = mList.get(i);
1000       if (m.isHidden) {
1001         nHidden++;
1002         bs.set(i);
1003       }
1004       if (shape.bsColixSet != null && shape.bsColixSet.get(i))
1005         BSUtil.setMapBitSet(temp, i, i, Shape.getColorCommandUnk("measure",
1006             m.colix, shape.translucentAllowed));
1007 
1008     }
1009     if (nHidden > 0)
1010       if (nHidden == measurementCount)
1011         app(commands, "measures off; # lines and numbers off");
1012       else
1013         for (int i = 0; i < measurementCount; i++)
1014           if (bs.get(i))
1015             BSUtil.setMapBitSet(temp, i, i, "measure off");
1016     if (ti != null) {
1017       commands.append(" measure ");
1018       addTickInfo(commands, ti, true);
1019       commands.append(";\n");
1020     }
1021     if (shape.mad >= 0)
1022       commands.append(" set measurements ").appendF(shape.mad / 2000f).append(";\n");
1023     String s = getCommands(temp, null, "select measures");
1024     if (s != null && s.length() != 0) {
1025       commands.append(s);
1026       app(commands, "select measures ({null})");
1027     }
1028 
1029     return commands.toString();
1030   }
1031 
1032   private Map<String, BS> temp = new Hashtable<String, BS>();
1033   private Map<String, BS> temp2 = new Hashtable<String, BS>();
1034   private Map<String, BS> temp3 = new Hashtable<String, BS>();
1035 
getShapeStatePriv(SB commands, boolean isAll, int iShape)1036   private void getShapeStatePriv(SB commands, boolean isAll, int iShape) {
1037     Shape[] shapes = vwr.shm.shapes;
1038     if (shapes == null)
1039       return;
1040     int i;
1041     int imax;
1042     if (iShape == Integer.MAX_VALUE) {
1043       i = 0;
1044       imax = JC.SHAPE_MAX;
1045     } else {
1046       imax = (i = iShape) + 1;
1047     }
1048     for (; i < imax; ++i) {
1049       Shape shape = shapes[i];
1050       if (shape != null
1051           && (isAll || i >= JC.SHAPE_MIN_SECONDARY
1052               && i < JC.SHAPE_MAX_SECONDARY)) {
1053         String cmd = getShapeState(shape);
1054         if (cmd != null && cmd.length() > 1)
1055           commands.append(cmd);
1056       }
1057     }
1058     commands.append("  select *;\n");
1059   }
1060 
getBondState(Sticks shape)1061   private String getBondState(Sticks shape) {
1062     BS bsOrderSet = shape.bsOrderSet;
1063     boolean reportAll = shape.reportAll;
1064     clearTemp();
1065     ModelSet modelSet = vwr.ms;
1066     boolean haveTainted = false;
1067     Bond[] bonds = modelSet.bo;
1068     int bondCount = modelSet.bondCount;
1069     short r;
1070 
1071     if (reportAll || shape.bsSizeSet != null) {
1072       int i0 = (reportAll ? bondCount - 1 : shape.bsSizeSet.nextSetBit(0));
1073       for (int i = i0; i >= 0; i = (reportAll ? i - 1 : shape.bsSizeSet
1074           .nextSetBit(i + 1)))
1075         BSUtil.setMapBitSet(temp, i, i, "wireframe "
1076             + ((r = bonds[i].mad) == 1 ? "on" : "" + PT.escF(r / 2000f)));
1077     }
1078     if (reportAll || bsOrderSet != null) {
1079       int i0 = (reportAll ? bondCount - 1 : bsOrderSet.nextSetBit(0));
1080       for (int i = i0; i >= 0; i = (reportAll ? i - 1 : bsOrderSet
1081           .nextSetBit(i + 1))) {
1082         Bond bond = bonds[i];
1083         if (reportAll || (bond.order & Edge.BOND_NEW) == 0)
1084           BSUtil.setMapBitSet(temp, i, i, "bondOrder "
1085               + Edge.getBondOrderNameFromOrder(bond.order));
1086       }
1087     }
1088     if (shape.bsColixSet != null)
1089       for (int i = shape.bsColixSet.nextSetBit(0); i >= 0; i = shape.bsColixSet
1090           .nextSetBit(i + 1)) {
1091         short colix = bonds[i].colix;
1092         if ((colix & C.OPAQUE_MASK) == C.USE_PALETTE)
1093           BSUtil.setMapBitSet(temp, i, i, Shape.getColorCommand("bonds",
1094               PAL.CPK.id, colix, shape.translucentAllowed));
1095         else
1096           BSUtil.setMapBitSet(temp, i, i, Shape.getColorCommandUnk("bonds",
1097               colix, shape.translucentAllowed));
1098       }
1099 
1100     String s = getCommands(temp, null, "select BONDS") + "\n"
1101         + (haveTainted ? getCommands(temp2, null, "select BONDS") + "\n" : "");
1102     clearTemp();
1103     return s;
1104   }
1105 
clearTemp()1106   private void clearTemp() {
1107     temp.clear();
1108     temp2.clear();
1109   }
1110 
getShapeState(Shape shape)1111   private String getShapeState(Shape shape) {
1112     String s;
1113     switch (shape.shapeID) {
1114     case JC.SHAPE_AXES:
1115       s = getAxesState((Axes) shape);
1116       break;
1117     case JC.SHAPE_UCCAGE:
1118       if (!vwr.ms.haveUnitCells)
1119         return "";
1120       String st = s = getFontLineShapeState((FontLineShape) shape);
1121       int iAtom = vwr.am.getUnitCellAtomIndex();
1122       if (iAtom >= 0)
1123         s += "  unitcell ({" + iAtom + "});\n";
1124       SymmetryInterface uc = vwr.getCurrentUnitCell();
1125       if (uc != null) {
1126         s += uc.getUnitCellState();
1127         s += st; // needs to be after this state as well.
1128       }
1129       break;
1130     case JC.SHAPE_BBCAGE:
1131       s = getFontLineShapeState((FontLineShape) shape);
1132       break;
1133     case JC.SHAPE_FRANK:
1134       s = getFontState(shape.myType, ((Frank) shape).baseFont3d);
1135       break;
1136     case JC.SHAPE_MEASURES:
1137       s = getMeasurementState((Measures) shape);
1138       break;
1139     case JC.SHAPE_STARS:
1140     case JC.SHAPE_VECTORS:
1141       s = getAtomShapeState((AtomShape) shape);
1142       break;
1143     case JC.SHAPE_STICKS:
1144       s = getBondState((Sticks) shape);
1145       break;
1146     case JC.SHAPE_ECHO:
1147       Echo es = (Echo) shape;
1148       SB sb = new SB();
1149       sb.append("\n  set echo off;\n");
1150       for (Text t : es.objects.values()) {
1151         sb.append(getTextState(t));
1152         if (t.hidden)
1153           sb.append("  set echo ID ").append(PT.esc(t.target))
1154               .append(" hidden;\n");
1155       }
1156       s = sb.toString();
1157       break;
1158     case JC.SHAPE_HALOS:
1159       Halos hs = (Halos) shape;
1160       s = getAtomShapeState(hs)
1161           + (hs.colixSelection == C.USE_PALETTE ? ""
1162               : hs.colixSelection == C.INHERIT_ALL ? "  color SelectionHalos NONE;\n"
1163                   : Shape.getColorCommandUnk("selectionHalos",
1164                       hs.colixSelection, hs.translucentAllowed) + ";\n");
1165       if (hs.bsHighlight != null)
1166         s += "  set highlight "
1167             + Escape.eBS(hs.bsHighlight)
1168             + "; "
1169             + Shape.getColorCommandUnk("highlight", hs.colixHighlight,
1170                 hs.translucentAllowed) + ";\n";
1171       break;
1172     case JC.SHAPE_HOVER:
1173       clearTemp();
1174       Hover h = (Hover) shape;
1175       if (h.atomFormats != null)
1176         for (int i = vwr.ms.ac; --i >= 0;)
1177           if (h.atomFormats[i] != null)
1178             BSUtil.setMapBitSet(temp, i, i,
1179                 "set hoverLabel " + PT.esc(h.atomFormats[i]));
1180       s = "\n  hover " + PT.esc((h.labelFormat == null ? "" : h.labelFormat))
1181           + ";\n" + getCommands(temp, null, "select");
1182       clearTemp();
1183       break;
1184     case JC.SHAPE_LABELS:
1185       Labels l = (Labels) shape;
1186       if (!l.isActive || l.bsSizeSet == null)
1187         return "";
1188       clearTemp();
1189       for (int i = l.bsSizeSet.nextSetBit(0); i >= 0; i = l.bsSizeSet
1190           .nextSetBit(i + 1)) {
1191         Text t = l.getLabel(i);
1192         String cmd = "label ";
1193         if (t == null) {
1194           cmd += PT.esc(l.formats[i]);
1195         } else {
1196           cmd += PT.esc(t.textUnformatted);
1197           if (t.pymolOffset != null)
1198             cmd += ";set labelOffset " + Escape.eAF(t.pymolOffset);
1199         }
1200         BSUtil.setMapBitSet(temp, i, i, cmd);
1201         if (l.bsColixSet != null && l.bsColixSet.get(i))
1202           BSUtil.setMapBitSet(temp2, i, i, Shape.getColorCommand("label",
1203               l.paletteIDs[i], l.colixes[i], l.translucentAllowed));
1204         if (l.bsBgColixSet != null && l.bsBgColixSet.get(i))
1205           BSUtil.setMapBitSet(temp2, i, i,
1206               "background label " + Shape.encodeColor(l.bgcolixes[i]));
1207         Text text = l.getLabel(i);
1208         float sppm = (text != null ? text.scalePixelsPerMicron : 0);
1209         if (sppm > 0)
1210           BSUtil.setMapBitSet(temp2, i, i, "set labelScaleReference "
1211               + (10000f / sppm));
1212         if (l.offsets != null && l.offsets.length > i) {
1213           int offsetFull = l.offsets[i];
1214           BSUtil.setMapBitSet(
1215               temp2,
1216               i,
1217               i,
1218               "set "
1219                   + (JC.isOffsetAbsolute(offsetFull) ? "labelOffsetAbsolute "
1220                       : "labelOffset ") + JC.getXOffset(offsetFull) + " "
1221                   + JC.getYOffset(offsetFull));
1222           String align = JC.getHorizAlignmentName(offsetFull >> 2);
1223           String pointer = JC.getPointerName(offsetFull);
1224           if (pointer.length() > 0)
1225             BSUtil.setMapBitSet(temp2, i, i, "set labelPointer " + pointer);
1226           if ((offsetFull & JC.LABEL_ZPOS_FRONT) != 0)
1227             BSUtil.setMapBitSet(temp2, i, i, "set labelFront");
1228           else if ((offsetFull & JC.LABEL_ZPOS_GROUP) != 0)
1229             BSUtil.setMapBitSet(temp2, i, i, "set labelGroup");
1230           // labelAlignment must come last, so we put it in a separate hash
1231           // table
1232           if (align.length() > 0)
1233             BSUtil.setMapBitSet(temp3, i, i, "set labelAlignment " + align);
1234         }
1235 
1236         if (l.mads != null && l.mads[i] < 0)
1237           BSUtil.setMapBitSet(temp2, i, i, "set toggleLabel");
1238         if (l.bsFontSet != null && l.bsFontSet.get(i))
1239           BSUtil.setMapBitSet(temp2, i, i,
1240               Shape.getFontCommand("label", Font.getFont3D(l.fids[i])));
1241       }
1242       s = getCommands(temp, temp2, "select")
1243           + getCommands(null, temp3, "select");
1244       temp3.clear();
1245       clearTemp();
1246       break;
1247     case JC.SHAPE_BALLS:
1248       clearTemp();
1249       int ac = vwr.ms.ac;
1250       Atom[] atoms = vwr.ms.at;
1251       Balls balls = (Balls) shape;
1252       short[] colixes = balls.colixes;
1253       byte[] pids = balls.paletteIDs;
1254       float r = 0;
1255       for (int i = 0; i < ac; i++) {
1256         if (atoms[i] != null && shape.bsSizeSet != null && shape.bsSizeSet.get(i)) {
1257           if ((r = atoms[i].madAtom) < 0)
1258             BSUtil.setMapBitSet(temp, i, i, "Spacefill on");
1259           else
1260             BSUtil.setMapBitSet(temp, i, i, "Spacefill " + PT.escF(r / 2000f));
1261         }
1262         if (shape.bsColixSet != null && shape.bsColixSet.get(i)) {
1263           byte pid = atoms[i].paletteID;
1264           if (pid != PAL.CPK.id || C.isColixTranslucent(atoms[i].colixAtom))
1265             BSUtil.setMapBitSet(temp, i, i, Shape.getColorCommand("atoms", pid,
1266                 atoms[i].colixAtom, shape.translucentAllowed));
1267           if (colixes != null && i < colixes.length)
1268             BSUtil.setMapBitSet(temp2, i, i, Shape.getColorCommand("balls",
1269                 pids[i], colixes[i], shape.translucentAllowed));
1270         }
1271       }
1272       s = getCommands(temp, temp2, "select");
1273       clearTemp();
1274       break;
1275     default:
1276       s = shape.getShapeState();
1277       break;
1278     }
1279     return s;
1280   }
1281 
getFontLineShapeState(FontLineShape shape)1282   private String getFontLineShapeState(FontLineShape shape) {
1283     String s = getFontState(shape.myType, shape.font3d);
1284     if (shape.tickInfos == null)
1285       return s;
1286     boolean isOff = (s.indexOf(" off") >= 0);
1287     SB sb = new SB();
1288     sb.append(s);
1289     for (int i = 0; i < 4; i++)
1290       if (shape.tickInfos[i] != null)
1291         appendTickInfo(shape.myType, sb, shape.tickInfos[i]);
1292     if (isOff)
1293       sb.append("  " + shape.myType + " off;\n");
1294     return sb.toString();
1295   }
1296 
getAxesState(Axes axes)1297   private String getAxesState(Axes axes) {
1298     SB sb = new SB();
1299     sb.append(getFontLineShapeState(axes));
1300     sb.append("  axes scale ").appendF(vwr.getFloat(T.axesscale)).append(";\n");
1301     if (axes.fixedOrigin != null)
1302       sb.append("  axes center ")
1303           .append(Escape.eP(axes.fixedOrigin)).append(";\n");
1304     P3 axisXY = axes.axisXY;
1305     if (axisXY.z != 0)
1306       sb.append("  axes position [")
1307           .appendI((int) axisXY.x).append(" ")
1308           .appendI((int) axisXY.y).append(" ")
1309           .append(axisXY.z < 0 ? " %" : "").append("];\n");
1310     String[] labels = axes.labels;
1311     if (labels != null) {
1312       sb.append("  axes labels ");
1313       for (int i = 0; i < labels.length; i++)
1314         if (labels[i] != null)
1315           sb.append(PT.esc(labels[i])).append(" ");
1316       sb.append(";\n");
1317     }
1318     if (axes.axisType != null) {
1319       sb.append("  axes type " + PT.esc(axes.axisType));
1320     }
1321     return sb.toString();
1322   }
1323 
1324 
1325   @Override
1326   public String getAtomShapeState(AtomShape shape) {
1327     // called also by Polyhedra
1328     if (!shape.isActive)
1329       return "";
1330     clearTemp();
1331     String type = JC.shapeClassBases[shape.shapeID];
1332     boolean isVector = (shape.shapeID == JC.SHAPE_VECTORS);
1333     int mad;
1334     if (shape.bsSizeSet != null)
1335       for (int i = shape.bsSizeSet.nextSetBit(0); i >= 0; i = shape.bsSizeSet
1336           .nextSetBit(i + 1))
1337         BSUtil.setMapBitSet(temp, i, i, type
1338             + " " + ((mad = shape.mads[i]) < 0 ? (isVector && mad < -1 ? "" + -mad :  "on") : PT.escF(mad / 2000f)));
1339     if (shape.bsColixSet != null)
1340       for (int i = shape.bsColixSet.nextSetBit(0); i >= 0; i = shape.bsColixSet
1341           .nextSetBit(i + 1))
1342         BSUtil.setMapBitSet(temp2, i, i, Shape.getColorCommand(type,
1343             shape.paletteIDs[i], shape.colixes[i], shape.translucentAllowed));
1344     String s = getCommands(temp, temp2, "select");
1345     clearTemp();
1346     return s;
1347   }
1348 
1349   private String getTextState(Text t) {
1350     SB s = new SB();
1351     String text = t.text;
1352     if (text == null || !t.isEcho || t.target.equals("error"))
1353       return "";
1354     //set echo top left
1355     //set echo myecho x y
1356     //echo .....
1357     boolean isImage = (t.image != null);
1358     //    if (isDefine) {
1359     String strOff = null;
1360     String echoCmd = "set echo ID " + PT.esc(t.target);
1361     switch (t.valign) {
1362     case JC.ECHO_XY:
1363       if (t.movableXPercent == Integer.MAX_VALUE
1364           || t.movableYPercent == Integer.MAX_VALUE) {
1365         strOff = (t.movableXPercent == Integer.MAX_VALUE ? t.movableX + " "
1366             : t.movableXPercent + "% ")
1367             + (t.movableYPercent == Integer.MAX_VALUE ? t.movableY + ""
1368                 : t.movableYPercent + "%");
1369       } else {
1370         strOff = "[" + t.movableXPercent + " " + t.movableYPercent + "%]";
1371       }
1372       //$FALL-THROUGH$
1373     case JC.ECHO_XYZ:
1374       if (strOff == null)
1375         strOff = Escape.eP(t.xyz);
1376       s.append("  ").append(echoCmd).append(" ").append(strOff);
1377       if (t.align != JC.TEXT_ALIGN_LEFT)
1378         s.append(";  ").append(echoCmd).append(" ").append(
1379             JC.getHorizAlignmentName(t.align));
1380       break;
1381     default:
1382       s.append("  set echo ").append(JC.getEchoName(t.valign)).append(" ")
1383           .append(JC.getHorizAlignmentName(t.align));
1384     }
1385     if (t.movableZPercent != Integer.MAX_VALUE)
1386       s.append(";  ").append(echoCmd).append(" depth ").appendI(
1387           t.movableZPercent);
1388     if (isImage)
1389       s.append("; ").append(echoCmd).append(" IMAGE /*file*/");
1390     else
1391       s.append("; echo ");
1392     s.append(PT.esc(text)); // was textUnformatted, but that is not really the STATE
1393     s.append(";\n");
1394     if (isImage && t.imageScale != 1)
1395       s.append("  ").append(echoCmd).append(" scale ").appendF(t.imageScale)
1396           .append(";\n");
1397     if (t.script != null)
1398       s.append("  ").append(echoCmd).append(" script ").append(
1399           PT.esc(t.script)).append(";\n");
1400     if (t.modelIndex >= 0)
1401       s.append("  ").append(echoCmd).append(" model ").append(
1402           vwr.getModelNumberDotted(t.modelIndex)).append(";\n");
1403     if (t.pointerPt != null) {
1404       s.append("  ").append(echoCmd).append(" point ").append(
1405           t.pointerPt instanceof Atom ? "({" + ((Atom) t.pointerPt).i
1406               + "})" : Escape.eP(t.pointerPt)).append(";\n");
1407     }
1408     if (t.pymolOffset != null) {
1409       s.append("  ").append(echoCmd).append(" offset ").append(
1410           Escape.escapeFloatA(t.pymolOffset, true)).append(";\n");
1411     }
1412     //    }
1413     //isDefine and target==top: do all
1414     //isDefine and target!=top: just start
1415     //!isDefine and target==top: do nothing
1416     //!isDefine and target!=top: do just this
1417     //fluke because top is defined with default font
1418     //in initShape(), so we MUST include its font def here
1419     //    if (isDefine != target.equals("top"))
1420     //      return s.toString();
1421     // these may not change much:
1422     t.appendFontCmd(s);
1423     s.append("; color echo");
1424     if (C.isColixTranslucent(t.colix))
1425       s.append(C.getColixTranslucencyLabel(t.colix));
1426     s.append(" ").append(C.getHexCode(t.colix));
1427     if (t.bgcolix != 0) {
1428       s.append("; color echo background ");
1429       if (C.isColixTranslucent(t.bgcolix))
1430         s.append(C.getColixTranslucencyLabel(t.bgcolix)).append(" ");
1431       s.append(C.getHexCode(t.bgcolix));
1432     }
1433     s.append(";\n");
1434     return s.toString();
1435   }
1436 
1437   @Override
1438   String getAllSettings(String prefix) {
1439     GlobalSettings g = vwr.g;
1440     SB commands = new SB();
1441     String[] list = new String[g.htBooleanParameterFlags.size()
1442         + g.htNonbooleanParameterValues.size() + g.htUserVariables.size()];
1443     //booleans
1444     int n = 0;
1445     String _prefix = "_" + prefix;
1446     for (String key : g.htBooleanParameterFlags.keySet()) {
1447       if (prefix == null || key.indexOf(prefix) == 0
1448           || key.indexOf(_prefix) == 0)
1449         list[n++] = (key.indexOf("_") == 0 ? key + " = " : "set " + key + " ")
1450             + g.htBooleanParameterFlags.get(key);
1451     }
1452     //save as _xxxx if you don't want "set" to be there first
1453     for (String key : g.htNonbooleanParameterValues.keySet()) {
1454       if (key.charAt(0) != '@'
1455           && (prefix == null || key.indexOf(prefix) == 0 || key
1456               .indexOf(_prefix) == 0)) {
1457         Object value = g.htNonbooleanParameterValues.get(key);
1458         if (value instanceof String)
1459           value = chop(PT.esc((String) value));
1460         list[n++] = (key.indexOf("_") == 0 ? key + " = " : "set " + key + " ")
1461             + value;
1462       }
1463     }
1464     for (String key : g.htUserVariables.keySet()) {
1465       if (prefix == null || key.indexOf(prefix) == 0) {
1466         SV value = g.htUserVariables.get(key);
1467         String s = value.escape();
1468         list[n++] = key + " " + (key.startsWith("@") ? "" : "= ")
1469             + (value.tok == T.string ? chop(PT.esc(s)) : s);
1470       }
1471     }
1472     Arrays.sort(list, 0, n);
1473     for (int i = 0; i < n; i++)
1474       if (list[i] != null)
1475         app(commands, list[i]);
1476     commands.append("\n");
1477     return commands.toString();
1478   }
1479 
1480   private static String chop(String s) {
1481     int len = s.length();
1482     if (len < 512)
1483       return s;
1484     SB sb = new SB();
1485     String sep = "\"\\\n    + \"";
1486     int pt = 0;
1487     for (int i = 72; i < len; pt = i, i += 72) {
1488       while (s.charAt(i - 1) == '\\')
1489         i++;
1490       sb.append((pt == 0 ? "" : sep)).append(s.substring(pt, i));
1491     }
1492     sb.append(sep).append(s.substring(pt, len));
1493     return sb.toString();
1494   }
1495 
1496   @Override
1497   String getFunctionCalls(String f) {
1498     if (f == null)
1499       f = "";
1500     SB s = new SB();
1501     int pt = f.indexOf("*");
1502     boolean isGeneric = (pt >= 0);
1503     boolean isStatic = (f.indexOf("static_") == 0);
1504     boolean namesOnly = (f.equalsIgnoreCase("names") || f
1505         .equalsIgnoreCase("static_names"));
1506     if (namesOnly)
1507       f = "";
1508     if (isGeneric)
1509       f = f.substring(0, pt);
1510     f = f.toLowerCase();
1511     if (isStatic || f.length() == 0)
1512       addFunctions(s, Viewer.staticFunctions, f, isGeneric, namesOnly);
1513     if (!isStatic || f.length() == 0)
1514       addFunctions(s, vwr.localFunctions, f, isGeneric, namesOnly);
1515     return s.toString();
1516   }
1517 
1518   private void addFunctions(SB s, Map<String, JmolScriptFunction> ht, String selectedFunction,
1519                             boolean isGeneric, boolean namesOnly) {
1520     String[] names = new String[ht.size()];
1521     int n = 0;
1522     for (String name : ht.keySet())
1523       if (selectedFunction.length() == 0 && !name.startsWith("_")
1524           || name.equalsIgnoreCase(selectedFunction) || isGeneric
1525           && name.toLowerCase().indexOf(selectedFunction) == 0)
1526         names[n++] = name;
1527     Arrays.sort(names, 0, n);
1528     for (int i = 0; i < n; i++) {
1529       JmolScriptFunction f = ht.get(names[i]);
1530       s.append(namesOnly ? f.getSignature() : f.toString());
1531       s.appendC('\n');
1532     }
1533   }
1534 
1535   private static boolean isTainted(BS[] tainted, int atomIndex, int type) {
1536     return (tainted != null && tainted[type] != null && tainted[type]
1537         .get(atomIndex));
1538   }
1539 
1540   @Override
1541   String getAtomicPropertyState(int taintWhat, BS bsSelected) {
1542     if (!vwr.g.preserveState)
1543       return "";
1544     BS bs;
1545     SB commands = new SB();
1546     for (int type = 0; type < AtomCollection.TAINT_MAX; type++)
1547       if (taintWhat < 0 || type == taintWhat)
1548         if ((bs = (bsSelected != null ? bsSelected : vwr
1549             .ms.getTaintedAtoms(type))) != null)
1550           getAtomicPropertyStateBuffer(commands, type, bs, null, null);
1551     return commands.toString();
1552   }
1553 
1554   @Override
1555   void getAtomicPropertyStateBuffer(SB commands, int type, BS bs,
1556                                            String label, float[] fData) {
1557     if (!vwr.g.preserveState)
1558       return;
1559     // see setAtomData()
1560     SB s = new SB();
1561     String dataLabel = (label == null ? AtomCollection.userSettableValues[type]
1562         : label)
1563         + " set";
1564     int n = 0;
1565     boolean isDefault = (type == AtomCollection.TAINT_COORD);
1566     Atom[] atoms = vwr.ms.at;
1567     BS[] tainted = vwr.ms.tainted;
1568     if (bs != null)
1569       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
1570         if (atoms[i] == null || atoms[i].isDeleted())
1571           continue;
1572         s.appendI(i + 1).append(" ").append(atoms[i].getElementSymbol())
1573             .append(" ").append(atoms[i].getInfo().replace(' ', '_')).append(
1574                 " ");
1575         switch (type) {
1576         case AtomCollection.TAINT_MAX:
1577           if (i < fData.length) // when data are appended, the array may not
1578             // extend that far
1579             s.appendF(fData[i]);
1580           break;
1581         case AtomCollection.TAINT_ATOMNO:
1582           s.appendI(atoms[i].getAtomNumber());
1583           break;
1584         case AtomCollection.TAINT_CHAIN:
1585           s.append(atoms[i].getChainIDStr());
1586           break;
1587         case AtomCollection.TAINT_RESNO:
1588           s.appendI(atoms[i].group.getResno());
1589           break;
1590         case AtomCollection.TAINT_SEQID:
1591           s.appendI(atoms[i].getSeqID());
1592           break;
1593         case AtomCollection.TAINT_ATOMNAME:
1594           s.append(atoms[i].getAtomName());
1595           break;
1596         case AtomCollection.TAINT_ATOMTYPE:
1597           s.append(atoms[i].getAtomType());
1598           break;
1599         case AtomCollection.TAINT_COORD:
1600           if (isTainted(tainted, i, AtomCollection.TAINT_COORD))
1601             isDefault = false;
1602           s.appendF(atoms[i].x).append(" ").appendF(atoms[i].y).append(" ")
1603               .appendF(atoms[i].z);
1604           break;
1605         case AtomCollection.TAINT_VIBRATION:
1606           Vibration v = atoms[i].getVibrationVector();
1607           if (v == null)
1608             s.append("0 0 0");
1609           else if (Float.isNaN(v.modScale))
1610             s.appendF(v.x).append(" ").appendF(v.y).append(" ").appendF(v.z);
1611           else
1612             s.appendF(PT.FLOAT_MIN_SAFE).append(" ").appendF(PT.FLOAT_MIN_SAFE).append(" ").appendF(v.modScale);
1613           break;
1614         case AtomCollection.TAINT_ELEMENT:
1615           s.appendI(atoms[i].getAtomicAndIsotopeNumber());
1616           break;
1617         case AtomCollection.TAINT_FORMALCHARGE:
1618           s.appendI(atoms[i].getFormalCharge());
1619           break;
1620         case AtomCollection.TAINT_BONDINGRADIUS:
1621           s.appendF(atoms[i].getBondingRadius());
1622           break;
1623         case AtomCollection.TAINT_OCCUPANCY:
1624           s.appendI(atoms[i].getOccupancy100());
1625           break;
1626         case AtomCollection.TAINT_PARTIALCHARGE:
1627           s.appendF(atoms[i].getPartialCharge());
1628           break;
1629         case AtomCollection.TAINT_TEMPERATURE:
1630           s.appendF(atoms[i].getBfactor100() / 100f);
1631           break;
1632         case AtomCollection.TAINT_VALENCE:
1633           s.appendI(atoms[i].getValence());
1634           break;
1635         case AtomCollection.TAINT_VANDERWAALS:
1636           s.appendF(atoms[i].getVanderwaalsRadiusFloat(vwr, VDW.AUTO));
1637           break;
1638         }
1639         s.append(" ;\n");
1640         ++n;
1641       }
1642     if (n == 0)
1643       return;
1644     if (isDefault)
1645       dataLabel += "(default)";
1646     commands.append("\n  DATA \"" + dataLabel + "\"\n").appendI(n).append(
1647         " ;\nJmol Property Data Format 1 -- Jmol ").append(
1648         Viewer.getJmolVersion()).append(";\n");
1649     commands.appendSB(s);
1650     commands.append("  end \"" + dataLabel + "\";\n");
1651   }
1652 
1653 
1654   /////////////////////////////////  undo/redo functions /////////////////////
1655 
1656 
1657   @Override
1658   void undoMoveAction(int action, int n) {
1659     switch (action) {
1660     case T.undomove:
1661     case T.redomove:
1662       switch (n) {
1663       case -2:
1664         vwr.undoClear();
1665         break;
1666       case -1:
1667         (action == T.undomove ? vwr.actionStates : vwr.actionStatesRedo)
1668             .clear();
1669         break;
1670       case 0:
1671         n = Integer.MAX_VALUE;
1672         //$FALL-THROUGH$
1673       default:
1674         if (n > MAX_ACTION_UNDO)
1675           n = (action == T.undomove ? vwr.actionStates
1676               : vwr.actionStatesRedo).size();
1677         for (int i = 0; i < n; i++)
1678           undoMoveActionClear(0, action, true);
1679       }
1680       break;
1681     }
1682   }
1683 
1684   @Override
1685   void undoMoveActionClear(int taintedAtom, int type, boolean clearRedo) {
1686     // called by actionManager
1687     if (!vwr.g.preserveState)
1688       return;
1689     int modelIndex = (taintedAtom >= 0 ? vwr.ms.at[taintedAtom].mi
1690         : vwr.ms.mc - 1);
1691     //System.out.print("undoAction " + type + " " + taintedAtom + " modelkit?"
1692     //    + modelSet.models[modelIndex].isModelkit());
1693     //System.out.println(" " + type + " size=" + actionStates.size() + " "
1694     //    + +actionStatesRedo.size());
1695     switch (type) {
1696     case T.redomove:
1697     case T.undomove:
1698       // from MouseManager
1699       // CTRL-Z: type = 1 UNDO
1700       // CTRL-Y: type = -1 REDO
1701       vwr.stopMinimization();
1702       String s = "";
1703       Lst<String> list1;
1704       Lst<String> list2;
1705       switch (type) {
1706       default:
1707       case T.undomove:
1708         list1 = vwr.actionStates;
1709         list2 = vwr.actionStatesRedo;
1710         break;
1711       case T.redomove:
1712         list1 = vwr.actionStatesRedo;
1713         list2 = vwr.actionStates;
1714         if (vwr.actionStatesRedo.size() == 1)
1715           return;
1716         break;
1717       }
1718       if (list1.size() == 0 || undoWorking)
1719         return;
1720       undoWorking = true;
1721       list2.add(0, list1.removeItemAt(0));
1722       s = vwr.actionStatesRedo.get(0);
1723       if (type == T.undomove && list2.size() == 1) {
1724         // must save current state, coord, etc.
1725         // but this destroys actionStatesRedo
1726         int[] pt = new int[] { 1 };
1727         type = PT.parseIntNext(s, pt);
1728         taintedAtom = PT.parseIntNext(s, pt);
1729         undoMoveActionClear(taintedAtom, type, false);
1730       }
1731       //System.out.println("redo type = " + type + " size=" + actionStates.size()
1732       //    + " " + +actionStatesRedo.size());
1733       if (vwr.ms.am[modelIndex].isModelKit
1734           || s.indexOf("zap ") < 0) {
1735         if (Logger.debugging)
1736           vwr.log(s);
1737         vwr.evalStringQuiet(s);
1738       } else {
1739         // if it's not modelkit mode and we are trying to do a zap, then ignore
1740         // and clear all action states.
1741         vwr.actionStates.clear();
1742       }
1743       break;
1744     default:
1745       if (undoWorking && clearRedo)
1746         return;
1747       undoWorking = true;
1748       BS bs;
1749       SB sb = new SB();
1750       sb.append("#" + type + " " + taintedAtom + " " + (new Date()) + "\n");
1751       if (taintedAtom >= 0) {
1752         bs = vwr.getModelUndeletedAtomsBitSet(modelIndex);
1753         vwr.ms.taintAtoms(bs, type);
1754         sb.append(getAtomicPropertyState(-1, null));
1755       } else {
1756         bs = vwr.getModelUndeletedAtomsBitSet(modelIndex);
1757         sb.append("zap ");
1758         sb.append(Escape.eBS(bs)).append(";");
1759         getInlineData(sb, vwr.getModelExtract(bs, false, true,
1760             "MOL"), true, null, null);
1761         sb.append("set refreshing false;").append(
1762             vwr.acm.getPickingState()).append(
1763             vwr.tm.getMoveToText(0, false)).append(
1764             "set refreshing true;");
1765 
1766       }
1767       if (clearRedo) {
1768         vwr.actionStates.add(0, sb.toString());
1769         vwr.actionStatesRedo.clear();
1770       } else {
1771         vwr.actionStatesRedo.add(1, sb.toString());
1772       }
1773       if (vwr.actionStates.size() == MAX_ACTION_UNDO) {
1774         vwr.actionStates.removeItemAt(MAX_ACTION_UNDO - 1);
1775       }
1776     }
1777     undoWorking = !clearRedo;
1778   }
1779 
1780   private boolean undoWorking = false;
1781   private final static int MAX_ACTION_UNDO = 100;
1782 
1783 
1784 }
1785