1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2009-07-31 09:22:19 -0500 (Fri, 31 Jul 2009) $ 4 * $Revision: 11291 $ 5 * 6 * Copyright (C) 2002-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.viewer; 25 26 import java.util.Map; 27 28 import org.jmol.api.EventManager; 29 import org.jmol.api.GenericPlatform; 30 import org.jmol.api.Interface; 31 import org.jmol.awtjs.Event; 32 import org.jmol.i18n.GT; 33 34 import javajs.util.AU; 35 import javajs.util.BS; 36 import org.jmol.modelset.AtomCollection; 37 import org.jmol.modelset.MeasurementPending; 38 import org.jmol.script.SV; 39 import org.jmol.script.ScriptEval; 40 import org.jmol.script.T; 41 import org.jmol.thread.HoverWatcherThread; 42 import org.jmol.util.BSUtil; 43 import org.jmol.util.C; 44 import org.jmol.util.Escape; 45 import org.jmol.util.Logger; 46 import org.jmol.util.Point3fi; 47 48 import javajs.util.P3; 49 import javajs.util.PT; 50 51 import org.jmol.util.Rectangle; 52 import org.jmol.viewer.binding.Binding; 53 import org.jmol.viewer.binding.JmolBinding; 54 55 public class ActionManager implements EventManager { 56 57 protected Viewer vwr; 58 protected boolean haveMultiTouchInput; 59 protected boolean isMultiTouch; 60 61 public Binding b; 62 63 private Binding jmolBinding; 64 private Binding pfaatBinding; 65 private Binding dragBinding; 66 private Binding rasmolBinding; 67 private Binding predragBinding; 68 private int LEFT_CLICKED; 69 private int LEFT_DRAGGED; 70 71 /** 72 * 73 * @param vwr 74 * @param commandOptions 75 */ setViewer(Viewer vwr, String commandOptions)76 public void setViewer(Viewer vwr, String commandOptions) { 77 this.vwr = vwr; 78 if (!Viewer.isJS) 79 createActions(); 80 setBinding(jmolBinding = new JmolBinding()); 81 LEFT_CLICKED = Binding.getMouseAction(1, Binding.LEFT, Event.CLICKED); 82 LEFT_DRAGGED = Binding.getMouseAction(1, Binding.LEFT, Event.DRAGGED); 83 dragGesture = new Gesture(20, vwr); 84 } 85 86 protected Thread hoverWatcherThread; 87 checkHover()88 public void checkHover() { 89 if (zoomTrigger) { 90 zoomTrigger = false; 91 if (vwr.currentCursor == GenericPlatform.CURSOR_ZOOM) 92 vwr.setCursor(GenericPlatform.CURSOR_DEFAULT); 93 vwr.setInMotion(false); 94 return; 95 } 96 if (!vwr.getInMotion(true) && !vwr.tm.spinOn && !vwr.tm.navOn 97 && !vwr.checkObjectHovered(current.x, current.y)) { 98 int atomIndex = vwr.findNearestAtomIndex(current.x, current.y); 99 if (atomIndex < 0) 100 return; 101 boolean isLabel = (apm == PICKING_LABEL && bnd( 102 Binding 103 .getMouseAction(clickedCount, moved.modifiers, Event.DRAGGED), 104 ACTION_dragLabel)); 105 vwr.hoverOn(atomIndex, isLabel); 106 } 107 } 108 109 /** 110 * 111 * Specific to ActionManagerMT -- for processing SparshUI gestures 112 * 113 * @param groupID 114 * @param eventType 115 * @param touchID 116 * @param iData 117 * @param pt 118 * @param time 119 */ processMultitouchEvent(int groupID, int eventType, int touchID, int iData, P3 pt, long time)120 public void processMultitouchEvent(int groupID, int eventType, int touchID, int iData, 121 P3 pt, long time) { 122 // see subclass 123 } 124 125 /** 126 * 127 * @param desc 128 * @param name 129 */ bind(String desc, String name)130 void bind(String desc, String name) { 131 int jmolAction = getActionFromName(name); 132 int mouseAction = Binding.getMouseActionStr(desc); 133 if (mouseAction == 0) 134 return; 135 if (jmolAction >= 0) { 136 b.bindAction(mouseAction, jmolAction); 137 } else { 138 b.bindName(mouseAction, name); 139 } 140 } 141 clearBindings()142 protected void clearBindings() { 143 setBinding(jmolBinding = new JmolBinding()); 144 pfaatBinding = null; 145 dragBinding = null; 146 rasmolBinding = null; 147 } 148 unbindAction(String desc, String name)149 void unbindAction(String desc, String name) { 150 if (desc == null && name == null) { 151 clearBindings(); 152 return; 153 } 154 int jmolAction = getActionFromName(name); 155 int mouseAction = Binding.getMouseActionStr(desc); 156 if (jmolAction >= 0) 157 b.unbindAction(mouseAction, jmolAction); 158 else if (mouseAction != 0) 159 b.unbindName(mouseAction, name); 160 if (name == null) 161 b.unbindUserAction(desc); 162 } 163 164 //// Gestures 165 166 private Gesture dragGesture; 167 168 /* 169 * a "Jmol action" is one of these: 170 * 171 * A Jmol action is "bound" to a mouse action by the 172 * simple act of concatenating string "jmol action" + \t + "mouse action" 173 * 174 * 175 */ 176 public final static int ACTION_assignNew = 0; 177 public final static int ACTION_center = 1; 178 public final static int ACTION_clickFrank = 2; 179 public final static int ACTION_connectAtoms = 3; 180 public final static int ACTION_deleteAtom = 4; 181 public final static int ACTION_deleteBond = 5; 182 public final static int ACTION_depth = 6; 183 public final static int ACTION_dragAtom = 7; 184 public final static int ACTION_dragDrawObject = 8; 185 public final static int ACTION_dragDrawPoint = 9; 186 public final static int ACTION_dragLabel = 10; 187 public final static int ACTION_dragMinimize = 11; 188 public final static int ACTION_dragMinimizeMolecule = 12; 189 public final static int ACTION_dragSelected = 13; 190 public final static int ACTION_dragZ = 14; 191 public final static int ACTION_multiTouchSimulation = 15; 192 public final static int ACTION_navTranslate = 16; 193 public final static int ACTION_pickAtom = 17; 194 public final static int ACTION_pickIsosurface = 18; 195 public final static int ACTION_pickLabel = 19; 196 public final static int ACTION_pickMeasure = 20; 197 public final static int ACTION_pickNavigate = 21; 198 public final static int ACTION_pickPoint = 22; 199 public final static int ACTION_popupMenu = 23; 200 public final static int ACTION_reset = 24; 201 public final static int ACTION_rotate = 25; 202 public final static int ACTION_rotateBranch = 26; 203 public final static int ACTION_rotateSelected = 27; 204 public final static int ACTION_rotateZ = 28; 205 public final static int ACTION_rotateZorZoom = 29; 206 public final static int ACTION_select = 30; 207 public final static int ACTION_selectAndDrag = 31; 208 public final static int ACTION_selectAndNot = 32; 209 public final static int ACTION_selectNone = 33; 210 public final static int ACTION_selectOr = 34; 211 public final static int ACTION_selectToggle = 35; 212 public final static int ACTION_selectToggleExtended = 36; 213 public final static int ACTION_setMeasure = 37; 214 public final static int ACTION_slab = 38; 215 public final static int ACTION_slabAndDepth = 39; 216 public final static int ACTION_slideZoom = 40; 217 public final static int ACTION_spinDrawObjectCCW = 41; 218 public final static int ACTION_spinDrawObjectCW = 42; 219 public final static int ACTION_stopMotion = 43; 220 public final static int ACTION_swipe = 44; 221 public final static int ACTION_translate = 45; 222 public final static int ACTION_wheelZoom = 46; 223 public final static int ACTION_count = 47; 224 225 final static String[] actionInfo = new String[ACTION_count]; 226 final static String[] actionNames = new String[ACTION_count]; 227 newAction(int i, String name, String info)228 static void newAction(int i, String name, String info) { 229 actionInfo[i] = info; 230 actionNames[i] = name; 231 } 232 createActions()233 void createActions() { 234 if (actionInfo[ACTION_assignNew] != null) 235 return; 236 // OK for J2S because actionInfo and actionNames are both private 237 newAction(ACTION_assignNew, "_assignNew", GT.o(GT.$( 238 "assign/new atom or bond (requires {0})"), 239 "set picking assignAtom_??/assignBond_?")); 240 newAction(ACTION_center, "_center", GT.$("center")); 241 newAction(ACTION_clickFrank, "_clickFrank", GT 242 .$("pop up recent context menu (click on Jmol frank)")); 243 newAction(ACTION_deleteAtom, "_deleteAtom", GT.o(GT.$( 244 "delete atom (requires {0})"), "set picking DELETE ATOM")); 245 newAction(ACTION_deleteBond, "_deleteBond", GT.o(GT.$( 246 "delete bond (requires {0})"), "set picking DELETE BOND")); 247 newAction(ACTION_depth, "_depth", GT.o(GT.$( 248 "adjust depth (back plane; requires {0})"), "SLAB ON")); 249 newAction(ACTION_dragAtom, "_dragAtom", GT.o(GT.$("move atom (requires {0})"), 250 "set picking DRAGATOM")); 251 newAction(ACTION_dragDrawObject, "_dragDrawObject", GT.o(GT.$( 252 "move whole DRAW object (requires {0})"), "set picking DRAW")); 253 newAction(ACTION_dragDrawPoint, "_dragDrawPoint", GT.o(GT.$( 254 "move specific DRAW point (requires {0})"), "set picking DRAW")); 255 newAction(ACTION_dragLabel, "_dragLabel", GT.o(GT.$("move label (requires {0})"), 256 "set picking LABEL")); 257 newAction(ACTION_dragMinimize, "_dragMinimize", GT.o(GT.$( 258 "move atom and minimize molecule (requires {0})"), 259 "set picking DRAGMINIMIZE")); 260 newAction(ACTION_dragMinimizeMolecule, "_dragMinimizeMolecule", GT.o(GT.$( 261 "move and minimize molecule (requires {0})"), 262 "set picking DRAGMINIMIZEMOLECULE")); 263 newAction(ACTION_dragSelected, "_dragSelected", GT.o(GT.$( 264 "move selected atoms (requires {0})"), "set DRAGSELECTED")); 265 newAction(ACTION_dragZ, "_dragZ", GT.o(GT.$( 266 "drag atoms in Z direction (requires {0})"), "set DRAGSELECTED")); 267 newAction(ACTION_multiTouchSimulation, "_multiTouchSimulation", GT 268 .$("simulate multi-touch using the mouse)")); 269 newAction(ACTION_navTranslate, "_navTranslate", GT.o(GT.$( 270 "translate navigation point (requires {0} and {1})"), new String[] { 271 "set NAVIGATIONMODE", "set picking NAVIGATE" })); 272 newAction(ACTION_pickAtom, "_pickAtom", GT.$("pick an atom")); 273 newAction(ACTION_connectAtoms, "_pickConnect", GT.o(GT.$( 274 "connect atoms (requires {0})"), "set picking CONNECT")); 275 newAction(ACTION_pickIsosurface, "_pickIsosurface", GT.o(GT.$( 276 "pick an ISOSURFACE point (requires {0}"), "set DRAWPICKING")); 277 newAction(ACTION_pickLabel, "_pickLabel", GT.o(GT.$( 278 "pick a label to toggle it hidden/displayed (requires {0})"), 279 "set picking LABEL")); 280 newAction( 281 ACTION_pickMeasure, 282 "_pickMeasure", 283 GT.o(GT 284 .$( 285 "pick an atom to include it in a measurement (after starting a measurement or after {0})"), 286 "set picking DISTANCE/ANGLE/TORSION")); 287 newAction(ACTION_pickNavigate, "_pickNavigate", GT.o(GT.$( 288 "pick a point or atom to navigate to (requires {0})"), 289 "set NAVIGATIONMODE")); 290 newAction(ACTION_pickPoint, "_pickPoint", GT.o(GT 291 .$("pick a DRAW point (for measurements) (requires {0}"), 292 "set DRAWPICKING")); 293 newAction(ACTION_popupMenu, "_popupMenu", GT 294 .$("pop up the full context menu")); 295 newAction(ACTION_reset, "_reset", GT 296 .$("reset (when clicked off the model)")); 297 newAction(ACTION_rotate, "_rotate", GT.$("rotate")); 298 newAction(ACTION_rotateBranch, "_rotateBranch", GT.o(GT.$( 299 "rotate branch around bond (requires {0})"), "set picking ROTATEBOND")); 300 newAction(ACTION_rotateSelected, "_rotateSelected", GT.o(GT.$( 301 "rotate selected atoms (requires {0})"), "set DRAGSELECTED")); 302 newAction(ACTION_rotateZ, "_rotateZ", GT.$("rotate Z")); 303 newAction( 304 ACTION_rotateZorZoom, 305 "_rotateZorZoom", 306 GT 307 .$("rotate Z (horizontal motion of mouse) or zoom (vertical motion of mouse)")); 308 newAction(ACTION_select, "_select", GT.o(GT.$("select an atom (requires {0})"), 309 "set pickingStyle EXTENDEDSELECT")); 310 newAction(ACTION_selectAndDrag, "_selectAndDrag", GT.o(GT.$( 311 "select and drag atoms (requires {0})"), "set DRAGSELECTED")); 312 newAction(ACTION_selectAndNot, "_selectAndNot", GT.o(GT.$( 313 "unselect this group of atoms (requires {0})"), 314 "set pickingStyle DRAG/EXTENDEDSELECT")); 315 newAction(ACTION_selectNone, "_selectNone", GT.o(GT.$( 316 "select NONE (requires {0})"), "set pickingStyle EXTENDEDSELECT")); 317 newAction(ACTION_selectOr, "_selectOr", GT.o(GT.$( 318 "add this group of atoms to the set of selected atoms (requires {0})"), 319 "set pickingStyle DRAG/EXTENDEDSELECT")); 320 newAction(ACTION_selectToggle, "_selectToggle", GT.o(GT.$( 321 "toggle selection (requires {0})"), 322 "set pickingStyle DRAG/EXTENDEDSELECT/RASMOL")); 323 newAction( 324 ACTION_selectToggleExtended, 325 "_selectToggleOr", 326 GT.o(GT 327 .$( 328 "if all are selected, unselect all, otherwise add this group of atoms to the set of selected atoms (requires {0})"), 329 "set pickingStyle DRAG")); 330 newAction(ACTION_setMeasure, "_setMeasure", GT 331 .$("pick an atom to initiate or conclude a measurement")); 332 newAction(ACTION_slab, "_slab", GT.o(GT.$( 333 "adjust slab (front plane; requires {0})"), "SLAB ON")); 334 newAction(ACTION_slabAndDepth, "_slabAndDepth", GT.o(GT.$( 335 "move slab/depth window (both planes; requires {0})"), "SLAB ON")); 336 newAction(ACTION_slideZoom, "_slideZoom", GT 337 .$("zoom (along right edge of window)")); 338 newAction( 339 ACTION_spinDrawObjectCCW, 340 "_spinDrawObjectCCW", 341 GT.o(GT 342 .$( 343 "click on two points to spin around axis counterclockwise (requires {0})"), 344 "set picking SPIN")); 345 newAction(ACTION_spinDrawObjectCW, "_spinDrawObjectCW", GT.o(GT.$( 346 "click on two points to spin around axis clockwise (requires {0})"), 347 "set picking SPIN")); 348 newAction(ACTION_stopMotion, "_stopMotion", GT.o(GT.$( 349 "stop motion (requires {0})"), "set waitForMoveTo FALSE")); 350 newAction( 351 ACTION_swipe, 352 "_swipe", 353 GT 354 .$("spin model (swipe and release button and stop motion simultaneously)")); 355 newAction(ACTION_translate, "_translate", GT.$("translate")); 356 newAction(ACTION_wheelZoom, "_wheelZoom", GT.$("zoom")); 357 } 358 getActionName(int i)359 public static String getActionName(int i) { 360 return (i < actionNames.length ? actionNames[i] : null); 361 } 362 getActionFromName(String name)363 public static int getActionFromName(String name) { 364 for (int i = 0; i < actionNames.length; i++) 365 if (actionNames[i].equalsIgnoreCase(name)) 366 return i; 367 return -1; 368 } 369 getBindingInfo(String qualifiers)370 public String getBindingInfo(String qualifiers) { 371 return b.getBindingInfo(actionInfo, actionNames, qualifiers); 372 } 373 setBinding(Binding newBinding)374 protected void setBinding(Binding newBinding) { 375 // overridden in ActionManagerMT 376 b = newBinding; 377 } 378 bnd(int mouseAction, int... jmolActions)379 boolean bnd(int mouseAction, int... jmolActions) { 380 for (int i = jmolActions.length; --i >= 0;) 381 if (b.isBound(mouseAction, jmolActions[i])) 382 return true; 383 return false; 384 } 385 isDrawOrLabelAction(int a)386 private boolean isDrawOrLabelAction(int a) { 387 return (drawMode && bnd(a, ACTION_dragDrawObject, ACTION_dragDrawPoint) 388 || labelMode && bnd(a, ACTION_dragLabel)); 389 } 390 391 /** 392 * picking modes set picking.... 393 */ 394 395 private int apm = PICKING_IDENTIFY; 396 private int bondPickingMode; 397 getBondPickingMode()398 public int getBondPickingMode() { 399 return bondPickingMode; 400 } 401 402 public final static int PICKING_MK_RESET = -1; 403 public final static int PICKING_OFF = 0; 404 public final static int PICKING_IDENTIFY = 1; 405 public final static int PICKING_LABEL = 2; 406 public final static int PICKING_CENTER = 3; 407 public final static int PICKING_DRAW = 4; 408 public final static int PICKING_SPIN = 5; 409 public final static int PICKING_SYMMETRY = 6; 410 public final static int PICKING_DELETE_ATOM = 7; 411 public final static int PICKING_DELETE_BOND = 8; 412 public final static int PICKING_SELECT_ATOM = 9; 413 public final static int PICKING_SELECT_GROUP = 10; 414 public final static int PICKING_SELECT_CHAIN = 11; 415 public final static int PICKING_SELECT_MOLECULE = 12; 416 public final static int PICKING_SELECT_POLYMER = 13; 417 public final static int PICKING_SELECT_STRUCTURE = 14; 418 public final static int PICKING_SELECT_SITE = 15; 419 public final static int PICKING_SELECT_MODEL = 16; 420 public final static int PICKING_SELECT_ELEMENT = 17; 421 public final static int PICKING_MEASURE = 18; 422 public final static int PICKING_MEASURE_DISTANCE = 19; 423 public final static int PICKING_MEASURE_ANGLE = 20; 424 public final static int PICKING_MEASURE_TORSION = 21; 425 public final static int PICKING_MEASURE_SEQUENCE = 22; 426 public final static int PICKING_NAVIGATE = 23; 427 public final static int PICKING_CONNECT = 24; 428 public final static int PICKING_STRUTS = 25; 429 public final static int PICKING_DRAG_SELECTED = 26; 430 public final static int PICKING_DRAG_MOLECULE = 27; 431 public final static int PICKING_DRAG_ATOM = 28; 432 public final static int PICKING_DRAG_MINIMIZE = 29; 433 public final static int PICKING_DRAG_MINIMIZE_MOLECULE = 30; // for docking 434 public final static int PICKING_INVERT_STEREO = 31; 435 public final static int PICKING_ASSIGN_ATOM = 32; 436 public final static int PICKING_ASSIGN_BOND = 33; 437 public final static int PICKING_ROTATE_BOND = 34; 438 public final static int PICKING_IDENTIFY_BOND = 35; 439 public final static int PICKING_DRAG_LIGAND = 36; 440 public final static int PICKING_DRAG_MODEL = 37; 441 442 /** 443 * picking styles 444 */ 445 public final static int PICKINGSTYLE_SELECT_JMOL = 0; 446 public final static int PICKINGSTYLE_SELECT_CHIME = 0; 447 public final static int PICKINGSTYLE_SELECT_RASMOL = 1; 448 public final static int PICKINGSTYLE_SELECT_PFAAT = 2; 449 public final static int PICKINGSTYLE_SELECT_DRAG = 3; 450 public final static int PICKINGSTYLE_MEASURE_ON = 4; 451 public final static int PICKINGSTYLE_MEASURE_OFF = 5; 452 453 private final static String[] pickingModeNames; 454 static { 455 pickingModeNames = "off identify label center draw spin symmetry deleteatom deletebond atom group chain molecule polymer structure site model element measure distance angle torsion sequence navigate connect struts dragselected dragmolecule dragatom dragminimize dragminimizemolecule invertstereo assignatom assignbond rotatebond identifybond dragligand dragmodel".split(" "); 456 } 457 getPickingModeName(int pickingMode)458 public final static String getPickingModeName(int pickingMode) { 459 return (pickingMode < 0 || pickingMode >= pickingModeNames.length ? "off" 460 : pickingModeNames[pickingMode]); 461 } 462 getPickingMode(String str)463 public final static int getPickingMode(String str) { 464 for (int i = pickingModeNames.length; --i >= 0;) 465 if (str.equalsIgnoreCase(pickingModeNames[i])) 466 return i; 467 return -1; 468 } 469 470 private final static String[] pickingStyleNames; 471 472 static { 473 pickingStyleNames = "toggle selectOrToggle extendedSelect drag measure measureoff".split(" "); 474 } 475 getPickingStyleName(int pickingStyle)476 public final static String getPickingStyleName(int pickingStyle) { 477 return (pickingStyle < 0 || pickingStyle >= pickingStyleNames.length ? "toggle" 478 : pickingStyleNames[pickingStyle]); 479 } 480 getPickingStyleIndex(String str)481 public final static int getPickingStyleIndex(String str) { 482 for (int i = pickingStyleNames.length; --i >= 0;) 483 if (str.equalsIgnoreCase(pickingStyleNames[i])) 484 return i; 485 return -1; 486 } 487 getAtomPickingMode()488 public int getAtomPickingMode() { 489 return apm; 490 } 491 setPickingMode(int pickingMode)492 public void setPickingMode(int pickingMode) { 493 boolean isNew = false; 494 switch (pickingMode) { 495 case PICKING_MK_RESET: 496 // from set modelkit OFF only 497 isNew = true; 498 bondPickingMode = PICKING_IDENTIFY_BOND; 499 pickingMode = PICKING_IDENTIFY; 500 vwr.setStringProperty("pickingStyle", "toggle"); 501 vwr.setBooleanProperty("bondPicking", false); 502 break; 503 case PICKING_IDENTIFY_BOND: 504 case PICKING_ROTATE_BOND: 505 case PICKING_ASSIGN_BOND: 506 case PICKING_DELETE_BOND: 507 vwr.setBooleanProperty("bondPicking", true); 508 bondPickingMode = pickingMode; 509 // return; 510 // case PICKING_DELETE_BOND: 511 // bondPickingMode = pickingMode; 512 // if (vwr.getBondsPickable()) 513 // return; 514 resetMeasurement(); 515 return; 516 // isNew = true; 517 // break; 518 // if we have bondPicking mode, then we don't set atomPickingMode to this 519 } 520 isNew |= (apm != pickingMode); 521 apm = pickingMode; 522 if (isNew) 523 resetMeasurement(); 524 } 525 526 private int pickingStyle; 527 private int pickingStyleSelect = PICKINGSTYLE_SELECT_JMOL; 528 private int pickingStyleMeasure = PICKINGSTYLE_MEASURE_OFF; 529 private int rootPickingStyle = PICKINGSTYLE_SELECT_JMOL; 530 531 getPickingState()532 public String getPickingState() { 533 // the pickingMode is not reported in the state. But when we do an UNDO, 534 // we want to restore this. 535 String script = ";set modelkitMode " + vwr.getBoolean(T.modelkitmode) 536 + ";set picking " + getPickingModeName(apm); 537 if (apm == PICKING_ASSIGN_ATOM) 538 script += "_" + vwr.getModelkitProperty("atomType"); 539 script += ";"; 540 if (bondPickingMode != PICKING_OFF) 541 script += "set picking " + getPickingModeName(bondPickingMode); 542 if (bondPickingMode == PICKING_ASSIGN_BOND) 543 script += "_" + vwr.getModelkitProperty("bondType"); 544 script += ";"; 545 return script; 546 } 547 getPickingStyle()548 int getPickingStyle() { 549 return pickingStyle; 550 } 551 setPickingStyle(int pickingStyle)552 void setPickingStyle(int pickingStyle) { 553 this.pickingStyle = pickingStyle; 554 if (pickingStyle >= PICKINGSTYLE_MEASURE_ON) { 555 pickingStyleMeasure = pickingStyle; 556 resetMeasurement(); 557 } else { 558 if (pickingStyle < PICKINGSTYLE_SELECT_DRAG) 559 rootPickingStyle = pickingStyle; 560 pickingStyleSelect = pickingStyle; 561 } 562 rubberbandSelectionMode = false; 563 switch (pickingStyleSelect) { 564 case PICKINGSTYLE_SELECT_PFAAT: 565 if (!b.name.equals("extendedSelect")) 566 setBinding(pfaatBinding == null ? pfaatBinding = Binding 567 .newBinding(vwr, "Pfaat") : pfaatBinding); 568 break; 569 case PICKINGSTYLE_SELECT_DRAG: 570 if (!b.name.equals("drag")) 571 setBinding(dragBinding == null ? dragBinding = Binding 572 .newBinding(vwr, "Drag") : dragBinding); 573 rubberbandSelectionMode = true; 574 break; 575 case PICKINGSTYLE_SELECT_RASMOL: 576 if (!b.name.equals("selectOrToggle")) 577 setBinding(rasmolBinding == null ? rasmolBinding = Binding 578 .newBinding(vwr, "Rasmol") : rasmolBinding); 579 break; 580 default: 581 if (b != jmolBinding) 582 setBinding(jmolBinding); 583 } 584 if (!b.name.equals("drag")) 585 predragBinding = b; 586 } 587 588 589 590 private final static long MAX_DOUBLE_CLICK_MILLIS = 700; 591 protected final static long MININUM_GESTURE_DELAY_MILLISECONDS = 10; 592 private final static int SLIDE_ZOOM_X_PERCENT = 98; 593 public final static float DEFAULT_MOUSE_DRAG_FACTOR = 1f; 594 public final static float DEFAULT_MOUSE_WHEEL_FACTOR = 1.15f; 595 public final static float DEFAULT_GESTURE_SWIPE_FACTOR = 1f; 596 597 598 public final static int XY_RANGE = 10; // BH 2019.04.21 was 0 599 600 private float gestureSwipeFactor = DEFAULT_GESTURE_SWIPE_FACTOR; 601 protected float mouseDragFactor = DEFAULT_MOUSE_DRAG_FACTOR; 602 protected float mouseWheelFactor = DEFAULT_MOUSE_WHEEL_FACTOR; 603 setGestureSwipeFactor(float factor)604 void setGestureSwipeFactor(float factor) { 605 gestureSwipeFactor = factor; 606 } 607 setMouseDragFactor(float factor)608 void setMouseDragFactor(float factor) { 609 mouseDragFactor = factor; 610 } 611 setMouseWheelFactor(float factor)612 void setMouseWheelFactor(float factor) { 613 mouseWheelFactor = factor; 614 } 615 616 protected final MouseState current = new MouseState("current"); 617 protected final MouseState moved = new MouseState("moved"); 618 private final MouseState clicked = new MouseState("clicked"); 619 private final MouseState pressed = new MouseState("pressed"); 620 private final MouseState dragged = new MouseState("dragged"); 621 isDraggedIsShiftDown()622 boolean isDraggedIsShiftDown() { 623 return (dragged.modifiers & Binding.SHIFT) != 0; 624 } setCurrent(long time, int x, int y, int mods)625 protected void setCurrent(long time, int x, int y, int mods) { 626 vwr.hoverOff(); 627 current.set(time, x, y, mods); 628 } 629 getCurrentX()630 int getCurrentX() { 631 return current.x; 632 } 633 getCurrentY()634 int getCurrentY() { 635 return current.y; 636 } 637 638 protected int pressedCount; 639 protected int clickedCount; 640 641 private boolean drawMode; 642 private boolean labelMode; 643 private boolean dragSelectedMode; 644 private boolean measuresEnabled = true; 645 private boolean haveSelection; 646 setMouseMode()647 public void setMouseMode() { 648 drawMode = labelMode = false; 649 dragSelectedMode = vwr.getDragSelected(); 650 measuresEnabled = !dragSelectedMode; 651 if (!dragSelectedMode) 652 switch (apm) { 653 default: 654 return; 655 case PICKING_ASSIGN_ATOM: 656 measuresEnabled = !vwr.getModelkit(false).isPickAtomAssignCharge(); 657 return; 658 case PICKING_DRAW: 659 drawMode = true; 660 // drawMode and dragSelectedMode are incompatible 661 measuresEnabled = false; 662 break; 663 //other cases here? 664 case PICKING_LABEL: 665 labelMode = true; 666 measuresEnabled = false; 667 break; 668 case PICKING_SELECT_ATOM: 669 measuresEnabled = false; 670 break; 671 case PICKING_MEASURE_DISTANCE: 672 case PICKING_MEASURE_SEQUENCE: 673 case PICKING_MEASURE_ANGLE: 674 case PICKING_MEASURE_TORSION: 675 measuresEnabled = false; 676 return; 677 //break; 678 } 679 exitMeasurementMode(null); 680 } 681 clearMouseInfo()682 protected void clearMouseInfo() { 683 // when a second touch is made, this clears all record of first touch 684 pressedCount = clickedCount = 0; 685 dragGesture.setAction(0, 0); 686 exitMeasurementMode(null); 687 } 688 689 private boolean hoverActive = false; 690 691 private MeasurementPending mp; 692 693 private int dragAtomIndex = -1; 694 695 /** 696 * set to true in checkPressedAction if the screen coordinates of the press 697 * are relatively close to the coordinates of the hover that highlighted the bond, 698 * indicating that we can alias a left-click to a shift-left-click 699 * 700 */ 701 private boolean mkBondPressed; 702 setDragAtomIndex(int iatom)703 public void setDragAtomIndex(int iatom) { 704 // from label 705 dragAtomIndex = iatom; 706 setAtomsPicked(BSUtil.newAndSetBit(iatom), "Label picked for atomIndex = " + iatom); 707 } 708 709 710 private boolean rubberbandSelectionMode = false; 711 private final Rectangle rectRubber = new Rectangle(); 712 713 private boolean isAltKeyReleased = true; 714 private boolean keyProcessing; 715 716 protected boolean isMultiTouchClient; 717 protected boolean isMultiTouchServer; 718 isMTClient()719 public boolean isMTClient() { 720 return isMultiTouchClient; 721 } 722 isMTServer()723 public boolean isMTServer() { 724 return isMultiTouchServer; 725 } 726 dispose()727 public void dispose() { 728 clear(); 729 } 730 clear()731 public void clear() { 732 startHoverWatcher(false); 733 if (predragBinding != null) 734 b = predragBinding; 735 vwr.setPickingMode(null, PICKING_IDENTIFY); 736 vwr.setPickingStyle(null, rootPickingStyle); 737 isAltKeyReleased = true; 738 } 739 startHoverWatcher(boolean isStart)740 synchronized public void startHoverWatcher(boolean isStart) { 741 if (vwr.isPreviewOnly) 742 return; 743 try { 744 if (isStart) { 745 if (hoverWatcherThread != null) 746 return; 747 current.time = -1; 748 hoverWatcherThread = new HoverWatcherThread(this, current, moved, 749 vwr); 750 } else { 751 if (hoverWatcherThread == null) 752 return; 753 current.time = -1; 754 hoverWatcherThread.interrupt(); 755 hoverWatcherThread = null; 756 } 757 } catch (Exception e) { 758 // is possible -- seen once hoverWatcherThread.start() had null pointer. 759 } 760 } 761 762 /** 763 * only NONE (-1) is implemented; it just stops the hoverWatcher thread so 764 * that the vwr references are all removed 765 * 766 * @param modeMouse 767 */ setModeMouse(int modeMouse)768 public void setModeMouse(int modeMouse) { 769 if (modeMouse == JC.MOUSE_NONE) { 770 startHoverWatcher(false); 771 } 772 } 773 774 /** 775 * called by MouseManager.keyPressed 776 * 777 * @param key 778 * @param modifiers 779 * @return true if handled 780 */ 781 @Override keyPressed(int key, int modifiers)782 public boolean keyPressed(int key, int modifiers) { 783 if (keyProcessing) 784 return false; 785 keyProcessing = true; 786 switch (key) { 787 case Event.VK_ALT: 788 if (dragSelectedMode && isAltKeyReleased) 789 vwr.moveSelected(Integer.MIN_VALUE, 0, Integer.MIN_VALUE, 790 Integer.MIN_VALUE, Integer.MIN_VALUE, null, false, false, modifiers); 791 isAltKeyReleased = false; 792 moved.modifiers |= Binding.ALT; 793 break; 794 case Event.VK_SHIFT: 795 dragged.modifiers |= Binding.SHIFT; 796 moved.modifiers |= Binding.SHIFT; 797 break; 798 case Event.VK_CONTROL: 799 moved.modifiers |= Binding.CTRL; 800 break; 801 case Event.VK_ESCAPE: 802 vwr.hoverOff(); 803 exitMeasurementMode("escape"); 804 break; 805 default: 806 vwr.hoverOff(); 807 break; 808 } 809 int action = Binding.LEFT | Binding.SINGLE | Binding.DRAG | moved.modifiers; 810 if (!labelMode && !b.isUserAction(action)) { 811 checkMotionRotateZoom(action, current.x, 0, 0, false); 812 } 813 if (vwr.getBoolean(T.navigationmode)) { 814 // if (vwr.getBooleanProperty("showKeyStrokes", false)) 815 // vwr.evalStringQuiet("!set echo bottom left;echo " 816 // + (i == 0 ? "" : i + " " + m)); 817 switch (key) { 818 case Event.VK_UP: 819 case Event.VK_DOWN: 820 case Event.VK_LEFT: 821 case Event.VK_RIGHT: 822 case Event.VK_SPACE: 823 case Event.VK_PERIOD: 824 vwr.navigate(key, modifiers); 825 break; 826 } 827 } 828 keyProcessing = false; 829 return true; 830 } 831 832 @Override keyTyped(int keyChar, int modifiers)833 public boolean keyTyped(int keyChar, int modifiers) { 834 return false; 835 } 836 837 @Override keyReleased(int key)838 public void keyReleased(int key) { 839 switch (key) { 840 case Event.VK_ALT: 841 moved.modifiers &= ~Binding.ALT; 842 if (dragSelectedMode) 843 vwr.moveSelected(Integer.MAX_VALUE, 0, Integer.MIN_VALUE, 844 Integer.MIN_VALUE, Integer.MIN_VALUE, null, false, false, moved.modifiers); 845 isAltKeyReleased = true; 846 break; 847 case Event.VK_SHIFT: 848 moved.modifiers &= ~Binding.SHIFT; 849 break; 850 case Event.VK_CONTROL: 851 moved.modifiers &= ~Binding.CTRL; 852 } 853 if (moved.modifiers == 0) 854 vwr.setCursor(GenericPlatform.CURSOR_DEFAULT); 855 if (!vwr.getBoolean(T.navigationmode)) 856 return; 857 //if (vwr.getBooleanProperty("showKeyStrokes", false)) 858 //vwr.evalStringQuiet("!set echo bottom left;echo;"); 859 switch (key) { 860 case Event.VK_UP: 861 case Event.VK_DOWN: 862 case Event.VK_LEFT: 863 case Event.VK_RIGHT: 864 vwr.navigate(0, 0); 865 break; 866 } 867 } 868 869 @Override mouseEnterExit(long time, int x, int y, boolean isExit)870 public void mouseEnterExit(long time, int x, int y, boolean isExit) { 871 if (vwr.tm.stereoDoubleDTI) 872 x = x << 1; 873 setCurrent(time, x, y, 0); 874 if (isExit) 875 exitMeasurementMode("mouseExit"); //otherwise pending measurement can be left over. 876 } 877 878 private int pressAction; 879 private int dragAction; 880 private int clickAction; 881 setMouseActions(int count, int buttonMods, boolean isRelease)882 private void setMouseActions(int count, int buttonMods, boolean isRelease) { 883 pressAction = Binding.getMouseAction(count, buttonMods, 884 isRelease ? Event.RELEASED : Event.PRESSED); 885 dragAction = Binding.getMouseAction(count, buttonMods, Event.DRAGGED); 886 clickAction = Binding.getMouseAction(count, buttonMods, Event.CLICKED); 887 } 888 889 /** 890 * 891 * @param mode 892 * MOVED PRESSED DRAGGED RELEASED CLICKED WHEELED 893 * @param time 894 * @param x 895 * @param y 896 * @param count 897 * @param buttonMods 898 * LEFT RIGHT MIDDLE WHEEL SHIFT ALT CTRL 899 */ 900 @Override mouseAction(int mode, long time, int x, int y, int count, int buttonMods)901 public void mouseAction(int mode, long time, int x, int y, int count, 902 int buttonMods) { 903 if (!vwr.getMouseEnabled()) 904 return; 905 if (Logger.debuggingHigh && mode != Event.MOVED && vwr.getBoolean(T.testflag1)) 906 vwr.showString("mouse action: " + mode + " " + buttonMods + " " + Binding.getMouseActionName(Binding.getMouseAction(count, buttonMods, mode), false), false); 907 if (vwr.tm.stereoDoubleDTI) 908 x = x << 1; 909 switch (mode) { 910 case Event.MOVED: 911 setCurrent(time, x, y, buttonMods); 912 moved.setCurrent(current, 0); 913 if (mp != null || hoverActive) { 914 clickAction = Binding.getMouseAction(clickedCount, buttonMods, 915 Event.MOVED); 916 checkClickAction(x, y, time, 0); 917 return; 918 } 919 if (isZoomArea(x)) { 920 checkMotionRotateZoom(LEFT_DRAGGED, 0, 0, 0, false); 921 return; 922 } 923 if (vwr.currentCursor == GenericPlatform.CURSOR_ZOOM) 924 vwr.setCursor(GenericPlatform.CURSOR_DEFAULT); 925 return; 926 case Event.PRESSED: 927 setMouseMode(); 928 pressedCount = (pressed.check(20, x, y, buttonMods, time, 929 MAX_DOUBLE_CLICK_MILLIS) ? pressedCount + 1 : 1); 930 if (pressedCount == 1) { 931 vwr.checkInMotion(1); 932 setCurrent(time, x, y, buttonMods); 933 } 934 pressAction = Binding.getMouseAction(pressedCount, buttonMods, 935 Event.PRESSED); 936 vwr.setCursor(GenericPlatform.CURSOR_HAND); 937 pressed.setCurrent(current, 1); 938 dragged.setCurrent(current, 1); 939 vwr.setFocus(); 940 dragGesture.setAction(dragAction, time); 941 checkPressedAction(x, y, time); 942 return; 943 case Event.DRAGGED: 944 setMouseMode(); 945 setMouseActions(pressedCount, buttonMods, false); 946 int deltaX = x - dragged.x; 947 int deltaY = y - dragged.y; 948 setCurrent(time, x, y, buttonMods); 949 dragged.setCurrent(current, -1); 950 //if (false && apm != PICKING_ASSIGN_ATOM 951 // && apm != ACTION_pickMeasure 952 // && apm != PICKING_MEASURE_DISTANCE) 953 dragGesture.add(dragAction, x, y, time); 954 checkDragWheelAction(dragAction, x, y, deltaX, deltaY, time, 955 Event.DRAGGED); 956 return; 957 case Event.RELEASED: 958 setMouseActions(pressedCount, buttonMods, true); 959 setCurrent(time, x, y, buttonMods); 960 vwr.spinXYBy(0, 0, 0); 961 boolean dragRelease = !pressed.check(XY_RANGE, x, y, buttonMods, time, 962 Long.MAX_VALUE); 963 checkReleaseAction(x, y, time, dragRelease); 964 return; 965 case Event.WHEELED: 966 if (vwr.isApplet && !vwr.hasFocus()) 967 return; 968 setCurrent(time, current.x, current.y, buttonMods); 969 checkDragWheelAction(Binding.getMouseAction(0, buttonMods, 970 Event.WHEELED), current.x, current.y, 0, y, time, Event.WHEELED); 971 return; 972 case Event.CLICKED: 973 setMouseMode(); 974 // xyRange was 0 BH 2019.04.21 975 clickedCount = (count > 1 ? count : clicked.check(XY_RANGE, 0, 0, buttonMods, 976 time, MAX_DOUBLE_CLICK_MILLIS) ? clickedCount + 1 : 1); 977 if (clickedCount == 1) { 978 setCurrent(time, x, y, buttonMods); 979 } 980 setMouseActions(clickedCount, buttonMods, false); 981 clicked.setCurrent(current, clickedCount); 982 vwr.setFocus(); 983 if (apm != PICKING_SELECT_ATOM 984 && bnd(Binding.getMouseAction(1, buttonMods, Event.PRESSED), 985 ACTION_selectAndDrag)) 986 return; 987 clickAction = Binding.getMouseAction(clickedCount, buttonMods, 988 Event.CLICKED); 989 checkClickAction(x, y, time, clickedCount); 990 return; 991 } 992 } 993 checkPressedAction(int x, int y, long time)994 private void checkPressedAction(int x, int y, long time) { 995 int buttonMods = Binding.getButtonMods(pressAction); 996 boolean isDragSelectedAction = bnd( 997 Binding.getMouseAction(1, buttonMods, Event.PRESSED), 998 ACTION_selectAndDrag); 999 if (buttonMods != 0) { 1000 pressAction = vwr.notifyMouseClicked(x, y, pressAction, Event.PRESSED); 1001 if (pressAction == 0) 1002 return; 1003 buttonMods = Binding.getButtonMods(pressAction); 1004 } 1005 setMouseActions(pressedCount, buttonMods, false); 1006 if (Logger.debuggingHigh && vwr.getBoolean(T.testflag1)) 1007 Logger.debug(Binding.getMouseActionName(pressAction, false)); 1008 1009 if (isDrawOrLabelAction(dragAction) && vwr.checkObjectDragged(Integer.MIN_VALUE, 0, x, y, dragAction)) 1010 return; 1011 checkUserAction(pressAction, x, y, 0, 0, time, Event.PRESSED); 1012 boolean isBound = false; 1013 1014 switch (apm) { 1015 case PICKING_ASSIGN_ATOM: 1016 isBound = bnd(clickAction, ACTION_assignNew); 1017 break; 1018 case PICKING_DRAG_ATOM: 1019 isBound = bnd(dragAction, ACTION_dragAtom, ACTION_dragZ); 1020 break; 1021 case PICKING_DRAG_SELECTED: 1022 case PICKING_DRAG_LIGAND: 1023 case PICKING_DRAG_MODEL: 1024 case PICKING_DRAG_MOLECULE: 1025 isBound = bnd(dragAction, ACTION_dragAtom, ACTION_dragZ, ACTION_rotateSelected); 1026 break; 1027 case PICKING_DRAG_MINIMIZE: 1028 isBound = bnd(dragAction, ACTION_dragMinimize, ACTION_dragZ); 1029 break; 1030 case PICKING_DRAG_MINIMIZE_MOLECULE: 1031 isBound = bnd(dragAction, ACTION_dragMinimize, ACTION_dragZ, ACTION_rotateSelected); 1032 break; 1033 } 1034 if (isBound) { 1035 dragAtomIndex = vwr.findNearestAtomIndexMovable(x, y, true); 1036 if (dragAtomIndex >= 0 1037 && (apm == PICKING_ASSIGN_ATOM || apm == PICKING_INVERT_STEREO) 1038 // && vwr.ms.isAtomInLastModel(dragAtomIndex) 1039 ) { 1040 if (bondPickingMode == PICKING_ROTATE_BOND) { 1041 vwr.setModelkitProperty("bondAtomIndex", Integer.valueOf(dragAtomIndex)); 1042 } 1043 enterMeasurementMode(dragAtomIndex); 1044 mp.addPoint(dragAtomIndex, null, false); 1045 } 1046 int[] xy = (int[]) vwr.getModelkitProperty("screenXY"); 1047 mkBondPressed = (xy != null && pressed.inRange(10, xy[0], xy[1])); 1048 1049 return; 1050 } 1051 if (bnd(pressAction, ACTION_popupMenu)) { 1052 char type = 'j'; 1053 if (vwr.getBoolean(T.modelkitmode)) { 1054 Map<String, Object> t = vwr.checkObjectClicked(x, y, LEFT_CLICKED); 1055 type = ( 1056 // t != null && "bond".equals(t.get("type")) ? 'b' : vwr 1057 // .findNearestAtomIndex(x, y) >= 0 ? 'a' : 1058 'm'); 1059 } 1060 vwr.popupMenu(x, y, type); 1061 return; 1062 } 1063 if (dragSelectedMode) { 1064 haveSelection = (!isDragSelectedAction || vwr 1065 .findNearestAtomIndexMovable(x, y, true) >= 0); 1066 if (haveSelection && bnd(dragAction, ACTION_dragSelected, ACTION_dragZ)) 1067 vwr.moveSelected(Integer.MIN_VALUE, 0, Integer.MIN_VALUE, 1068 Integer.MIN_VALUE, Integer.MIN_VALUE, null, false, false, buttonMods); 1069 return; 1070 } 1071 // if (vwr.g.useArcBall) 1072 // vwr.rotateArcBall(x, y, 0); 1073 checkMotionRotateZoom(dragAction, x, 0, 0, true); 1074 } 1075 checkDragWheelAction(int dragWheelAction, int x, int y, int deltaX, int deltaY, long time, int mode)1076 private void checkDragWheelAction(int dragWheelAction, int x, int y, 1077 int deltaX, int deltaY, long time, int mode) { 1078 int buttonmods = Binding.getButtonMods(dragWheelAction); 1079 if (buttonmods != 0) { 1080 int newAction = vwr.notifyMouseClicked(x, y, 1081 Binding.getMouseAction(pressedCount, buttonmods, mode), mode); // why was this "-pressedCount"? passing to user? 1082 if (newAction == 0) 1083 return; 1084 if (newAction > 0) 1085 dragWheelAction = newAction; 1086 } 1087 1088 if (isRubberBandSelect(dragWheelAction)) { 1089 calcRectRubberBand(); 1090 vwr.refresh(Viewer.REFRESH_SYNC_MASK, "rubberBand selection"); 1091 return; 1092 } 1093 1094 if (checkUserAction(dragWheelAction, x, y, deltaX, deltaY, time, mode)) 1095 return; 1096 1097 if (vwr.g.modelKitMode && vwr.getModelkit(false).getRotateBondIndex() >= 0) { 1098 if (dragAtomIndex >= 0 || mkBondPressed 1099 || bnd(dragWheelAction, ACTION_rotateBranch)) { 1100 vwr.moveSelected(deltaX, deltaY, Integer.MIN_VALUE, x, y, null, false, 1101 false, dragAtomIndex >= 0 ? 0 : Event.VK_SHIFT); 1102 return; 1103 } 1104 } 1105 1106 BS bs = null; 1107 if (dragAtomIndex >= 0 && apm != PICKING_LABEL) { 1108 1109 switch (apm) { 1110 case PICKING_DRAG_SELECTED: 1111 dragSelected(dragWheelAction, deltaX, deltaY, true); 1112 return; 1113 case PICKING_DRAG_LIGAND: 1114 case PICKING_DRAG_MODEL: 1115 case PICKING_DRAG_MOLECULE: 1116 case PICKING_DRAG_MINIMIZE_MOLECULE: 1117 bs = vwr.ms.getAtoms((apm == PICKING_DRAG_MODEL ? T.model : T.molecule), BSUtil.newAndSetBit(dragAtomIndex)); 1118 if (apm == PICKING_DRAG_LIGAND) 1119 bs.and(vwr.getAtomBitSet("ligand")); 1120 //$FALL-THROUGH$ 1121 case PICKING_DRAG_ATOM: 1122 case PICKING_DRAG_MINIMIZE: 1123 if (dragGesture.getPointCount() == 1) 1124 vwr.undoMoveActionClear(dragAtomIndex, AtomCollection.TAINT_COORD, 1125 true); 1126 setMotion(GenericPlatform.CURSOR_MOVE, true); 1127 if (bnd(dragWheelAction, ACTION_rotateSelected)) { 1128 vwr.rotateSelected(getDegrees(deltaX, true), 1129 getDegrees(deltaY, false), bs); 1130 } else { 1131 switch (apm) { 1132 case PICKING_DRAG_LIGAND: 1133 case PICKING_DRAG_MODEL: 1134 case PICKING_DRAG_MOLECULE: 1135 case PICKING_DRAG_MINIMIZE_MOLECULE: 1136 vwr.select(bs, false, 0, true); 1137 break; 1138 } 1139 vwr.moveAtomWithHydrogens( 1140 dragAtomIndex, 1141 deltaX, 1142 deltaY, 1143 (bnd(dragWheelAction, ACTION_dragZ) ? -deltaY : Integer.MIN_VALUE), 1144 bs); 1145 } 1146 // NAH! if (atomPickingMode == PICKING_DRAG_MINIMIZE_MOLECULE && (dragGesture.getPointCount() % 5 == 0)) 1147 // minimize(false); 1148 return; 1149 } 1150 } 1151 1152 if (dragAtomIndex >= 0 && mode == Event.DRAGGED 1153 && bnd(clickAction, ACTION_assignNew) && apm == PICKING_ASSIGN_ATOM) { 1154 int nearestAtomIndex = vwr.findNearestAtomIndexMovable(x, y, false); 1155 if (nearestAtomIndex >= 0) { 1156 if (mp != null) { 1157 mp.setCount(1); 1158 } else if (measuresEnabled) { 1159 enterMeasurementMode(nearestAtomIndex); 1160 } 1161 addToMeasurement(nearestAtomIndex, null, true); 1162 mp.colix = C.MAGENTA; 1163 } else if (mp != null) { 1164 mp.setCount(1); 1165 mp.colix = C.GOLD; 1166 } 1167 if (mp == null) 1168 return; 1169 if (vwr.antialiased) { 1170 x <<= 1; 1171 y <<= 1; 1172 } 1173 1174 mp.traceX = x; 1175 mp.traceY = y; 1176 vwr.refresh(Viewer.REFRESH_SYNC_MASK, "assignNew"); 1177 return; 1178 } 1179 1180 if (!drawMode && !labelMode && bnd(dragWheelAction, ACTION_translate)) { 1181 vwr.translateXYBy(deltaX, deltaY); 1182 return; 1183 } 1184 if (dragSelectedMode && haveSelection 1185 && bnd(dragWheelAction, ACTION_dragSelected, ACTION_rotateSelected)) { 1186 // we will drag atoms and either rotate or translate them 1187 // possibly just the atoms or possibly their molecule (decided in Viewer) 1188 int iatom = vwr.bsA().nextSetBit(0); 1189 if (iatom < 0) 1190 return; 1191 if (dragGesture.getPointCount() == 1) 1192 vwr.undoMoveActionClear(iatom, AtomCollection.TAINT_COORD, true); 1193 else 1194 vwr.moveSelected(Integer.MAX_VALUE, 0, Integer.MIN_VALUE, 1195 Integer.MIN_VALUE, Integer.MIN_VALUE, null, false, false, buttonmods); 1196 dragSelected(dragWheelAction, deltaX, deltaY, false); 1197 return; 1198 } 1199 1200 if (isDrawOrLabelAction(dragWheelAction)) { 1201 setMotion(GenericPlatform.CURSOR_MOVE, true); 1202 if (vwr.checkObjectDragged(dragged.x, dragged.y, x, y, dragWheelAction)) { 1203 return; 1204 } 1205 } 1206 if (checkMotionRotateZoom(dragWheelAction, x, deltaX, deltaY, true)) { 1207 if (vwr.tm.slabEnabled && bnd(dragWheelAction,ACTION_slabAndDepth)) 1208 vwr.slabDepthByPixels(deltaY); 1209 else 1210 vwr.zoomBy(deltaY); 1211 return; 1212 } 1213 if (bnd(dragWheelAction, ACTION_rotate)) { 1214 // if (vwr.g.useArcBall) 1215 // vwr.rotateArcBall(x, y, mouseDragFactor); 1216 // else 1217 vwr.rotateXYBy(getDegrees(deltaX, true), getDegrees(deltaY, false)); 1218 return; 1219 } 1220 if (bnd(dragWheelAction, ACTION_rotateZorZoom)) { 1221 if (deltaX == 0 && Math.abs(deltaY) > 1) { 1222 // if (deltaY < 0 && deltaX > deltaY || deltaY > 0 && deltaX < deltaY) 1223 setMotion(GenericPlatform.CURSOR_ZOOM, true); 1224 vwr.zoomBy(deltaY + (deltaY > 0 ? -1 : 1)); 1225 } else if (deltaY == 0 && Math.abs(deltaX) > 1) { 1226 // if (deltaX < 0 && deltaY > deltaX || deltaX > 0 && deltaY < deltaX) 1227 setMotion(GenericPlatform.CURSOR_MOVE, true); 1228 vwr.rotateZBy(-deltaX + (deltaX > 0 ? 1 : -1), Integer.MAX_VALUE, 1229 Integer.MAX_VALUE); 1230 } 1231 return; 1232 } 1233 if (vwr.tm.slabEnabled) { 1234 if (bnd(dragWheelAction, ACTION_depth)) { 1235 vwr.depthByPixels(deltaY); 1236 return; 1237 } 1238 if (bnd(dragWheelAction, ACTION_slab)) { 1239 vwr.slabByPixels(deltaY); 1240 return; 1241 } 1242 if (bnd(dragWheelAction, ACTION_slabAndDepth)) { 1243 vwr.slabDepthByPixels(deltaY); 1244 return; 1245 } 1246 } 1247 if (bnd(dragWheelAction, ACTION_wheelZoom)) { 1248 zoomByFactor(deltaY, Integer.MAX_VALUE, Integer.MAX_VALUE); 1249 return; 1250 } 1251 if (bnd(dragWheelAction, ACTION_rotateZ)) { 1252 setMotion(GenericPlatform.CURSOR_MOVE, true); 1253 vwr.rotateZBy(-deltaX, Integer.MAX_VALUE, Integer.MAX_VALUE); 1254 return; 1255 } 1256 } 1257 1258 /** 1259 * change actual coordinates of selected atoms from set dragSeleted TRUE or 1260 * set PICKING DRAGSELECTED 1261 * 1262 * Basically, set dragSelected adds new functionality to Jmol with alt-drag 1263 * and alt-shift drag, and set picking dragSelected replaces the standard 1264 * mouse drag with a move action and also adds rotate and z-shift options. 1265 * 1266 * set dragSelected also allows other picking types, such as set picking SELECT, 1267 * which uses double-click to start rotating/moving another molecule. 1268 * 1269 * @param a 1270 * @param deltaX 1271 * @param deltaY 1272 * @param isPickingDrag 1273 */ dragSelected(int a, int deltaX, int deltaY, boolean isPickingDrag)1274 private void dragSelected(int a, int deltaX, int deltaY, boolean isPickingDrag) { 1275 1276 // see footnotes below for ^, $, #, and * 1277 // 1278 // settings:^ set picking dragSelected set dragSelected 1279 // 1280 // move:# drag alt-shift-drag 1281 // rotate:#* alt-drag alt-drag 1282 // z-shift:# shift-drag (n/a) 1283 // 1284 // double-click:$ (starts measurement) (sets selected if set picking SELECT) 1285 // 1286 // # all actions involve whole molecules unless set allowMoveAtoms TRUE 1287 // ^ set picking dragSelected overrules set dragSelected 1288 // * rotate requires set allowRotateSelected TRUE 1289 // $ set dragSelected allows setting of a new molecule with double-click when set picking SELECT 1290 // $ set picking dragSelected allows measurements with double-click, as usual 1291 1292 setMotion(GenericPlatform.CURSOR_MOVE, true); 1293 if (bnd(a, ACTION_rotateSelected) && vwr.getBoolean(T.allowrotateselected)) 1294 vwr.rotateSelected(getDegrees(deltaX, true), getDegrees(deltaY, false), 1295 null); 1296 else 1297 vwr.moveSelected( 1298 deltaX, 1299 deltaY, 1300 (isPickingDrag && bnd(a, ACTION_dragZ) ? -deltaY : Integer.MIN_VALUE), 1301 Integer.MIN_VALUE, Integer.MIN_VALUE, null, true, false, dragged.modifiers); 1302 } 1303 1304 checkReleaseAction(int x, int y, long time, boolean dragRelease)1305 private void checkReleaseAction(int x, int y, long time, boolean dragRelease) { 1306 if (Logger.debuggingHigh && vwr.getBoolean(T.testflag1)) 1307 Logger.debug(Binding.getMouseActionName(pressAction, false)); 1308 vwr.checkInMotion(0); 1309 vwr.setInMotion(false); 1310 vwr.setCursor(GenericPlatform.CURSOR_DEFAULT); 1311 dragGesture.add(dragAction, x, y, time); 1312 // necessary for reactivating 1313 // if (dragRelease) 1314 // vwr.setModelKitRotateBondIndex(Integer.MIN_VALUE); 1315 if (dragAtomIndex >= 0) { 1316 if (apm == PICKING_DRAG_MINIMIZE 1317 || apm == PICKING_DRAG_MINIMIZE_MOLECULE) 1318 minimize(true); 1319 } 1320 if (apm == PICKING_ASSIGN_ATOM 1321 && bnd(clickAction, ACTION_assignNew)) { 1322 if (mp == null || dragAtomIndex < 0) { 1323 exitMeasurementMode(null); 1324 return; 1325 } else if (bondPickingMode == PICKING_ROTATE_BOND) { 1326 vwr.setModelkitProperty("bondAtomIndex", Integer.valueOf(dragAtomIndex)); 1327 exitMeasurementMode(null); 1328 return; 1329 } 1330 assignNew(x, y); 1331 return; 1332 } 1333 dragAtomIndex = -1; 1334 mkBondPressed = false; 1335 boolean isRbAction = isRubberBandSelect(dragAction); 1336 if (isRbAction) 1337 selectRb(clickAction); 1338 rubberbandSelectionMode = (b.name.equals("drag")); 1339 rectRubber.x = Integer.MAX_VALUE; 1340 if (dragRelease) { 1341 vwr.notifyMouseClicked(x, y, Binding.getMouseAction(pressedCount, 0, 1342 Event.RELEASED), Event.RELEASED); 1343 } 1344 if (isDrawOrLabelAction(dragAction)) { 1345 vwr.checkObjectDragged(Integer.MAX_VALUE, 0, x, y, dragAction); 1346 return; 1347 } 1348 if (haveSelection && dragSelectedMode && bnd(dragAction, ACTION_dragSelected)) 1349 vwr.moveSelected(Integer.MAX_VALUE, 0, Integer.MIN_VALUE, 1350 Integer.MIN_VALUE, Integer.MIN_VALUE, null, false, false, dragged.modifiers); 1351 1352 if (dragRelease 1353 && checkUserAction(pressAction, x, y, 0, 0, time, Event.RELEASED)) 1354 return; 1355 1356 if (vwr.getBoolean(T.allowgestures)) { 1357 if (bnd(dragAction, ACTION_swipe)) { 1358 float speed = getExitRate(); 1359 if (speed > 0) 1360 vwr.spinXYBy(dragGesture.getDX(4, 2), dragGesture.getDY(4, 2), 1361 speed * 30 * gestureSwipeFactor); 1362 if (vwr.g.logGestures) 1363 vwr.log("$NOW$ swipe " + dragGesture + " " + speed); 1364 return; 1365 } 1366 1367 } 1368 } 1369 checkClickAction(int x, int y, long time, int clickedCount)1370 private void checkClickAction(int x, int y, long time, int clickedCount) { 1371 // points are always picked up first, then atoms 1372 // so that atom picking can be superceded by draw picking 1373 // Binding.MOVED is used for some vwr methods. 1374 if (clickedCount > 0) { 1375 if (checkUserAction(clickAction, x, y, 0, 0, time, Binding.CLICK)) 1376 return; 1377 clickAction = vwr.notifyMouseClicked(x, y, clickAction, Binding.CLICK); 1378 if (clickAction == 0) 1379 return; 1380 } 1381 if (Logger.debuggingHigh && vwr.getBoolean(T.testflag1)) 1382 Logger.debug(Binding.getMouseActionName(clickAction, false)); 1383 if (bnd(clickAction, ACTION_clickFrank)) { 1384 if (vwr.frankClicked(x, y)) { 1385 vwr.popupMenu(-x, y, 'j'); 1386 return; 1387 } 1388 if (vwr.frankClickedModelKit(x, y)) { 1389 vwr.popupMenu(10, 0, 'm'); 1390 return; 1391 } 1392 } 1393 Point3fi nearestPoint = null; 1394 boolean isBond = false; 1395 boolean isIsosurface = false; 1396 Map<String, Object> map = null; 1397 // t.tok will let us know if this is an atom or a bond that was clicked 1398 if (!drawMode) { 1399 map = vwr.checkObjectClicked(x, y, clickAction); 1400 if (map != null) { 1401 if (labelMode) { 1402 pickLabel(((Integer)map.get("atomIndex")).intValue()); 1403 return; 1404 } 1405 isBond = "bond".equals(map.get("type")); 1406 isIsosurface = "isosurface".equals(map.get("type")); 1407 nearestPoint = getPoint(map); 1408 } 1409 } 1410 if (isBond) 1411 clickedCount = 1; 1412 1413 if (nearestPoint != null && Float.isNaN(nearestPoint.x)) 1414 return; 1415 int nearestAtomIndex = findNearestAtom(x, y, nearestPoint, clickedCount > 0); 1416 1417 if (clickedCount == 0 && apm != PICKING_ASSIGN_ATOM) { 1418 // mouse move 1419 if (mp == null) 1420 return; 1421 if (nearestPoint != null 1422 || mp.getIndexOf(nearestAtomIndex) == 0) 1423 mp.addPoint(nearestAtomIndex, nearestPoint, false); 1424 if (mp.haveModified) 1425 vwr.setPendingMeasurement(mp); 1426 vwr.refresh(Viewer.REFRESH_SYNC_MASK, "measurementPending"); 1427 return; 1428 } 1429 setMouseMode(); 1430 1431 if (bnd(clickAction, ACTION_stopMotion)) { 1432 vwr.tm.stopMotion(); 1433 // continue checking --- no need to exit here 1434 } 1435 1436 if (vwr.getBoolean(T.navigationmode) 1437 && apm == PICKING_NAVIGATE 1438 && bnd(clickAction, ACTION_pickNavigate)) { 1439 vwr.navTranslatePercent(x * 100f / vwr.getScreenWidth() - 50f, y 1440 * 100f / vwr.getScreenHeight() - 50f); 1441 return; 1442 } 1443 1444 // bond change by clicking on a bond 1445 // bond deletion by clicking a bond 1446 if (isBond) { 1447 if (bnd(clickAction, bondPickingMode == PICKING_ROTATE_BOND 1448 || bondPickingMode == PICKING_ASSIGN_BOND ? ACTION_assignNew 1449 : ACTION_deleteBond)) { 1450 bondPicked(((Integer) map.get("index")).intValue()); 1451 return; 1452 } 1453 } else if (isIsosurface) { 1454 return; 1455 } else { 1456 if (apm != PICKING_ASSIGN_ATOM && mp != null 1457 && bnd(clickAction, ACTION_pickMeasure)) { 1458 atomOrPointPicked(nearestAtomIndex, nearestPoint); 1459 if (addToMeasurement(nearestAtomIndex, nearestPoint, false) == 4) 1460 toggleMeasurement(); 1461 return; 1462 } 1463 1464 if (bnd(clickAction, ACTION_setMeasure)) { 1465 if (mp != null) { 1466 addToMeasurement(nearestAtomIndex, nearestPoint, true); 1467 toggleMeasurement(); 1468 } else if (!drawMode && !labelMode && !dragSelectedMode 1469 && measuresEnabled) { 1470 enterMeasurementMode(nearestAtomIndex); 1471 addToMeasurement(nearestAtomIndex, nearestPoint, true); 1472 } 1473 atomOrPointPicked(nearestAtomIndex, nearestPoint); 1474 return; 1475 } 1476 } 1477 if (isSelectAction(clickAction)) { 1478 // TODO: in drawMode the binding changes 1479 if (!isIsosurface) 1480 atomOrPointPicked(nearestAtomIndex, nearestPoint); 1481 return; 1482 } 1483 if (bnd(clickAction, ACTION_reset)) { 1484 if (nearestAtomIndex < 0) 1485 reset(); 1486 return; 1487 } 1488 } 1489 pickLabel(int iatom)1490 private void pickLabel(int iatom) { 1491 String label = vwr.ms.at[iatom].atomPropertyString(vwr, T.label); 1492 if (pressedCount == 2) { 1493 label = vwr.apiPlatform.prompt("Set label for atomIndex=" + iatom, label, null, false); 1494 if (label != null) { 1495 vwr.shm.setAtomLabel(label, iatom); 1496 vwr.refresh(Viewer.REFRESH_REPAINT, "label atom"); 1497 } 1498 } else { 1499 setAtomsPicked(BSUtil.newAndSetBit(iatom), "Label picked for atomIndex = " + iatom + ": " + label); 1500 } 1501 } 1502 checkUserAction(int mouseAction, int x, int y, int deltaX, int deltaY, long time, int mode)1503 private boolean checkUserAction(int mouseAction, int x, int y, int deltaX, 1504 int deltaY, long time, int mode) { 1505 if (!b.isUserAction(mouseAction)) 1506 return false; 1507 boolean passThrough = false; 1508 Object obj; 1509 Map<String, Object> ht = b.getBindings(); 1510 String mkey = mouseAction + "\t"; 1511 for (String key : ht.keySet()) { 1512 if (key.indexOf(mkey) != 0 || !AU.isAS(obj = ht.get(key))) 1513 continue; 1514 String script = ((String[]) obj)[1]; 1515 P3 nearestPoint = null; 1516 if (script.indexOf("_ATOM") >= 0) { 1517 int iatom = findNearestAtom(x, y, null, true); 1518 script = PT.rep(script, "_ATOM", "({" 1519 + (iatom >= 0 ? "" + iatom : "") + "})"); 1520 if (iatom >= 0) 1521 script = PT.rep(script, "_POINT", Escape.eP(vwr 1522 .ms.at[iatom])); 1523 } 1524 if (!drawMode 1525 && (script.indexOf("_POINT") >= 0 || script.indexOf("_OBJECT") >= 0 || script 1526 .indexOf("_BOND") >= 0)) { 1527 Map<String, Object> t = vwr.checkObjectClicked(x, y, mouseAction); 1528 if (t != null && (nearestPoint = (P3) t.get("pt")) != null) { 1529 boolean isBond = t.get("type").equals("bond"); 1530 if (isBond) 1531 script = PT.rep(script, "_BOND", "[{" 1532 + t.get("index") + "}]"); 1533 script = PT.rep(script, "_POINT", Escape 1534 .eP(nearestPoint)); 1535 script = PT.rep(script, "_OBJECT", Escape 1536 .escapeMap(t)); 1537 } 1538 script = PT.rep(script, "_BOND", "[{}]"); 1539 script = PT.rep(script, "_OBJECT", "{}"); 1540 } 1541 script = PT.rep(script, "_POINT", "{}"); 1542 script = PT.rep(script, "_ACTION", "" + mouseAction); 1543 script = PT.rep(script, "_X", "" + x); 1544 script = PT.rep(script, "_Y", "" 1545 + (vwr.getScreenHeight() - y)); 1546 script = PT.rep(script, "_DELTAX", "" + deltaX); 1547 script = PT.rep(script, "_DELTAY", "" + deltaY); 1548 script = PT.rep(script, "_TIME", "" + time); 1549 script = PT.rep(script, "_MODE", "" + mode); 1550 if (script.startsWith("+:")) { 1551 passThrough = true; 1552 script = script.substring(2); 1553 } 1554 vwr.evalStringQuiet(script); 1555 } 1556 return !passThrough; 1557 } 1558 1559 /** 1560 * 1561 * @param mouseAction 1562 * @param x 1563 * @param deltaX 1564 * @param deltaY 1565 * @param isDrag 1566 * @return TRUE if motion was a zoom 1567 */ checkMotionRotateZoom(int mouseAction, int x, int deltaX, int deltaY, boolean isDrag)1568 private boolean checkMotionRotateZoom(int mouseAction, int x, int deltaX, 1569 int deltaY, boolean isDrag) { 1570 boolean isSlideZoom = bnd(mouseAction, ACTION_slideZoom) && isZoomArea(pressed.x); 1571 boolean isRotateXY = bnd(mouseAction, ACTION_rotate); 1572 boolean isRotateZorZoom = bnd(mouseAction, ACTION_rotateZorZoom); 1573 if (!isSlideZoom && !isRotateXY && !isRotateZorZoom) 1574 return false; 1575 boolean isZoom = (isRotateZorZoom && (deltaX == 0 || Math.abs(deltaY) > 5 * Math 1576 .abs(deltaX))); 1577 int cursor = (isZoom || isZoomArea(moved.x) 1578 || bnd(mouseAction, ACTION_wheelZoom) ? GenericPlatform.CURSOR_ZOOM 1579 : isRotateXY || isRotateZorZoom ? GenericPlatform.CURSOR_MOVE : bnd( 1580 mouseAction, ACTION_center) ? GenericPlatform.CURSOR_HAND 1581 : GenericPlatform.CURSOR_DEFAULT); 1582 setMotion(cursor, isDrag); 1583 return (isZoom || isSlideZoom); 1584 } 1585 getExitRate()1586 private float getExitRate() { 1587 long dt = dragGesture.getTimeDifference(2); 1588 return (isMultiTouch ? (dt > (MININUM_GESTURE_DELAY_MILLISECONDS << 3) ? 0 : 1589 dragGesture.getSpeedPixelsPerMillisecond(2, 1)) 1590 : (dt > MININUM_GESTURE_DELAY_MILLISECONDS ? 0 : dragGesture 1591 .getSpeedPixelsPerMillisecond(4, 2))); 1592 } 1593 isRubberBandSelect(int action)1594 private boolean isRubberBandSelect(int action) { 1595 // drag and wheel and release 1596 action = action & ~Binding.DRAG | Binding.CLICK; 1597 return (rubberbandSelectionMode 1598 && bnd(action, ACTION_selectToggle, ACTION_selectOr, ACTION_selectAndNot)); 1599 } 1600 getRubberBand()1601 Rectangle getRubberBand() { 1602 return (rubberbandSelectionMode && rectRubber.x != Integer.MAX_VALUE ? rectRubber 1603 : null); 1604 } 1605 calcRectRubberBand()1606 private void calcRectRubberBand() { 1607 int factor = (vwr.antialiased ? 2 : 1); 1608 if (current.x < pressed.x) { 1609 rectRubber.x = current.x * factor; 1610 rectRubber.width = (pressed.x - current.x) * factor; 1611 } else { 1612 rectRubber.x = pressed.x * factor; 1613 rectRubber.width = (current.x - pressed.x) * factor; 1614 } 1615 if (current.y < pressed.y) { 1616 rectRubber.y = current.y * factor; 1617 rectRubber.height = (pressed.y - current.y) * factor; 1618 } else { 1619 rectRubber.y = pressed.y * factor; 1620 rectRubber.height = (current.y - pressed.y) * factor; 1621 } 1622 } 1623 1624 /** 1625 * Transform a screen pixel change to an angular change 1626 * such that a full sweep of the dimension (up to 500 pixels) 1627 * corresponds to 180 degrees of rotation. 1628 * 1629 * @param delta 1630 * @param isX 1631 * @return desired scaled rotation, in degrees 1632 */ getDegrees(float delta, boolean isX)1633 protected float getDegrees(float delta, boolean isX) { 1634 return delta / Math.min(500, isX ? vwr.getScreenWidth() 1635 : vwr.getScreenHeight()) * 180 * mouseDragFactor; 1636 } 1637 isZoomArea(int x)1638 private boolean isZoomArea(int x) { 1639 return x > vwr.getScreenWidth() * (vwr.tm.stereoDoubleFull || vwr.tm.stereoDoubleDTI ? 2 : 1) 1640 * SLIDE_ZOOM_X_PERCENT / 100f; 1641 } 1642 getPoint(Map<String, Object> t)1643 private Point3fi getPoint(Map<String, Object> t) { 1644 Point3fi pt = new Point3fi(); 1645 pt.setT((P3) t.get("pt")); 1646 pt.mi = (short) ((Integer) t.get("modelIndex")).intValue(); 1647 return pt; 1648 } 1649 findNearestAtom(int x, int y, Point3fi nearestPoint, boolean isClicked)1650 private int findNearestAtom(int x, int y, Point3fi nearestPoint, 1651 boolean isClicked) { 1652 int index = (drawMode || nearestPoint != null ? -1 : vwr 1653 .findNearestAtomIndexMovable(x, y, false)); 1654 return (index >= 0 && (isClicked || mp == null) 1655 && !vwr.slm.isInSelectionSubset(index) ? -1 : index); 1656 } 1657 isSelectAction(int action)1658 private boolean isSelectAction(int action) { 1659 return (bnd(action, ACTION_pickAtom) 1660 || !drawMode 1661 && !labelMode 1662 && apm == PICKING_IDENTIFY 1663 && bnd(action, ACTION_center) 1664 || dragSelectedMode 1665 && bnd(dragAction, ACTION_rotateSelected, ACTION_dragSelected) 1666 || bnd(action, ACTION_pickPoint, ACTION_selectToggle, ACTION_selectAndNot, 1667 ACTION_selectOr, ACTION_selectToggleExtended, ACTION_select)); 1668 } 1669 1670 //////////// specific actions //////////////// 1671 1672 private MeasurementPending measurementQueued; 1673 public boolean zoomTrigger; 1674 enterMeasurementMode(int iAtom)1675 private void enterMeasurementMode(int iAtom) { 1676 vwr.setPicked(iAtom, true); 1677 vwr.setCursor(GenericPlatform.CURSOR_CROSSHAIR); 1678 vwr.setPendingMeasurement(mp = getMP()); 1679 measurementQueued = mp; 1680 } 1681 getMP()1682 private MeasurementPending getMP() { 1683 return ((MeasurementPending) Interface 1684 .getInterface("org.jmol.modelset.MeasurementPending", vwr, "mouse")).set(vwr.ms); 1685 } 1686 addToMeasurement(int atomIndex, Point3fi nearestPoint, boolean dblClick)1687 private int addToMeasurement(int atomIndex, Point3fi nearestPoint, 1688 boolean dblClick) { 1689 if (atomIndex == -1 && nearestPoint == null || mp == null) { 1690 exitMeasurementMode(null); 1691 return 0; 1692 } 1693 int measurementCount = mp.count; 1694 if (mp.traceX != Integer.MIN_VALUE && measurementCount == 2) 1695 mp.setCount(measurementCount = 1); 1696 return (measurementCount == 4 && !dblClick ? measurementCount 1697 : mp.addPoint(atomIndex, nearestPoint, true)); 1698 } 1699 resetMeasurement()1700 private void resetMeasurement() { 1701 // doesn't reset the measurement that is being picked using 1702 // double-click, just the one using set picking measure. 1703 exitMeasurementMode(null); 1704 measurementQueued = getMP(); 1705 } 1706 exitMeasurementMode(String refreshWhy)1707 void exitMeasurementMode(String refreshWhy) { 1708 if (mp == null) 1709 return; 1710 vwr.setPendingMeasurement(mp = null); 1711 vwr.setCursor(GenericPlatform.CURSOR_DEFAULT); 1712 if (refreshWhy != null) 1713 vwr.refresh(Viewer.REFRESH_SYNC_MASK, refreshWhy); 1714 } 1715 getSequence()1716 private void getSequence() { 1717 int a1 = measurementQueued.getAtomIndex(1); 1718 int a2 = measurementQueued.getAtomIndex(2); 1719 if (a1 < 0 || a2 < 0) 1720 return; 1721 try { 1722 String sequence = vwr.getSmilesOpt(null, a1, a2, JC.SMILES_GEN_BIO, null); 1723 vwr.setStatusMeasuring("measureSequence", -2, sequence, 0); 1724 } catch (Exception e) { 1725 Logger.error(e.toString()); 1726 } 1727 } 1728 minimize(boolean dragDone)1729 private void minimize(boolean dragDone) { 1730 int iAtom = dragAtomIndex; 1731 if (dragDone) { 1732 dragAtomIndex = -1; 1733 mkBondPressed = false; 1734 } 1735 vwr.dragMinimizeAtom(iAtom); 1736 } 1737 queueAtom(int atomIndex, Point3fi ptClicked)1738 private int queueAtom(int atomIndex, Point3fi ptClicked) { 1739 int n = measurementQueued.addPoint(atomIndex, ptClicked, true); 1740 if (atomIndex >= 0) 1741 vwr.setStatusAtomPicked(atomIndex, "Atom #" + n + ":" 1742 + vwr.getAtomInfo(atomIndex), null, false); 1743 return n; 1744 } 1745 setMotion(int cursor, boolean inMotion)1746 protected void setMotion(int cursor, boolean inMotion) { 1747 switch (vwr.currentCursor) { 1748 case GenericPlatform.CURSOR_WAIT: 1749 break; 1750 default: 1751 vwr.setCursor(cursor); 1752 } 1753 if (inMotion) 1754 vwr.setInMotion(true); 1755 } 1756 zoomByFactor(int dz, int x, int y)1757 protected void zoomByFactor(int dz, int x, int y) { 1758 if (dz == 0) 1759 return; 1760 setMotion(GenericPlatform.CURSOR_ZOOM, true); 1761 vwr.zoomByFactor((float) Math.pow(mouseWheelFactor, dz), x, y); 1762 moved.setCurrent(current, 0); 1763 vwr.setInMotion(true); 1764 zoomTrigger = true; 1765 startHoverWatcher(true); 1766 } 1767 1768 1769 /// methods that utilize vwr.script 1770 runScript(String script)1771 private void runScript(String script) { 1772 vwr.script(script); 1773 } 1774 atomOrPointPicked(int atomIndex, Point3fi ptClicked)1775 private void atomOrPointPicked(int atomIndex, Point3fi ptClicked) { 1776 // atomIndex < 0 is off structure. 1777 // if picking spin or picking symmetry is on, then 1778 // we need to enter this method to process those events. 1779 if (atomIndex < 0) { 1780 resetMeasurement(); // for set picking measure only 1781 if (bnd(clickAction, ACTION_selectNone)) { 1782 runScript("select none"); 1783 return; 1784 } 1785 if (apm != PICKING_SPIN 1786 && apm != PICKING_SYMMETRY) 1787 return; 1788 } 1789 int n = 2; 1790 switch (apm) { 1791 case PICKING_DRAG_ATOM: 1792 // this is done in mouse drag, not mouse release 1793 case PICKING_DRAG_MINIMIZE: 1794 return; 1795 case PICKING_OFF: 1796 return; 1797 case PICKING_STRUTS: 1798 case PICKING_CONNECT: 1799 case PICKING_DELETE_BOND: 1800 boolean isDelete = (apm == PICKING_DELETE_BOND); 1801 boolean isStruts = (apm == PICKING_STRUTS); 1802 if (!bnd(clickAction, (isDelete ? ACTION_deleteBond 1803 : ACTION_connectAtoms))) 1804 return; 1805 if (measurementQueued == null || measurementQueued.count == 0 1806 || measurementQueued.count > 2) { 1807 resetMeasurement(); 1808 enterMeasurementMode(atomIndex); 1809 } 1810 addToMeasurement(atomIndex, ptClicked, true); 1811 if (queueAtom(atomIndex, ptClicked) != 2) 1812 return; 1813 String cAction = (isDelete 1814 || measurementQueued.isConnected(vwr.ms.at, 2) ? " DELETE" 1815 : isStruts ? "STRUTS" : ""); 1816 runScript("connect " + measurementQueued.getMeasurementScript(" ", true) 1817 + cAction); 1818 resetMeasurement(); 1819 return; 1820 case PICKING_MEASURE_TORSION: 1821 n++; 1822 //$FALL-THROUGH$ 1823 case PICKING_MEASURE_ANGLE: 1824 n++; 1825 //$FALL-THROUGH$ 1826 case PICKING_MEASURE: 1827 case PICKING_MEASURE_DISTANCE: 1828 case PICKING_MEASURE_SEQUENCE: 1829 if (!bnd(clickAction, ACTION_pickMeasure)) 1830 return; 1831 if (measurementQueued == null || measurementQueued.count == 0 1832 || measurementQueued.count > n) { 1833 resetMeasurement(); 1834 enterMeasurementMode(atomIndex); 1835 } 1836 addToMeasurement(atomIndex, ptClicked, true); 1837 queueAtom(atomIndex, ptClicked); 1838 int i = measurementQueued.count; 1839 if (i == 1) 1840 vwr.setPicked(atomIndex, true); 1841 if (i < n) 1842 return; 1843 if (apm == PICKING_MEASURE_SEQUENCE) { 1844 getSequence(); 1845 } else { 1846 vwr.setStatusMeasuring("measurePicked", n, measurementQueued 1847 .getStringDetail(), measurementQueued.value); 1848 if (apm == PICKING_MEASURE 1849 || pickingStyleMeasure == PICKINGSTYLE_MEASURE_ON) { 1850 runScript("measure " 1851 + measurementQueued.getMeasurementScript(" ", true)); 1852 } 1853 } 1854 resetMeasurement(); 1855 return; 1856 } 1857 int mode = (mp != null 1858 && apm != PICKING_IDENTIFY ? PICKING_IDENTIFY 1859 : apm); 1860 switch (mode) { 1861 case PICKING_CENTER: 1862 if (!bnd(clickAction, ACTION_pickAtom)) 1863 return; 1864 if (ptClicked == null) { 1865 zoomTo(atomIndex); 1866 } else { 1867 runScript("zoomTo " + Escape.eP(ptClicked)); 1868 } 1869 return; 1870 case PICKING_SPIN: 1871 case PICKING_SYMMETRY: 1872 if (bnd(clickAction, ACTION_pickAtom)) 1873 checkTwoAtomAction(ptClicked, atomIndex); 1874 } 1875 if (ptClicked != null) 1876 return; 1877 // atoms only here: 1878 BS bs; 1879 switch (mode) { 1880 case PICKING_IDENTIFY: 1881 if (!drawMode && !labelMode && bnd(clickAction, ACTION_center)) 1882 zoomTo(atomIndex); 1883 else if (bnd(clickAction, ACTION_pickAtom)) 1884 vwr.setStatusAtomPicked(atomIndex, null, null, false); 1885 return; 1886 case PICKING_LABEL: 1887 if (bnd(clickAction, ACTION_pickLabel)) { 1888 runScript("set labeltoggle {atomindex=" + atomIndex + "}"); 1889 pickLabel(atomIndex); 1890 } 1891 return; 1892 case PICKING_INVERT_STEREO: 1893 if (bnd(clickAction, ACTION_assignNew)) { 1894 vwr.invertRingAt(atomIndex, true); 1895 vwr.setStatusAtomPicked(atomIndex, "invert stereo for atomIndex=" + atomIndex, null, false); 1896 } 1897 return; 1898 case PICKING_DELETE_ATOM: 1899 if (bnd(clickAction, ACTION_deleteAtom)) { 1900 bs = BSUtil.newAndSetBit(atomIndex); 1901 vwr.deleteAtoms(bs, false); 1902 vwr.setStatusAtomPicked(atomIndex, "deleted: " + Escape.eBS(bs), null, false); 1903 } 1904 return; 1905 } 1906 // set picking select options: 1907 String spec = "atomindex=" + atomIndex; 1908 switch (apm) { 1909 default: 1910 return; 1911 case PICKING_SELECT_ATOM: 1912 selectAtoms(spec); 1913 break; 1914 case PICKING_SELECT_GROUP: 1915 selectAtoms("within(group, " + spec + ")"); 1916 break; 1917 case PICKING_SELECT_CHAIN: 1918 selectAtoms("within(chain, " + spec + ")"); 1919 break; 1920 case PICKING_SELECT_POLYMER: 1921 selectAtoms("within(polymer, " + spec + ")"); 1922 break; 1923 case PICKING_SELECT_STRUCTURE: 1924 selectAtoms("within(structure, " + spec + ")"); 1925 break; 1926 case PICKING_SELECT_MOLECULE: 1927 selectAtoms("within(molecule, " + spec + ")"); 1928 break; 1929 case PICKING_SELECT_MODEL: 1930 selectAtoms("within(model, " + spec + ")"); 1931 break; 1932 // only the next two use VISIBLE (as per the documentation) 1933 case PICKING_SELECT_ELEMENT: 1934 selectAtoms("visible and within(element, " + spec + ")"); 1935 break; 1936 case PICKING_SELECT_SITE: 1937 selectAtoms("visible and within(site, " + spec + ")"); 1938 break; 1939 } 1940 vwr.clearClickCount(); 1941 vwr.setStatusAtomPicked(atomIndex, null, null, false); 1942 } 1943 assignNew(int x, int y)1944 private void assignNew(int x, int y) { 1945 if (!vwr.getModelkit(false).handleAssignNew(pressed, dragged, mp, dragAtomIndex)) { 1946 exitMeasurementMode("bond dropped"); 1947 } 1948 exitMeasurementMode(null); 1949 } 1950 bondPicked(int index)1951 private void bondPicked(int index) { 1952 if (bondPickingMode == PICKING_ASSIGN_BOND) { 1953 vwr.undoMoveActionClear(-1, T.save, true); 1954 } 1955 1956 switch (bondPickingMode) { 1957 case PICKING_ASSIGN_BOND: 1958 vwr.setModelkitProperty("scriptAssignBond", Integer.valueOf(index)); 1959 break; 1960 case PICKING_ROTATE_BOND: 1961 // done separately 1962 break; 1963 case PICKING_DELETE_BOND: 1964 vwr.deleteBonds(BSUtil.newAndSetBit(index)); 1965 } 1966 } 1967 checkTwoAtomAction(Point3fi ptClicked, int atomIndex)1968 private void checkTwoAtomAction(Point3fi ptClicked, int atomIndex) { 1969 boolean isSpin = (apm == PICKING_SPIN); 1970 if (vwr.tm.spinOn || vwr.tm.navOn 1971 || vwr.getPendingMeasurement() != null) { 1972 resetMeasurement(); 1973 if (vwr.tm.spinOn) 1974 runScript("spin off"); 1975 return; 1976 } 1977 if (measurementQueued.count >= 2) 1978 resetMeasurement(); 1979 int queuedAtomCount = measurementQueued.count; 1980 if (queuedAtomCount == 1) { 1981 if (ptClicked == null) { 1982 if (measurementQueued.getAtomIndex(1) == atomIndex) 1983 return; 1984 } else { 1985 if (measurementQueued.getAtom(1).distance(ptClicked) == 0) 1986 return; 1987 } 1988 } 1989 if (atomIndex >= 0 || ptClicked != null) 1990 queuedAtomCount = queueAtom(atomIndex, ptClicked); 1991 if (queuedAtomCount < 2) { 1992 if (isSpin) 1993 vwr.scriptStatus(queuedAtomCount == 1 ? GT 1994 .$("pick one more atom in order to spin the model around an axis") 1995 : GT.$("pick two atoms in order to spin the model around an axis")); 1996 else 1997 vwr 1998 .scriptStatus(queuedAtomCount == 1 ? GT 1999 .$("pick one more atom in order to display the symmetry relationship") 2000 : GT 2001 .$("pick two atoms in order to display the symmetry relationship between them")); 2002 return; 2003 } 2004 String s = measurementQueued.getMeasurementScript(" ", false); 2005 if (isSpin) 2006 runScript("spin" + s + " " + vwr.getInt(T.pickingspinrate)); 2007 else 2008 runScript("draw symop " + s + ";show symop " + s); 2009 } 2010 reset()2011 private void reset() { 2012 runScript("!reset"); 2013 } 2014 2015 private boolean selectionWorking = false; 2016 selectAtoms(String item)2017 private void selectAtoms(String item) { 2018 if (mp != null || selectionWorking) 2019 return; 2020 selectionWorking = true; 2021 String s = (rubberbandSelectionMode 2022 || bnd(clickAction, ACTION_selectToggle) ? "selected and not (" 2023 + item + ") or (not selected) and " : bnd(clickAction, 2024 ACTION_selectAndNot) ? "selected and not " : bnd(clickAction, 2025 ACTION_selectOr) ? "selected or " : clickAction == 0 2026 || bnd(clickAction, ACTION_selectToggleExtended) ? "selected tog " 2027 : bnd(clickAction, ACTION_select) ? "" : null); 2028 if (s != null) { 2029 s += "(" + item + ")"; 2030 try { 2031 BS bs = vwr.getAtomBitSetEval(null, s); 2032 setAtomsPicked(bs, "selected: " + Escape.eBS(bs)); 2033 vwr.refresh(Viewer.REFRESH_SYNC_MASK, "selections set"); 2034 } catch (Exception e) { 2035 // ignore 2036 } 2037 } 2038 selectionWorking = false; 2039 } 2040 setAtomsPicked(BS bs, String msg)2041 private void setAtomsPicked(BS bs, String msg) { 2042 vwr.select(bs, false, 0, false); 2043 vwr.setStatusAtomPicked(-1, msg, null, false); 2044 } 2045 selectRb(int action)2046 private void selectRb(int action) { 2047 BS bs = vwr.ms.findAtomsInRectangle(rectRubber); 2048 if (bs.length() > 0) { 2049 String s = Escape.eBS(bs); 2050 if (bnd(action, ACTION_selectOr)) 2051 runScript("selectionHalos on;select selected or " + s); 2052 else if (bnd(action, ACTION_selectAndNot)) 2053 runScript("selectionHalos on;select selected and not " + s); 2054 else 2055 // ACTION_selectToggle 2056 runScript("selectionHalos on;select selected tog " + s); 2057 } 2058 vwr.refresh(Viewer.REFRESH_SYNC_MASK, "mouseReleased"); 2059 } 2060 toggleMeasurement()2061 private void toggleMeasurement() { 2062 if (mp == null) 2063 return; 2064 int measurementCount = mp.count; 2065 if (measurementCount >= 2 && measurementCount <= 4) 2066 runScript("!measure " 2067 + mp.getMeasurementScript(" ", true)); 2068 exitMeasurementMode(null); 2069 } 2070 zoomTo(int atomIndex)2071 private void zoomTo(int atomIndex) { 2072 runScript("zoomTo (atomindex=" + atomIndex + ")"); 2073 vwr.setStatusAtomPicked(atomIndex, null, null, false); 2074 } 2075 userActionEnabled(int action)2076 public boolean userActionEnabled(int action) { 2077 return vwr.isFunction(getActionName(action).toLowerCase()); 2078 } 2079 2080 /** 2081 * If the user has created a function to handle this action, 2082 * run it and cancel action processing if that function returns an explicit FALSE; 2083 * 2084 * @param action 2085 * @param params 2086 * @return true to continue with the standard action 2087 */ userAction(int action, Object[] params)2088 public boolean userAction(int action, Object[] params) { 2089 if (!userActionEnabled(action)) 2090 return false; 2091 SV result = ScriptEval.runUserAction(getActionName(action), params, vwr); 2092 return !SV.vF.equals(result); 2093 } 2094 2095 } 2096 2097 class MotionPoint { 2098 int index; 2099 int x; 2100 int y; 2101 long time; 2102 set(int index, int x, int y, long time)2103 void set(int index, int x, int y, long time) { 2104 this.index = index; 2105 this.x = x; 2106 this.y = y; 2107 this.time = time; 2108 } 2109 2110 @Override toString()2111 public String toString() { 2112 return "[x = " + x + " y = " + y + " time = " + time + " ]"; 2113 } 2114 } 2115 2116 class Gesture { 2117 private int action; 2118 MotionPoint[] nodes; 2119 private int ptNext; 2120 private long time0; 2121 private Viewer vwr; 2122 Gesture(int nPoints, Viewer vwr)2123 public Gesture(int nPoints, Viewer vwr) { 2124 this.vwr = vwr; 2125 nodes = new MotionPoint[nPoints]; 2126 for (int i = 0; i < nPoints; i++) 2127 nodes[i] = new MotionPoint(); 2128 } 2129 setAction(int action, long time)2130 void setAction(int action, long time) { 2131 this.action = action; 2132 ptNext = 0; 2133 time0 = time; 2134 for (int i = 0; i < nodes.length; i++) 2135 nodes[i].index = -1; 2136 } 2137 add(int action, int x, int y, long time)2138 int add(int action, int x, int y, long time) { 2139 this.action = action; 2140 getNode(ptNext).set(ptNext, x, y, time - time0); 2141 ptNext++; 2142 return ptNext; 2143 } 2144 getTimeDifference(int nPoints)2145 public long getTimeDifference(int nPoints) { 2146 nPoints = getPointCount2(nPoints, 0); 2147 if (nPoints < 2) 2148 return 0; 2149 MotionPoint mp1 = getNode(ptNext - 1); 2150 MotionPoint mp0 = getNode(ptNext - nPoints); 2151 return mp1.time - mp0.time; 2152 } 2153 getSpeedPixelsPerMillisecond(int nPoints, int nPointsPrevious)2154 public float getSpeedPixelsPerMillisecond(int nPoints, int nPointsPrevious) { 2155 nPoints = getPointCount2(nPoints, nPointsPrevious); 2156 if (nPoints < 2) 2157 return 0; 2158 MotionPoint mp1 = getNode(ptNext - 1 - nPointsPrevious); 2159 MotionPoint mp0 = getNode(ptNext - nPoints - nPointsPrevious); 2160 float dx = ((float) (mp1.x - mp0.x)) / vwr.getScreenWidth() * 360; 2161 float dy = ((float) (mp1.y - mp0.y)) / vwr.getScreenHeight() * 360; 2162 return (float) Math.sqrt(dx * dx + dy * dy) / (mp1.time - mp0.time); 2163 } 2164 getDX(int nPoints, int nPointsPrevious)2165 int getDX(int nPoints, int nPointsPrevious) { 2166 nPoints = getPointCount2(nPoints, nPointsPrevious); 2167 if (nPoints < 2) 2168 return 0; 2169 MotionPoint mp1 = getNode(ptNext - 1 - nPointsPrevious); 2170 MotionPoint mp0 = getNode(ptNext - nPoints - nPointsPrevious); 2171 return mp1.x - mp0.x; 2172 } 2173 getDY(int nPoints, int nPointsPrevious)2174 int getDY(int nPoints, int nPointsPrevious) { 2175 nPoints = getPointCount2(nPoints, nPointsPrevious); 2176 if (nPoints < 2) 2177 return 0; 2178 MotionPoint mp1 = getNode(ptNext - 1 - nPointsPrevious); 2179 MotionPoint mp0 = getNode(ptNext - nPoints - nPointsPrevious); 2180 return mp1.y - mp0.y; 2181 } 2182 getPointCount()2183 int getPointCount() { 2184 return ptNext; 2185 } 2186 getPointCount2(int nPoints, int nPointsPrevious)2187 private int getPointCount2(int nPoints, int nPointsPrevious) { 2188 if (nPoints > nodes.length - nPointsPrevious) 2189 nPoints = nodes.length - nPointsPrevious; 2190 int n = nPoints + 1; 2191 for (; --n >= 0;) 2192 if (getNode(ptNext - n - nPointsPrevious).index >= 0) 2193 break; 2194 return n; 2195 } 2196 getNode(int i)2197 MotionPoint getNode(int i) { 2198 return nodes[(i + nodes.length + nodes.length) % nodes.length]; 2199 } 2200 2201 @Override toString()2202 public String toString() { 2203 if (nodes.length == 0) 2204 return "" + this; 2205 return Binding.getMouseActionName(action, false) + " nPoints = " + ptNext 2206 + " " + nodes[0]; 2207 } 2208 } 2209 2210