1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2010-05-11 15:47:18 -0500 (Tue, 11 May 2010) $ 4 * $Revision: 13064 $ 5 * 6 * Copyright (C) 2000-2005 The Jmol Development Team 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.modelkit; 25 26 import java.util.Hashtable; 27 import java.util.Map; 28 29 import org.jmol.api.SC; 30 import org.jmol.i18n.GT; 31 import org.jmol.modelset.Atom; 32 import org.jmol.modelset.AtomCollection; 33 import org.jmol.modelset.Bond; 34 import org.jmol.modelset.MeasurementPending; 35 import org.jmol.modelset.ModelSet; 36 import org.jmol.popup.JmolGenericPopup; 37 import org.jmol.popup.PopupResource; 38 import org.jmol.script.ScriptEval; 39 import org.jmol.script.T; 40 import org.jmol.util.BSUtil; 41 import org.jmol.util.Edge; 42 import org.jmol.util.Elements; 43 import org.jmol.util.Escape; 44 import org.jmol.util.Logger; 45 import org.jmol.viewer.ActionManager; 46 import org.jmol.viewer.JC; 47 import org.jmol.viewer.MouseState; 48 import org.jmol.viewer.Viewer; 49 50 import javajs.util.AU; 51 import javajs.util.BS; 52 import javajs.util.Lst; 53 import javajs.util.P3; 54 import javajs.util.PT; 55 import javajs.util.SB; 56 import javajs.util.V3; 57 58 /** 59 * An abstract popup class that is instantiated for a given platform and context 60 * as one of: 61 * 62 * <pre> 63 * -- abstract ModelKitPopup 64 * -- AwtModelKitPopup 65 * -- JSModelKitPopup 66 * </pre> 67 * 68 */ 69 70 abstract public class ModelKitPopup extends JmolGenericPopup { 71 menuHidePopup(SC popup)72 abstract protected void menuHidePopup(SC popup); 73 74 private static PopupResource bundle = new ModelKitPopupResourceBundle(null, null); 75 76 // scripting options 77 78 // { "xtalModeMenu", "mkmode_molecular mkmode_view mkmode_edit" }, 79 public static final String MODE_OPTIONS = ";view;edit;molecular;"; 80 public static final String SYMMETRY_OPTIONS = ";none;applylocal;retainlocal;applyfull;"; 81 public static final String UNITCELL_OPTIONS = ";packed;extend;"; 82 public static final String BOOLEAN_OPTIONS = ";autobond;hidden;showsymopinfo;clicktosetelement;addhydrogen;addhydrogens;"; 83 public static final String SET_OPTIONS = ";element;"; 84 85 ////////////// modelkit state ////////////// 86 87 private static final int MAX_LABEL = 32; 88 static final String ATOM_MENU = "atomMenu"; 89 static final String BOND_MENU = "bondMenu"; 90 static final String XTAL_MENU = "xtalMenu"; 91 92 public final static int STATE_BITS_XTAL /* 0b00000000011*/ = 0x03; 93 public final static int STATE_MOLECULAR /* 0b00000000000*/ = 0x00; 94 public final static int STATE_XTALVIEW /* 0b00000000001*/ = 0x01; 95 public final static int STATE_XTALEDIT /* 0b00000000010*/ = 0x02; 96 97 public final static int STATE_BITS_SYM_VIEW /* 0b00000011100*/ = 0x1c; 98 public final static int STATE_SYM_NONE /* 0b00000000000*/ = 0x00; 99 public final static int STATE_SYM_SHOW /* 0b00000001000*/ = 0x08; 100 101 public final static int STATE_BITS_SYM_EDIT /* 0b00011100000*/ = 0xe0; 102 public final static int STATE_SYM_APPLYLOCAL /* 0b00000100000*/ = 0x20; 103 public final static int STATE_SYM_RETAINLOCAL /* 0b00001000000*/ = 0x40; 104 public final static int STATE_SYM_APPLYFULL /* 0b00010000000*/ = 0x80; 105 106 public final static int STATE_BITS_UNITCELL /* 0b11100000000*/ = 0x700; 107 public final static int STATE_UNITCELL_PACKED /* 0b00000000000*/ = 0x000; 108 public final static int STATE_UNITCELL_EXTEND /* 0b00100000000*/ = 0x100; 109 110 private static final P3 Pt000 = new P3(); 111 112 /** 113 * set in assignAtom only 114 */ 115 private boolean allowPopup = true; 116 117 /** 118 * set by MODELKIT [DISPLAY/HIDE] 119 */ 120 private boolean hidden = false; 121 isHidden()122 public boolean isHidden() { 123 return hidden; 124 } 125 126 private boolean hasUnitCell; 127 private String[] allOperators; 128 private int currentModelIndex = -1; 129 130 private boolean alertedNoEdit; 131 132 private String atomHoverLabel = "C", bondHoverLabel = GT.$("increase order"), xtalHoverLabel; 133 private String activeMenu; 134 protected ModelSet lastModelSet; 135 136 private String pickAtomAssignType = "C"; 137 private String lastElementType = "C"; 138 private char pickBondAssignType = 'p'; // increment up 139 private boolean isPickAtomAssignCharge; // pl or mi 140 141 private BS bsHighlight = new BS(); 142 143 private int bondIndex = -1, bondAtomIndex1 = -1, bondAtomIndex2 = -1; 144 145 private BS bsRotateBranch; 146 private int branchAtomIndex; 147 private boolean isRotateBond; 148 149 private int[] screenXY = new int[2]; // for tracking mouse-down on bond 150 151 private Map<String, Object> mkdata = new Hashtable<String, Object>(); 152 153 154 155 private boolean showSymopInfo = true; 156 157 /** 158 * when TRUE, add H atoms to C when added to the modelSet. 159 */ 160 private boolean addXtalHydrogens = true; 161 162 /** 163 * Except for H atoms, do not allow changes to elements just by clicking them. 164 * This protects against doing that inadvertently when editing. 165 * 166 */ 167 private boolean clickToSetElement = true; 168 169 /** 170 * set to true for proximity-based autobonding (prior to 14.32.4/15.2.4 the default was TRUE 171 */ 172 private boolean autoBond = false; 173 174 175 private P3 centerPoint, spherePoint, viewOffset; 176 private float centerDistance; 177 private Object symop; 178 private int centerAtomIndex = -1, secondAtomIndex = -1, atomIndexSphere = -1; 179 private String drawData; 180 private String drawScript; 181 private int iatom0; 182 183 protected SC bondRotationCheckBox, prevBondCheckBox; 184 185 private String bondRotationName = ".modelkitMenu.bondMenu.rotateBondP!RD"; 186 187 private String lastCenter = "0 0 0", lastOffset = "0 0 0"; 188 189 ModelKitPopup()190 public ModelKitPopup() { 191 } 192 193 @Override initialize(Viewer vwr, PopupResource bundle, String title)194 protected void initialize(Viewer vwr, PopupResource bundle, String title) { 195 super.initialize(vwr, bundle, title); 196 initializeForModel(); 197 } 198 199 //////////////// menu creation and update /////////////// 200 201 @Override getBundle(String menu)202 protected PopupResource getBundle(String menu) { 203 return bundle; 204 } 205 initializeForModel()206 public void initializeForModel() { 207 resetBondFields("init"); 208 allOperators = null; 209 currentModelIndex = -999; 210 iatom0 = 0; 211 atomIndexSphere = centerAtomIndex = secondAtomIndex = -1; 212 centerPoint = spherePoint = null; 213 // no! atomHoverLabel = bondHoverLabel = xtalHoverLabel = null; 214 hasUnitCell = (vwr.getCurrentUnitCell() != null); 215 symop = null; 216 setDefaultState(hasUnitCell ? STATE_XTALVIEW : STATE_MOLECULAR); 217 //setProperty("clicktosetelement",Boolean.valueOf(!hasUnitCell)); 218 //setProperty("addhydrogen",Boolean.valueOf(!hasUnitCell)); 219 } 220 221 @Override jpiShow(int x, int y)222 public void jpiShow(int x, int y) { 223 if (!hidden && allowPopup) 224 super.jpiShow(x, y); 225 } 226 227 @Override jpiUpdateComputedMenus()228 public void jpiUpdateComputedMenus() { 229 hasUnitCell = (vwr.getCurrentUnitCell() != null); 230 htMenus.get(XTAL_MENU).setEnabled(hasUnitCell); 231 boolean isOK = true; 232 if (vwr.ms != lastModelSet) { 233 lastModelSet = vwr.ms; 234 isOK = false; 235 // } else if (currentModelIndex == -1 || currentModelIndex != vwr.am.cmi) { 236 // isOK = false; 237 } 238 currentModelIndex = Math.max(vwr.am.cmi, 0); 239 iatom0 = vwr.ms.am[currentModelIndex].firstAtomIndex; 240 if (!isOK) { 241 allOperators = null; 242 updateOperatorMenu(); 243 } 244 updateAllXtalMenuOptions(); 245 } 246 247 @Override appUpdateForShow()248 protected void appUpdateForShow() { 249 jpiUpdateComputedMenus(); 250 } 251 updateOperatorMenu()252 protected void updateOperatorMenu() { 253 if (allOperators != null) 254 return; 255 String data = runScriptBuffered("show symop"); 256 allOperators = PT.split(data.trim().replace('\t', ' '), "\n"); 257 SC menu = htMenus.get("xtalOp!PersistMenu"); 258 if (menu != null) 259 addAllCheckboxItems(menu, allOperators); 260 } 261 addAllCheckboxItems(SC menu, String[] labels)262 private void addAllCheckboxItems(SC menu, String[] labels) { 263 menuRemoveAll(menu, 0); 264 SC subMenu = menu; 265 int pt = (labels.length > MAX_LABEL ? 0 : Integer.MIN_VALUE); 266 for (int i = 0; i < labels.length; i++) { 267 if (pt >= 0 && (pt++ % MAX_LABEL) == 0) { 268 String id = "mtsymop" + pt + "Menu"; 269 subMenu = menuNewSubMenu( 270 (i + 1) + "..." + Math.min(i + MAX_LABEL, labels.length), 271 menuGetId(menu) + "." + id); 272 menuAddSubMenu(menu, subMenu); 273 htMenus.put(id, subMenu); 274 pt = 1; 275 } 276 if (i == 0) 277 menuEnable( 278 menuCreateItem(subMenu, GT.$("none"), "draw sym_* delete", null), 279 true); 280 String sym = labels[i]; // XYZoriginal 281 menuEnable(menuCreateItem(subMenu, sym, sym, 282 subMenu.getName() + "." + "mkop_" + (i + 1)), true); 283 } 284 285 } 286 updateAllXtalMenuOptions()287 protected void updateAllXtalMenuOptions() { 288 289 // "mkaddHydrogens??P!CB", "add hydrogens on new atoms", 290 // "mkclicktosetelement??P!CB", "allow clicking to set atom element", 291 // "mksel_atom", "select atom", 292 // "mksel_position", "select position", 293 // "mkmode_molecular", GT.$("No View/Edit"), 294 // "mksymmetry_none", GT.$("do not apply"), 295 // "mksymmetry_retainLocal", GT.$("retain local"), 296 // "mksymmetry_applyLocal", GT.$("apply local"), 297 // "mksymmetry_applyFull", GT.$("apply full"), 298 // "mkunitcell_extend", GT.$("extend cell"), 299 // "mkunitcell_packed", GT.$("pack cell"), 300 // "mkasymmetricUnit", GT.$("asymmetric unit"), 301 // "mkallAtoms", GT.$("all atoms"), 302 303 // mode 304 String text = ""; 305 switch (getMKState()) { 306 case STATE_MOLECULAR: 307 text = " (not enabled)"; 308 break; 309 case STATE_XTALVIEW: 310 text = " (view)"; 311 break; 312 case STATE_XTALEDIT: 313 text = " (edit)"; 314 break; 315 } 316 setLabel("xtalModePersistMenu", "Crystal Mode: " + text); 317 318 // atom or position 319 text = (centerAtomIndex < 0 && centerPoint == null ? "(not selected)" 320 : centerAtomIndex >= 0 ? vwr.getAtomInfo(centerAtomIndex) : centerPoint.toString()); 321 setLabel("xtalSelPersistMenu", "Center: " + text); 322 // operator 323 text = (symop == null || allOperators == null ? "(no operator selected)" : symop instanceof Integer ? allOperators[((Integer) symop).intValue() - 1] : symop.toString()); 324 setLabel("operator", text); 325 326 // editing option 327 switch (getSymEditState()) { 328 case STATE_SYM_NONE: 329 text = "do not apply symmetry"; 330 break; 331 case STATE_SYM_RETAINLOCAL: 332 text = "retain local symmetry"; 333 break; 334 case STATE_SYM_APPLYLOCAL: 335 text = "apply local symmetry"; 336 break; 337 case STATE_SYM_APPLYFULL: 338 text = "apply full symmetry"; 339 break; 340 } 341 setLabel("xtalEditOptPersistMenu", "Edit option: " + text); 342 343 // packing 344 switch (getUnitCellState()) { 345 case STATE_UNITCELL_PACKED: 346 text = "packed"; 347 break; 348 case STATE_UNITCELL_EXTEND: 349 text = "unpacked" + (viewOffset == null ? "(no view offset)" : "(view offset=" + viewOffset + ")"); 350 break; 351 } 352 setLabel("xtalPackingPersistMenu", "Packing: " + text); 353 354 } 355 setLabel(String key, String label)356 private void setLabel(String key, String label) { 357 menuSetLabel(htMenus.get(key), label); 358 } 359 360 /** 361 * for FrankRender -- the thin box on the top left 362 * 363 * @return [ "atomMenu" | "bondMenu" | "xtalMenu" | null ] 364 */ getActiveMenu()365 public String getActiveMenu() { 366 return activeMenu; 367 } 368 369 /** 370 * Set the active menu and request a repaint. 371 * 372 * @param name 373 * @return activeMenu or null 374 */ setActiveMenu(String name)375 public String setActiveMenu(String name) { 376 // TODO -- if the hovering is working, this should not be necessary 377 String active = (name.indexOf(XTAL_MENU) >= 0 ? XTAL_MENU 378 : name.indexOf(ATOM_MENU) >= 0 ? ATOM_MENU 379 : name.indexOf(BOND_MENU) >= 0 ? BOND_MENU : null); 380 if (active != null) { 381 activeMenu = active; 382 if ((active == XTAL_MENU) == (getMKState() == STATE_MOLECULAR)) 383 setMKState(active == XTAL_MENU ? STATE_XTALVIEW : STATE_MOLECULAR); 384 vwr.refresh(Viewer.REFRESH_REPAINT, "modelkit"); 385 if (active == BOND_MENU && prevBondCheckBox == null) 386 prevBondCheckBox = htMenus.get("assignBond_pP!RD"); 387 } 388 // System.out.println("active menu is " + activeMenu + " state=" + getMKState()); 389 return active; 390 } 391 392 /** 393 * Set the active menu based on updating a value -- usually by the user, but 394 * also during setup (ignored). 395 * 396 */ 397 @Override appUpdateSpecialCheckBoxValue(SC source, String actionCommand, boolean selected)398 protected void appUpdateSpecialCheckBoxValue(SC source, String actionCommand, 399 boolean selected) { 400 if (!selected) 401 return; 402 String name = source.getName(); 403 if (!updatingForShow && setActiveMenu(name) != null) { 404 exitBondRotation(); 405 String text = source.getText(); 406 // don't turn this into a Java 8 switch -- we need this to still compile in Java 6 for legacy Jmol 407 if (activeMenu == BOND_MENU) { 408 bondHoverLabel = text; 409 if (name.equals(bondRotationName )) { 410 bondRotationCheckBox = source; 411 } else { 412 prevBondCheckBox = source; 413 } 414 } else if (activeMenu == ATOM_MENU) { 415 atomHoverLabel = text; 416 } else if (activeMenu == XTAL_MENU) { 417 xtalHoverLabel = atomHoverLabel = text; 418 } 419 } 420 } 421 422 // { "xtalSelMenu", "mksel_atom mksel_position" }, 423 // { "xtalSelOpMenu", "mkselop_byop xtalOpMenu mkselop_addOffset mkselop_atom2" }, 424 425 private int state = STATE_MOLECULAR & STATE_SYM_NONE & STATE_SYM_APPLYFULL 426 & STATE_UNITCELL_EXTEND; // 0x00 427 private float rotationDeg; 428 isXtalState()429 private boolean isXtalState() { 430 return ((state & STATE_BITS_XTAL) != 0); 431 } 432 setMKState(int bits)433 private void setMKState(int bits) { 434 state = (state & ~STATE_BITS_XTAL) | (hasUnitCell ? bits : STATE_MOLECULAR); 435 } 436 getMKState()437 private int getMKState() { 438 return state & STATE_BITS_XTAL; 439 } 440 setSymEdit(int bits)441 private void setSymEdit(int bits) { 442 state = (state & ~STATE_BITS_SYM_EDIT) | bits; 443 } 444 getSymEditState()445 private int getSymEditState() { 446 return state & STATE_BITS_SYM_EDIT; 447 } 448 setSymViewState(int bits)449 private void setSymViewState(int bits) { 450 state = (state & ~STATE_BITS_SYM_VIEW) | bits; 451 } 452 getSymViewState()453 private int getSymViewState() { 454 return state & STATE_BITS_SYM_VIEW; 455 } 456 setUnitCell(int bits)457 private void setUnitCell(int bits) { 458 state = (state & ~STATE_BITS_UNITCELL) | bits; 459 } 460 getUnitCellState()461 private int getUnitCellState() { 462 return state & STATE_BITS_UNITCELL; 463 } 464 isPickAtomAssignCharge()465 public boolean isPickAtomAssignCharge() { 466 return isPickAtomAssignCharge; 467 } 468 469 /** Get a property of the modelkit. 470 * 471 * @param data a name or an array with [name, value] 472 * @return value 473 */ getProperty(Object data)474 public Object getProperty(Object data) { 475 String key = (data instanceof String ? data : ((Object[]) data)[0]).toString(); 476 Object value = (data instanceof String ? null : ((Object[]) data)[1]); 477 return setProperty(key, value); 478 } 479 480 /** 481 * Modify the state by setting a property -- primarily from CmdExt.modelkit. 482 * 483 * Also can be used for "get" purposes. 484 * 485 * @param name 486 * @param value 487 * @return null or "get" value 488 */ setProperty(String name, Object value)489 public synchronized Object setProperty(String name, Object value) { 490 try { 491 name = name.toLowerCase().intern(); 492 // if (value != null) 493 // System.out.println("ModelKitPopup.setProperty " + name + "=" + value); 494 495 // boolean get/set 496 497 if (name == "addhydrogen" || name == "addhydrogens") { 498 if (value != null) 499 addXtalHydrogens = isTrue(value); 500 return Boolean.valueOf(addXtalHydrogens); 501 } 502 503 if (name == "autobond") { 504 if (value != null) 505 autoBond = isTrue(value); 506 return Boolean.valueOf(autoBond); 507 } 508 509 if (name == "clicktosetelement") { 510 if (value != null) 511 clickToSetElement = isTrue(value); 512 return Boolean.valueOf(clickToSetElement); 513 } 514 515 if (name == "hidden") { 516 if (value != null) 517 hidden = isTrue(value); 518 return Boolean.valueOf(hidden); 519 } 520 521 if (name == "ismolecular") { 522 return Boolean.valueOf(getMKState() == STATE_MOLECULAR); 523 } 524 525 if (name == "showsymopinfo") { 526 if (value != null) 527 showSymopInfo = isTrue(value); 528 return Boolean.valueOf(showSymopInfo); 529 } 530 531 // get only 532 533 if (name == "hoverlabel") { 534 // no setting of this, only getting 535 return getHoverLabel(((Integer) value).intValue()); 536 } 537 538 if (name == "alloperators") { 539 return allOperators; 540 } 541 542 if (name == "data") { 543 return getData(value == null ? null : value.toString()); 544 } 545 546 if (name == "invariant") { 547 int iatom = (value instanceof BS ? ((BS) value).nextSetBit(0) : -1); 548 P3 atom = vwr.ms.getAtom(iatom); 549 if (atom == null) 550 return null; 551 return vwr.getSymmetryInfo(iatom, null, -1, null, atom, atom, T.array, 552 null, 0, 0, 0); 553 } 554 555 if (name == "symop") { 556 setDefaultState(STATE_XTALVIEW); 557 if (value != null) { 558 symop = value; 559 showSymop(symop); 560 } 561 return symop; 562 } 563 564 if (name == "atomtype") { 565 if (value != null) { 566 pickAtomAssignType = (String) value; 567 isPickAtomAssignCharge = (pickAtomAssignType.equals("pl") 568 || pickAtomAssignType.equals("mi")); 569 if (!isPickAtomAssignCharge && !"X".equals(pickAtomAssignType)) { 570 lastElementType = pickAtomAssignType; 571 } 572 } 573 return pickAtomAssignType; 574 } 575 576 if (name == "bondtype") { 577 if (value != null) { 578 String s = ((String) value).substring(0, 1).toLowerCase(); 579 if (" 012345pm".indexOf(s) > 0) 580 pickBondAssignType = s.charAt(0); 581 } 582 return "" + pickBondAssignType; 583 } 584 585 if (name == "bondindex") { 586 if (value != null) { 587 setBondIndex(((Integer) value).intValue(), false); 588 } 589 return (bondIndex < 0 ? null : Integer.valueOf(bondIndex)); 590 } 591 592 if (name == "rotatebondindex") { 593 if (value != null) { 594 setBondIndex(((Integer) value).intValue(), true); 595 } 596 return (bondIndex < 0 ? null : Integer.valueOf(bondIndex)); 597 } 598 599 if (name == "offset") { 600 if (value == "none") { 601 viewOffset = null; 602 } else if (value != null) { 603 viewOffset = (value instanceof P3 ? (P3) value 604 : pointFromTriad(value.toString())); 605 if (viewOffset != null) 606 setSymViewState(STATE_SYM_SHOW); 607 } 608 showXtalSymmetry(); 609 return viewOffset; 610 } 611 612 if (name == "distance") { 613 setDefaultState(STATE_XTALEDIT); 614 float d = (value == null ? Float.NaN 615 : value instanceof Float ? ((Float) value).floatValue() 616 : PT.parseFloat((String) value)); 617 if (!Float.isNaN(d)) { 618 notImplemented("setProperty: distance"); 619 centerDistance = d; 620 } 621 return Float.valueOf(centerDistance); 622 } 623 624 if (name == "point") { 625 if (value != null) { 626 notImplemented("setProperty: point"); 627 setDefaultState(STATE_XTALEDIT); 628 spherePoint = (P3) value; 629 atomIndexSphere = (spherePoint instanceof Atom 630 ? ((Atom) spherePoint).i 631 : -1); 632 } 633 return spherePoint; 634 } 635 636 if (name == "screenxy") { 637 if (value != null) { 638 screenXY = (int[]) value; 639 } 640 return screenXY; 641 } 642 643 // set only (always returning null): 644 645 if (name == "bondatomindex") { 646 int i = ((Integer) value).intValue(); 647 if (i != bondAtomIndex2) 648 bondAtomIndex1 = i; 649 650 bsRotateBranch = null; 651 return null; 652 } 653 654 if (name == "highlight") { 655 if (value == null) 656 bsHighlight = new BS(); 657 else 658 bsHighlight = (BS) value; 659 return null; 660 } 661 if (name == "mode") { // view, edit, or molecular 662 boolean isEdit = ("edit".equals(value)); 663 setMKState("view".equals(value) ? STATE_XTALVIEW 664 : isEdit ? STATE_XTALEDIT : STATE_MOLECULAR); 665 if (isEdit) 666 addXtalHydrogens = false; 667 return null; 668 } 669 670 if (name == "symmetry") { 671 setDefaultState(STATE_XTALEDIT); 672 name = ((String) value).toLowerCase().intern(); 673 setSymEdit(name == "applylocal" ? STATE_SYM_APPLYLOCAL 674 : name == "retainlocal" ? STATE_SYM_RETAINLOCAL 675 : name == "applyfull" ? STATE_SYM_APPLYFULL : 0); 676 showXtalSymmetry(); 677 return null; 678 } 679 680 if (name == "unitcell") { // packed or extend 681 boolean isPacked = "packed".equals(value); 682 setUnitCell(isPacked ? STATE_UNITCELL_PACKED : STATE_UNITCELL_EXTEND); 683 viewOffset = (isPacked ? Pt000 : null); 684 return null; 685 } 686 687 if (name == "center") { 688 setDefaultState(STATE_XTALVIEW); 689 centerPoint = (P3) value; 690 lastCenter = centerPoint.x + " " + centerPoint.y + " " + centerPoint.z; 691 centerAtomIndex = (centerPoint instanceof Atom ? ((Atom) centerPoint).i 692 : -1); 693 atomIndexSphere = -1; 694 secondAtomIndex = -1; 695 processAtomClick(centerAtomIndex); 696 return null; 697 } 698 699 if (name == "scriptassignbond") { 700 // from ActionManger only 701 appRunScript("modelkit assign bond [{" + value + "}] \"" 702 + pickBondAssignType + "\""); 703 return null; 704 } 705 706 // not yet implemented 707 if (name == "addconstraint") { 708 notImplemented("setProperty: addConstraint"); 709 } 710 711 if (name == "removeconstraint") { 712 notImplemented("setProperty: removeConstraint"); 713 } 714 715 if (name == "removeallconstraints") { 716 notImplemented("setProperty: removeAllConstraints"); 717 } 718 719 System.err.println("ModelKitPopup.setProperty? " + name + " " + value); 720 721 } catch (Exception e) { 722 return "?"; 723 } 724 725 return null; 726 } 727 isTrue(Object value)728 private static boolean isTrue(Object value) { 729 return (Boolean.valueOf(value.toString()) == Boolean.TRUE); 730 } 731 732 /** 733 * @param key 734 * @return 735 */ 736 @SuppressWarnings("javadoc") getData(String key)737 private Object getData(String key) { 738 addData("addHydrogens", Boolean.valueOf(addXtalHydrogens)); 739 addData("autobond", Boolean.valueOf(autoBond)); 740 addData("clickToSetElement", Boolean.valueOf(clickToSetElement)); 741 addData("hidden", Boolean.valueOf(hidden)); 742 addData("showSymopInfo", Boolean.valueOf(showSymopInfo)); 743 addData("centerPoint" , centerPoint); 744 addData("centerAtomIndex", Integer.valueOf(centerAtomIndex)); 745 addData("secondAtomIndex", Integer.valueOf(secondAtomIndex)); 746 addData("symop", symop); 747 addData("offset", viewOffset); 748 addData("drawData", drawData); 749 addData("drawScript", drawScript); 750 addData("isMolecular", Boolean.valueOf(getMKState() == STATE_MOLECULAR)); 751 return mkdata; 752 } 753 addData(String key, Object value)754 private void addData(String key, Object value) { 755 mkdata.put(key, value == null ? "null" : value); 756 } 757 758 /** 759 * An atom has been clicked -- handle it. Called from CmdExt.assignAtom 760 * from the script created in ActionManager.assignNew from Actionmanager.checkReleaseAction 761 * 762 * @param atomIndex 763 * @return true if handled 764 */ processAtomClick(int atomIndex)765 private boolean processAtomClick(int atomIndex) { 766 switch (getMKState()) { 767 case STATE_MOLECULAR: 768 return isVwrRotateBond(); 769 case STATE_XTALVIEW: 770 centerAtomIndex = atomIndex; 771 if (getSymViewState() == STATE_SYM_NONE) 772 setSymViewState(STATE_SYM_SHOW); 773 showXtalSymmetry(); 774 return true; 775 case STATE_XTALEDIT: 776 if (atomIndex == centerAtomIndex) 777 return true; 778 notImplemented("edit click"); 779 return false; 780 } 781 notImplemented("atom click unknown XTAL state"); 782 return false; 783 } 784 785 /** 786 * Called by Viewer.hoverOn to set the special label if desired. 787 * 788 * @param atomIndex 789 * @return special label or null 790 */ getHoverLabel(int atomIndex)791 private String getHoverLabel(int atomIndex) { 792 int state = getMKState(); 793 String msg = null; 794 switch (state) { 795 case STATE_XTALVIEW: 796 if (symop == null) 797 symop = Integer.valueOf(1); 798 msg = "view symop " + symop + " for " + vwr.getAtomInfo(atomIndex); 799 break; 800 case STATE_XTALEDIT: 801 msg = "start editing for " + vwr.getAtomInfo(atomIndex); 802 break; 803 case STATE_MOLECULAR: 804 if (isRotateBond) { 805 if (atomIndex == bondAtomIndex1 || atomIndex == bondAtomIndex2) { 806 msg = "rotate branch"; 807 branchAtomIndex = atomIndex; 808 bsRotateBranch = null; 809 } else { 810 msg = "rotate bond"; 811 bsRotateBranch = null; 812 branchAtomIndex = -1; 813 // resetBondFields("gethover"); 814 } 815 } 816 if (bondIndex < 0) { 817 if (atomHoverLabel.length() <= 2) { 818 msg = atomHoverLabel = "Click to change to " + atomHoverLabel 819 + " or drag to add " + atomHoverLabel; 820 } else { 821 msg = atomHoverLabel; 822 vwr.highlight(BSUtil.newAndSetBit(atomIndex)); 823 } 824 } else { 825 if (msg == null) { 826 switch (bsHighlight.cardinality()) { 827 case 0: 828 vwr.highlight(BSUtil.newAndSetBit(atomIndex)); 829 //$FALL-THROUGH$ 830 case 1: 831 if (!isRotateBond) 832 setActiveMenu(ATOM_MENU); 833 if (atomHoverLabel.indexOf("charge") >= 0) { 834 int ch = vwr.ms.at[atomIndex].getFormalCharge(); 835 ch += (atomHoverLabel.indexOf("increase") >= 0 ? 1 :-1); 836 msg = atomHoverLabel + " to " + (ch > 0 ? "+" : "") + ch; 837 } else { 838 msg = atomHoverLabel; 839 } 840 break; 841 case 2: 842 msg = bondHoverLabel; 843 break; 844 } 845 } 846 } 847 break; 848 } 849 850 return msg; 851 } 852 setDefaultState(int mode)853 private void setDefaultState(int mode) { 854 if (!hasUnitCell) 855 mode = STATE_MOLECULAR; 856 if (!hasUnitCell || isXtalState() != hasUnitCell) { 857 setMKState(mode); 858 switch (mode) { 859 case STATE_MOLECULAR: 860 break; 861 case STATE_XTALVIEW: 862 if (getSymViewState() == STATE_SYM_NONE) 863 setSymViewState(STATE_SYM_SHOW); 864 break; 865 case STATE_XTALEDIT: 866 break; 867 } 868 } 869 } 870 871 /////////////////// menu execution ////////////// 872 873 @Override appGetBooleanProperty(String name)874 protected boolean appGetBooleanProperty(String name) { 875 if (name.startsWith("mk")) { 876 return ((Boolean) getProperty(name.substring(2))).booleanValue(); 877 } 878 return vwr.getBooleanProperty(name); 879 } 880 881 /** 882 * From JmolGenericPopup.appRunSpecialCheckBox when name starts with "mk" or has "??" in it. 883 */ 884 @Override getUnknownCheckBoxScriptToRun(SC item, String name, String what, boolean TF)885 public String getUnknownCheckBoxScriptToRun(SC item, String name, String what, 886 boolean TF) { 887 if (name.startsWith("mk")) { 888 processMKPropertyItem(name, TF); 889 return null; 890 } 891 // must be ?? -- atom setting by user input 892 String element = promptUser(GT.$("Element?"), ""); 893 if (element == null || Elements.elementNumberFromSymbol(element, true) == 0) 894 return null; 895 menuSetLabel(item, element); 896 item.setActionCommand("assignAtom_" + element + "P!:??"); 897 atomHoverLabel = "Click or click+drag for " + element; 898 return "set picking assignAtom_" + element; 899 } 900 901 processMKPropertyItem(String name, boolean TF)902 private void processMKPropertyItem(String name, boolean TF) { 903 // set a property 904 // { "xtalOptionsPersistMenu", "mkaddHydrogensCB mkclicktosetelementCB" } 905 name = name.substring(2); 906 int pt = name.indexOf("_"); 907 if (pt > 0) { 908 setProperty(name.substring(0, pt), name.substring(pt + 1)); 909 } else { 910 setProperty(name, Boolean.valueOf(TF)); 911 } 912 } 913 914 /** 915 * Draw the symmetry element 916 */ showXtalSymmetry()917 private void showXtalSymmetry() { 918 String script = null; 919 switch (getSymViewState()) { 920 case STATE_SYM_NONE: 921 script = "draw * delete"; 922 break; 923 case STATE_SYM_SHOW: 924 default: 925 P3 offset = null; 926 if (secondAtomIndex >= 0) { 927 script = "draw ID sym symop " 928 + (centerAtomIndex < 0 ? centerPoint 929 : " {atomindex=" + centerAtomIndex + "}") 930 + " {atomindex=" + secondAtomIndex + "}"; 931 } else { 932 offset = this.viewOffset; 933 if (symop == null) 934 symop = Integer.valueOf(1); 935 int iatom = (centerAtomIndex >= 0 ? centerAtomIndex 936 : centerPoint != null ? -1 : iatom0); // default to first atom 937 script = "draw ID sym symop " 938 + (symop == null ? "1" 939 : symop instanceof String ? "'" + symop + "'" 940 : PT.toJSON(null, symop)) 941 + (iatom < 0 ? centerPoint : " {atomindex=" + iatom + "}") 942 + (offset == null ? "" : " offset " + offset); 943 } 944 drawData = runScriptBuffered(script); 945 drawScript = script; 946 drawData = (showSymopInfo 947 ? drawData.substring(0, drawData.indexOf("\n") + 1) 948 : ""); 949 appRunScript( 950 ";refresh;set echo top right;echo " + drawData.replace('\t', ' ') 951 ); 952 break; 953 } 954 } 955 956 /** 957 * Original ModelKitPopup functionality -- assign an atom. 958 * 959 * @param atomIndex 960 * @param type 961 * @param autoBond 962 * @param addHsAndBond 963 * @param isClick whether this is a click or not 964 */ assignAtom(int atomIndex, String type, boolean autoBond, boolean addHsAndBond, boolean isClick)965 private void assignAtom(int atomIndex, String type, boolean autoBond, 966 boolean addHsAndBond, boolean isClick) { 967 if (isClick) { 968 969 if (isVwrRotateBond()) { 970 bondAtomIndex1 = atomIndex; 971 return; 972 } 973 974 if (processAtomClick(atomIndex) || !clickToSetElement 975 && vwr.ms.getAtom(atomIndex).getElementNumber() != 1) 976 return; 977 978 } 979 Atom atom = vwr.ms.at[atomIndex]; 980 if (atom == null) 981 return; 982 vwr.ms.clearDB(atomIndex); 983 if (type == null) 984 type = "C"; 985 986 // not as simple as just defining an atom. 987 // if we click on an H, and C is being defined, 988 // this sprouts an sp3-carbon at that position. 989 990 BS bs = new BS(); 991 boolean wasH = (atom.getElementNumber() == 1); 992 int atomicNumber = (PT.isUpperCase(type.charAt(0)) 993 ? Elements.elementNumberFromSymbol(type, true) 994 : -1); 995 996 // 1) change the element type or charge 997 998 boolean isDelete = false; 999 if (atomicNumber > 0) { 1000 boolean doTaint = (atomicNumber > 1 || !addHsAndBond); 1001 vwr.ms.setElement(atom, atomicNumber, doTaint); 1002 vwr.shm.setShapeSizeBs(JC.SHAPE_BALLS, 0, vwr.rd, 1003 BSUtil.newAndSetBit(atomIndex)); 1004 vwr.ms.setAtomName(atomIndex, type + atom.getAtomNumber(), doTaint); 1005 if (vwr.getBoolean(T.modelkitmode)) 1006 vwr.ms.am[atom.mi].isModelKit = true; 1007 if (!vwr.ms.am[atom.mi].isModelKit || atomicNumber > 1) 1008 vwr.ms.taintAtom(atomIndex, AtomCollection.TAINT_ATOMNAME); 1009 } else if (type.toLowerCase().equals("pl")) { 1010 atom.setFormalCharge(atom.getFormalCharge() + 1); 1011 } else if (type.toLowerCase().equals("mi")) { 1012 atom.setFormalCharge(atom.getFormalCharge() - 1); 1013 } else if (type.equals("X")) { 1014 isDelete = true; 1015 } else if (!type.equals(".") || !addXtalHydrogens) { 1016 return; // uninterpretable 1017 } 1018 1019 if (!addHsAndBond) 1020 return; 1021 1022 // type = "." is for connect 1023 1024 // 2) delete noncovalent bonds and attached hydrogens for that atom. 1025 1026 vwr.ms.removeUnnecessaryBonds(atom, isDelete); 1027 1028 // 3) adjust distance from previous atom. 1029 1030 float dx = 0; 1031 if (atom.getCovalentBondCount() == 1) 1032 if (wasH) { 1033 dx = 1.50f; 1034 } else if (!wasH && atomicNumber == 1) { 1035 dx = 1.0f; 1036 } 1037 if (dx != 0) { 1038 V3 v = V3.newVsub(atom, vwr.ms.at[atom.getBondedAtomIndex(0)]); 1039 float d = v.length(); 1040 v.normalize(); 1041 v.scale(dx - d); 1042 vwr.ms.setAtomCoordRelative(atomIndex, v.x, v.y, v.z); 1043 } 1044 1045 BS bsA = BSUtil.newAndSetBit(atomIndex); 1046 1047 if (isDelete) { 1048 vwr.deleteAtoms(bsA, false); 1049 } 1050 if (atomicNumber != 1 && autoBond) { 1051 1052 // 4) clear out all atoms within 1.0 angstrom 1053 vwr.ms.validateBspf(false); 1054 bs = vwr.ms.getAtomsWithinRadius(1.0f, bsA, false, null); 1055 bs.andNot(bsA); 1056 if (bs.nextSetBit(0) >= 0) 1057 vwr.deleteAtoms(bs, false); 1058 1059 // 5) attach nearby non-hydrogen atoms (rings) 1060 1061 bs = vwr.getModelUndeletedAtomsBitSet(atom.mi); 1062 bs.andNot(vwr.ms.getAtomBitsMDa(T.hydrogen, null, new BS())); 1063 vwr.ms.makeConnections2(0.1f, 1.8f, 1, T.create, bsA, bs, null, false, 1064 false, 0); 1065 1066 // 6) add hydrogen atoms 1067 1068 } 1069 1070 if (addXtalHydrogens) 1071 vwr.addHydrogens(bsA, Viewer.MIN_SILENT); 1072 } 1073 1074 /** 1075 * Original ModelKit functionality -- assign a bond. 1076 * 1077 * @param bondIndex 1078 * @param type 1079 * @return bit set of atoms to modify 1080 */ assignBond(int bondIndex, char type)1081 private BS assignBond(int bondIndex, char type) { 1082 int bondOrder = type - '0'; 1083 Bond bond = vwr.ms.bo[bondIndex]; 1084 vwr.ms.clearDB(bond.atom1.i); 1085 switch (type) { 1086 case '0': 1087 case '1': 1088 case '2': 1089 case '3': 1090 case '4': 1091 case '5': 1092 break; 1093 case 'p': 1094 case 'm': 1095 bondOrder = Edge.getBondOrderNumberFromOrder(bond.getCovalentOrder()) 1096 .charAt(0) - '0' + (type == 'p' ? 1 : -1); 1097 if (bondOrder > 3) 1098 bondOrder = 1; 1099 else if (bondOrder < 0) 1100 bondOrder = 3; 1101 break; 1102 default: 1103 return null; 1104 } 1105 BS bsAtoms = new BS(); 1106 try { 1107 if (bondOrder == 0) { 1108 BS bs = new BS(); 1109 bs.set(bond.index); 1110 bsAtoms.set(bond.atom1.i); 1111 bsAtoms.set(bond.atom2.i); 1112 vwr.ms.deleteBonds(bs, false); 1113 } else { 1114 bond.setOrder(bondOrder | Edge.BOND_NEW); 1115 if (bond.atom1.getElementNumber() != 1 1116 && bond.atom2.getElementNumber() != 1) { 1117 vwr.ms.removeUnnecessaryBonds(bond.atom1, false); 1118 vwr.ms.removeUnnecessaryBonds(bond.atom2, false); 1119 } 1120 bsAtoms.set(bond.atom1.i); 1121 bsAtoms.set(bond.atom2.i); 1122 } 1123 } catch (Exception e) { 1124 Logger.error("Exception in seBondOrder: " + e.toString()); 1125 } 1126 if (type != '0' && addXtalHydrogens) 1127 vwr.addHydrogens(bsAtoms, Viewer.MIN_SILENT); 1128 return bsAtoms; 1129 } 1130 isVwrRotateBond()1131 private boolean isVwrRotateBond() { 1132 return (vwr.acm.getBondPickingMode() == ActionManager.PICKING_ROTATE_BOND); 1133 } 1134 getRotateBondIndex()1135 public int getRotateBondIndex() { 1136 return (getMKState() == STATE_MOLECULAR && isRotateBond ? bondIndex : -1); 1137 } 1138 1139 /** 1140 * @param where 1141 */ resetBondFields(String where)1142 private void resetBondFields(String where) { 1143 bsRotateBranch = null; 1144 // do not set bondIndex to -1 here 1145 branchAtomIndex = bondAtomIndex1 = bondAtomIndex2 = -1; 1146 } 1147 1148 /** 1149 * Set the bond for rotation -- called by Sticks.checkObjectHovered via 1150 * Viewer.highlightBond. 1151 * 1152 * 1153 * @param index 1154 * @param isRotate 1155 */ setBondIndex(int index, boolean isRotate)1156 private void setBondIndex(int index, boolean isRotate) { 1157 if (!isRotate && isVwrRotateBond()) { 1158 vwr.setModelKitRotateBondIndex(index); 1159 return; 1160 } 1161 1162 boolean haveBond = (bondIndex >= 0); 1163 if (!haveBond && index < 0) 1164 return; 1165 if (index < 0) { 1166 resetBondFields("setbondindex<0"); 1167 return; 1168 } 1169 1170 bsRotateBranch = null; 1171 branchAtomIndex = -1; 1172 bondIndex = index; 1173 isRotateBond = isRotate; 1174 bondAtomIndex1 = vwr.ms.bo[index].getAtomIndex1(); 1175 bondAtomIndex2 = vwr.ms.bo[index].getAtomIndex2(); 1176 setActiveMenu(BOND_MENU); 1177 } 1178 1179 1180 /** 1181 * Actually rotate the bond. Called by ActionManager.checkDragWheelAction. 1182 * 1183 * @param deltaX 1184 * @param deltaY 1185 * @param x 1186 * @param y 1187 * @param forceFull 1188 */ actionRotateBond(int deltaX, int deltaY, int x, int y, boolean forceFull)1189 public void actionRotateBond(int deltaX, int deltaY, int x, int y, boolean forceFull) { 1190 1191 if (bondIndex < 0) 1192 return; 1193 BS bsBranch = bsRotateBranch; 1194 Atom atomFix, atomMove; 1195 ModelSet ms = vwr.ms; 1196 if (forceFull) { 1197 bsBranch = null; 1198 branchAtomIndex = -1; 1199 } 1200 if (bsBranch == null) { 1201 Bond b = ms.bo[bondIndex]; 1202 atomMove = (branchAtomIndex == b.atom1.i ? b.atom1 : b.atom2); 1203 atomFix = (atomMove == b.atom1 ? b.atom2 : b.atom1); 1204 vwr.undoMoveActionClear(atomFix.i, AtomCollection.TAINT_COORD, true); 1205 1206 if (branchAtomIndex >= 0) 1207 bsBranch = vwr.getBranchBitSet(atomMove.i, atomFix.i, true); 1208 if (bsBranch != null) 1209 for (int n = 0, i = atomFix.bonds.length; --i >= 0;) { 1210 if (bsBranch.get(atomFix.getBondedAtomIndex(i)) && ++n == 2) { 1211 bsBranch = null; 1212 break; 1213 } 1214 } 1215 if (bsBranch == null) { 1216 bsBranch = ms.getMoleculeBitSetForAtom(atomFix.i); 1217 } 1218 bsRotateBranch = bsBranch; 1219 bondAtomIndex1 = atomFix.i; 1220 bondAtomIndex2 = atomMove.i; 1221 } else { 1222 atomFix = ms.at[bondAtomIndex1]; 1223 atomMove = ms.at[bondAtomIndex2]; 1224 } 1225 V3 v1 = V3.new3(atomMove.sX - atomFix.sX, atomMove.sY - atomFix.sY, 0); 1226 V3 v2 = V3.new3(deltaX, deltaY, 0); 1227 v1.cross(v1, v2); 1228 float degrees = (v1.z > 0 ? 1 : -1) * v2.length(); 1229 1230 BS bs = BSUtil.copy(bsBranch); 1231 bs.andNot(vwr.slm.getMotionFixedAtoms()); 1232 vwr.rotateAboutPointsInternal(null, atomFix, atomMove, 0, degrees, false, bs, 1233 null, null, null, null); 1234 } 1235 1236 ////////////// more callback methods ////////////// 1237 1238 @Override menuFocusCallback(String name, String actionCommand, boolean gained)1239 public void menuFocusCallback(String name, String actionCommand, 1240 boolean gained) { 1241 if (gained && !processSymop(name, true)) { 1242 setActiveMenu(name); 1243 } 1244 exitBondRotation(); 1245 } 1246 exitBondRotation()1247 protected void exitBondRotation() { 1248 System.out.println("MKP exitBondRotation"); 1249 isRotateBond = false; 1250 vwr.highlight(null); 1251 if (prevBondCheckBox != null) 1252 bondHoverLabel = prevBondCheckBox.getText(); 1253 vwr.setPickingMode(null, ActionManager.PICKING_ASSIGN_BOND); 1254 } 1255 1256 @Override menuClickCallback(SC source, String script)1257 public void menuClickCallback(SC source, String script) { 1258 doMenuClickCallbackMK(source, script); 1259 } 1260 doMenuClickCallbackMK(SC source, String script)1261 public void doMenuClickCallbackMK(SC source, String script) { 1262 //action performed 1263 if (processSymop(source.getName(), false)) 1264 return; 1265 if (script.equals("clearQPersist")) { 1266 for (SC item : htCheckbox.values()) { 1267 if (item.getActionCommand().indexOf(":??") < 0) 1268 continue; 1269 menuSetLabel(item, "??"); 1270 item.setActionCommand("_??P!:"); 1271 item.setSelected(false); 1272 } 1273 appRunScript("set picking assignAtom_C"); 1274 return; 1275 } 1276 // may come back to getScriptForCallback 1277 doMenuClickCallback(source, script); 1278 } 1279 1280 /** 1281 * Secondary processing of menu item click 1282 */ 1283 @Override getScriptForCallback(SC source, String id, String script)1284 protected String getScriptForCallback(SC source, String id, String script) { 1285 if (script.startsWith("mk")) { 1286 processXtalClick(id, script); 1287 script = null; // cancels any further processing 1288 } 1289 return script; 1290 } 1291 processXtalClick(String id, String action)1292 private void processXtalClick(String id, String action) { 1293 if (processSymop(id, false)) 1294 return; 1295 action = action.intern(); 1296 if (action.startsWith("mkmode_")) { 1297 if (!alertedNoEdit && action == "mkmode_edit") { 1298 alertedNoEdit = true; 1299 vwr.alert("ModelKit xtal edit has not been implemented"); 1300 return; 1301 } 1302 processModeClick(action); 1303 } else if (action.startsWith("mksel_")) { 1304 processSelClick(action); 1305 } else if (action.startsWith("mkselop_")) { 1306 processSelOpClick(action); 1307 } else if (action.startsWith("mksymmetry_")) { 1308 processSymClick(action); 1309 } else if (action.startsWith("mkunitcell_")) { 1310 processUCClick(action); 1311 } else { 1312 notImplemented("XTAL click " + action); 1313 } 1314 updateAllXtalMenuOptions(); 1315 } processSelOpClick(String action)1316 private void processSelOpClick(String action) { 1317 secondAtomIndex = -1; 1318 if (action == "mkselop_addoffset") { 1319 String pos = promptUser("Enter i j k for an offset for viewing the operator - leave blank to clear", lastOffset); 1320 if (pos == null) 1321 return; 1322 lastOffset = pos; 1323 if (pos.length() == 0 || pos == "none") { 1324 setProperty("offset", "none"); 1325 return; 1326 } 1327 P3 p = pointFromTriad(pos); 1328 if (p == null) { 1329 processSelOpClick(action); 1330 } else { 1331 setProperty("offset", p); 1332 } 1333 } else if (action == "mkselop_atom2") { 1334 notImplemented(action); 1335 } 1336 } 1337 processSymop(String id, boolean isFocus)1338 private boolean processSymop(String id, boolean isFocus) { 1339 int pt = id.indexOf(".mkop_"); 1340 if (pt >= 0) { 1341 Object op = symop; 1342 symop = Integer.valueOf(id.substring(pt + 6)); 1343 showSymop(symop); 1344 if (isFocus) // temporary only 1345 symop = op; 1346 return true; 1347 } 1348 return false; 1349 } 1350 showSymop(Object symop)1351 private void showSymop(Object symop) { 1352 secondAtomIndex = -1; 1353 this.symop = symop; 1354 showXtalSymmetry(); 1355 } 1356 processModeClick(String action)1357 private void processModeClick(String action) { 1358 processMKPropertyItem(action, false); 1359 } 1360 processSelClick(String action)1361 private void processSelClick(String action) { 1362 if (action == "mksel_atom") { 1363 centerPoint = null; 1364 centerAtomIndex = -1; 1365 secondAtomIndex = -1; 1366 // indicate next click is an atom 1367 } else if (action == "mksel_position") { 1368 String pos = promptUser("Enter three fractional coordinates", lastCenter); 1369 if (pos == null) 1370 return; 1371 lastCenter = pos; 1372 P3 p = pointFromTriad(pos); 1373 if (p == null) { 1374 processSelClick(action); 1375 return; 1376 } 1377 centerAtomIndex = -Integer.MAX_VALUE; 1378 centerPoint = p; 1379 showXtalSymmetry(); 1380 } 1381 } 1382 processSymClick(String action)1383 private void processSymClick(String action) { 1384 if (action == "mksymmetry_none") { 1385 setSymEdit(STATE_SYM_NONE); 1386 } else { 1387 processMKPropertyItem(action, false); 1388 } 1389 } 1390 processUCClick(String action)1391 private void processUCClick(String action) { 1392 processMKPropertyItem(action, false); 1393 showXtalSymmetry(); 1394 } 1395 1396 /** 1397 * Called from ActionManager for a drag-drop 1398 * 1399 * @param pressed 1400 * @param dragged 1401 * @param countPlusIndices 1402 * @return true if handled here 1403 */ handleDragAtom(MouseState pressed, MouseState dragged, int[] countPlusIndices)1404 public boolean handleDragAtom(MouseState pressed, MouseState dragged, 1405 int[] countPlusIndices) { 1406 switch (getMKState()) { 1407 case STATE_MOLECULAR: 1408 return false; 1409 case STATE_XTALEDIT: 1410 if (countPlusIndices[0] > 2) 1411 return true; 1412 notImplemented("drag atom for XTAL edit"); 1413 break; 1414 case STATE_XTALVIEW: 1415 if (getSymViewState() == STATE_SYM_NONE) 1416 setSymViewState(STATE_SYM_SHOW); 1417 switch (countPlusIndices[0]) { 1418 case 1: 1419 centerAtomIndex = countPlusIndices[1]; 1420 secondAtomIndex = -1; 1421 break; 1422 case 2: 1423 centerAtomIndex = countPlusIndices[1]; 1424 secondAtomIndex = countPlusIndices[2]; 1425 break; 1426 } 1427 showXtalSymmetry(); 1428 return true; 1429 } 1430 return true; 1431 } 1432 pointFromTriad(String pos)1433 private static P3 pointFromTriad(String pos) { 1434 float[] a = PT.parseFloatArray(PT.replaceAllCharacters(pos, "{,}", " ")); 1435 return (a.length == 3 && !Float.isNaN(a[2]) ? P3.new3(a[0], a[1], a[2]) : null); 1436 } 1437 notImplemented(String action)1438 private static void notImplemented(String action) { 1439 System.err.println("ModelKitPopup.notImplemented(" + action + ")"); 1440 } 1441 promptUser(String msg, String def)1442 private String promptUser(String msg, String def) { 1443 return vwr.prompt(msg, def, null, false); 1444 } 1445 runScriptBuffered(String script)1446 private String runScriptBuffered(String script) { 1447 SB sb = new SB(); 1448 try { 1449 // System.out.println("MKP\n" + script); 1450 ((ScriptEval) vwr.eval).runBufferedSafely(script, sb); 1451 } catch (Exception e) { 1452 e.printStackTrace(); 1453 } 1454 return sb.toString(); 1455 } 1456 1457 /** 1458 * C 1459 * 1460 * @param pressed 1461 * @param dragged 1462 * @param mp 1463 * @param dragAtomIndex 1464 * @return true if we should do a refresh now 1465 */ handleAssignNew(MouseState pressed, MouseState dragged, MeasurementPending mp, int dragAtomIndex)1466 public boolean handleAssignNew(MouseState pressed, MouseState dragged, 1467 MeasurementPending mp, int dragAtomIndex) { 1468 1469 // H C + -, etc. 1470 // also check valence and add/remove H atoms as necessary? 1471 boolean inRange = pressed.inRange(ActionManager.XY_RANGE, dragged.x, 1472 dragged.y); 1473 1474 if (inRange) { 1475 dragged.x = pressed.x; 1476 dragged.y = pressed.y; 1477 } 1478 1479 if (handleDragAtom(pressed, dragged, mp.countPlusIndices)) 1480 return true; 1481 boolean isCharge = isPickAtomAssignCharge; 1482 String atomType = pickAtomAssignType; 1483 if (mp.count == 2) { 1484 vwr.undoMoveActionClear(-1, T.save, true); 1485 if (((Atom) mp.getAtom(1)).isBonded((Atom) mp.getAtom(2))) { 1486 appRunScript("modelkit assign bond " + mp.getMeasurementScript(" ", false) + "'p'"); 1487 } else { 1488 appRunScript("modelkit connect " + mp.getMeasurementScript(" ", false)); 1489 } 1490 } else { 1491 if (atomType.equals("Xx")) { 1492 atomType = lastElementType; 1493 } 1494 if (inRange) { 1495 String s = "modelkit assign atom ({" + dragAtomIndex + "}) \"" + atomType + "\" true"; 1496 if (isCharge) { 1497 s += ";{atomindex=" + dragAtomIndex + "}.label='%C'; "; 1498 vwr.undoMoveActionClear(dragAtomIndex, 1499 AtomCollection.TAINT_FORMALCHARGE, true); 1500 } else { 1501 vwr.undoMoveActionClear(-1, T.save, true); 1502 } 1503 appRunScript(s); 1504 } else if (!isCharge) { 1505 vwr.undoMoveActionClear(-1, T.save, true); 1506 Atom a = vwr.ms.at[dragAtomIndex]; 1507 if (a.getElementNumber() == 1) { 1508 assignAtomClick(dragAtomIndex, "X", null); 1509 } else { 1510 int x = dragged.x; 1511 int y = dragged.y; 1512 1513 if (vwr.antialiased) { 1514 x <<= 1; 1515 y <<= 1; 1516 } 1517 1518 P3 ptNew = P3.new3(x, y, a.sZ); 1519 vwr.tm.unTransformPoint(ptNew, ptNew); 1520 assignAtomClick(dragAtomIndex, atomType, ptNew); 1521 } 1522 } 1523 } 1524 return true; 1525 } 1526 cmdAssignAtom(int atomIndex, P3 pt, String type, String cmd, boolean isClick)1527 public void cmdAssignAtom(int atomIndex, P3 pt, String type, String cmd, boolean isClick) { 1528 if (isClick && type.equals("X")) 1529 vwr.setModelKitRotateBondIndex(-1); 1530 int ac = vwr.ms.ac; 1531 Atom atom = (atomIndex < 0 ? null : vwr.ms.at[atomIndex]); 1532 if (pt == null) { 1533 if (atomIndex < 0 || atom == null) 1534 return; 1535 int mi = atom.mi; 1536 vwr.sm.modifySend(atomIndex, mi, 1, cmd); 1537 // After this next command, vwr.modelSet will be a different instance 1538 assignAtom(atomIndex, type, autoBond, true, true); 1539 if (!PT.isOneOf(type, ";Mi;Pl;X;")) 1540 vwr.ms.setAtomNamesAndNumbers(0, -ac, null); 1541 vwr.sm.modifySend(atomIndex, mi, -1, "OK"); 1542 vwr.refresh(Viewer.REFRESH_SYNC_MASK, "assignAtom"); 1543 return; 1544 } 1545 BS bs = (atomIndex < 0 ? new BS() : BSUtil.newAndSetBit(atomIndex)); 1546 P3[] pts = new P3[] { pt }; 1547 Lst<Atom> vConnections = new Lst<Atom>(); 1548 int modelIndex = -1; 1549 if (atom != null) { 1550 vConnections.addLast(atom); 1551 modelIndex = atom.mi; 1552 vwr.sm.modifySend(atomIndex, modelIndex, 3, cmd); 1553 } 1554 try { 1555 int pickingMode = vwr.acm.getAtomPickingMode(); 1556 boolean wasHidden = hidden; 1557 boolean isMK = vwr.getBoolean(T.modelkitmode); 1558 if (!isMK) { 1559 allowPopup = false; 1560 vwr.setBooleanProperty("modelkitmode", true); 1561 hidden = true; 1562 } 1563 bs = vwr.addHydrogensInline(bs, vConnections, pts); 1564 if (!isMK) { 1565 vwr.setBooleanProperty("modelkitmode", false); 1566 hidden = wasHidden; 1567 allowPopup = true; 1568 vwr.acm.setPickingMode(pickingMode); 1569 menuHidePopup(popupMenu); 1570 } 1571 int atomIndex2 = bs.nextSetBit(0); 1572 assignAtom(atomIndex2, type, false, atomIndex >= 0, true); 1573 if (atomIndex >= 0) 1574 assignAtom(atomIndex, ".", false, true, isClick); 1575 atomIndex = atomIndex2; 1576 } catch (Exception ex) { 1577 // 1578 } 1579 vwr.ms.setAtomNamesAndNumbers(0, -ac, null); 1580 vwr.sm.modifySend(atomIndex, modelIndex, -3, "OK"); 1581 } 1582 cmdAssignBond(int bondIndex, char type, String cmd)1583 public void cmdAssignBond(int bondIndex, char type, String cmd) { 1584 int modelIndex = -1; 1585 try { 1586 if (type == '-') 1587 type = pickBondAssignType; 1588 modelIndex = vwr.ms.bo[bondIndex].atom1.mi; 1589 int ac = vwr.ms.ac; 1590 vwr.sm.modifySend(bondIndex, modelIndex, 2, 1591 cmd); 1592 BS bsAtoms = assignBond(bondIndex, type); 1593 vwr.ms.setAtomNamesAndNumbers(0, -ac, null); 1594 if (bsAtoms == null || type == '0') 1595 vwr.refresh(Viewer.REFRESH_SYNC_MASK, "setBondOrder"); 1596 vwr.sm.modifySend(bondIndex, modelIndex, -2, "" + type); 1597 } catch (Exception ex) { 1598 Logger.error("assignBond failed"); 1599 vwr.sm.modifySend(bondIndex, modelIndex, -2, "ERROR " + ex); 1600 } 1601 } 1602 cmdAssignConnect(int index, int index2, char type, String cmd)1603 public void cmdAssignConnect(int index, int index2, char type, String cmd) { 1604 float[][] connections = AU.newFloat2(1); 1605 connections[0] = new float[] { index, index2 }; 1606 int modelIndex = vwr.ms.at[index].mi; 1607 vwr.sm.modifySend(index, modelIndex, 2, cmd); 1608 vwr.ms.connect(connections); 1609 int ac = vwr.ms.ac; 1610 // note that vwr.ms changes during the assignAtom command 1611 assignAtom(index, ".", true, true, false); 1612 assignAtom(index2, ".", true, true, false); 1613 vwr.ms.setAtomNamesAndNumbers(0, -ac, null); 1614 vwr.sm.modifySend(index, modelIndex, -2, "OK"); 1615 if (type != '1') { 1616 BS bs = BSUtil.newAndSetBit(index); 1617 bs.set(index2); 1618 bs = vwr.getBondsForSelectedAtoms(bs); 1619 int bondIndex = bs.nextSetBit(0); 1620 cmdAssignBond(bondIndex, type, cmd); 1621 } 1622 vwr.refresh(Viewer.REFRESH_SYNC_MASK, "assignConnect"); 1623 } 1624 assignAtomClick(int atomIndex, String element, P3 ptNew)1625 public void assignAtomClick(int atomIndex, String element, P3 ptNew) { 1626 // if (vwr.ms.isAtomInLastModel(atomIndex)) { 1627 vwr.script("modelkit assign atom ({" + atomIndex + "}) \"" + element + "\" " 1628 + (ptNew == null ? "" : Escape.eP(ptNew)) + " true"); 1629 // } 1630 } 1631 1632 @Override appRunSpecialCheckBox(SC item, String basename, String script, boolean TF)1633 protected boolean appRunSpecialCheckBox(SC item, String basename, String script, 1634 boolean TF) { 1635 if (basename.indexOf("assignAtom_Xx") == 0) { 1636 pickAtomAssignType = lastElementType; 1637 } 1638 return super.appRunSpecialCheckBox(item, basename, script, TF); 1639 } 1640 getDefaultModel()1641 public String getDefaultModel() { 1642 return (addXtalHydrogens ? JC.MODELKIT_ZAP_STRING : "1\n\nC 0 0 0\n"); 1643 } 1644 } 1645