1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2009-06-26 23:35:44 -0500 (Fri, 26 Jun 2009) $ 4 * $Revision: 11131 $ 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.openscience.jmol.app.jmolpanel; 25 26 import java.awt.Component; 27 import java.io.FileInputStream; 28 import java.io.FileOutputStream; 29 import java.lang.reflect.Method; 30 import java.net.URI; 31 import java.util.Hashtable; 32 import java.util.Map; 33 import java.util.Properties; 34 35 import org.jmol.api.JSVInterface; 36 import org.jmol.api.JmolAppConsoleInterface; 37 import org.jmol.api.JmolCallbackListener; 38 import org.jmol.api.JmolStatusListener; 39 import org.jmol.api.JmolSyncInterface; 40 import org.jmol.c.CBK; 41 import org.jmol.dialog.Dialog; 42 import org.jmol.script.T; 43 import org.jmol.util.Logger; 44 import org.jmol.viewer.Viewer; 45 import org.openscience.jmol.app.JmolPlugin; 46 import org.openscience.jmol.app.jmolpanel.console.AppConsole; 47 import org.openscience.jmol.app.webexport.WebExport; 48 49 import javajs.util.PT; 50 import jspecview.application.MainFrame; 51 52 public class StatusListener implements JmolStatusListener, JmolSyncInterface, JSVInterface { 53 54 /* 55 * starting with Jmol 11.7.27, JmolStatusListener extends JmolCallbackListener 56 * 57 * providing a simpler interface if all that is wanted is callback 58 * functionality. 59 * 60 * Only three methods are involved: 61 * 62 * boolean notifyEnabled(int type) -- lets the statusManager know if there is 63 * an implementation of a given callback type 64 * 65 * void notifyCallback(int type, Object[] data) -- callback action; data 66 * varies with callback type -- see org.jmol.viewer.StatusManager for details 67 * 68 * void setCallbackFunction(String callbackType, String callbackFunction) -- 69 * called by statusManager in response to the "set callback" script command -- 70 * also used by the Jmol application to change menus and languages -- can 71 * remain unimplemented if no such user action is intended 72 */ 73 74 private JmolPanel jmolPanel; 75 private DisplayPanel display; 76 77 private Viewer vwr; 78 private MainFrame jSpecViewFrame; 79 private boolean jSpecViewForceNew; 80 81 setViewer(Viewer vwr)82 public void setViewer(Viewer vwr) { 83 this.vwr = vwr; 84 } 85 StatusListener(JmolPanel jmolPanel, DisplayPanel display)86 public StatusListener(JmolPanel jmolPanel, DisplayPanel display) { 87 // just required for Jmol application's particular callbacks 88 this.jmolPanel = jmolPanel; 89 this.display = display; 90 } 91 92 // / JmolCallbackListener interface /// 93 @Override notifyEnabled(CBK type)94 public boolean notifyEnabled(CBK type) { 95 switch (type) { 96 case ANIMFRAME: 97 case LOADSTRUCT: 98 case STRUCTUREMODIFIED: 99 case MEASURE: 100 case SERVICE: 101 case PICK: 102 case SCRIPT: 103 case SYNC: 104 // enabled only for SYNC 105 case ECHO: 106 case ERROR: 107 case MESSAGE: 108 case MINIMIZATION: 109 case MODELKIT: 110 case DRAGDROP: 111 case RESIZE: 112 case CLICK: 113 case ATOMMOVED: 114 case HOVER: 115 return true; 116 case APPLETREADY: 117 case AUDIO: 118 case EVAL: 119 case IMAGE: 120 // applet only (but you could change this for your listener) 121 break; 122 } 123 return false; 124 } 125 126 private Map<String, Object> nboOptions; 127 128 @SuppressWarnings("unchecked") 129 @Override notifyCallback(CBK type, Object[] data)130 public void notifyCallback(CBK type, Object[] data) { 131 if (vwr == null) { 132 // during initialization 133 return; 134 } 135 if (jmolPanel.isServer() && data != null && "SYNC".equals(data[0])) { 136 data[0] = type.toString(); 137 jmolPanel.sendNioSyncRequest(data, JmolPanel.OUTSOCKET, null); 138 } 139 if (!jmolPanel.plugins.isEmpty()) 140 for (JmolPlugin p : jmolPanel.plugins.values()) 141 p.notifyCallback(type, data); 142 String strInfo = (data == null || data[1] == null ? null 143 : data[1].toString()); 144 Map<String, Object> info; 145 switch (type) { 146 case MESSAGE: 147 // deprecated 148 return; 149 case SERVICE: 150 if (display == null) 151 return; 152 info = (Map<String, Object>) data[1]; 153 try { 154 String service = (String) info.get("service"); 155 if ("nbo".equals(service)) { 156 if ("showPanel".equals(info.get("action"))) 157 jmolPanel.startNBO(info); 158 //else 159 //jmol.getNBOService().processRequest(info, 0); 160 } 161 } catch (Exception e) { 162 // ignore 163 } 164 return; 165 case LOADSTRUCT: 166 notifyFileLoaded(strInfo, (String) data[2], (String) data[3], 167 (String) data[4], (Boolean) data[8]); 168 if (jmolPanel.gaussianDialog != null) 169 jmolPanel.gaussianDialog.updateModel(-2); 170 break; 171 case ANIMFRAME: 172 int[] iData = (int[]) data[1]; 173 strInfo = PT.toJSON(null, iData); 174 int modelIndex = iData[0]; 175 if (modelIndex <= -2) 176 modelIndex = -2 - modelIndex; // animation is running 177 //int file = iData[1]; 178 //int model = iData[2]; 179 if (display.haveDisplay) { 180 String menuName = (String) data[2]; 181 if (menuName.equals("0.0: ")) 182 menuName = ""; 183 jmolPanel.setStatus(1, menuName); 184 if (jmolPanel.frame != null) { 185 //Font f = jmol.frame.getFont(); 186 //if (f != null) { 187 //int m = jmol.frame.getFontMetrics(f).stringWidth("M"); 188 //int n = jmol.frame.getWidth() / m; 189 //if (n < menuName.length()) 190 //menuName = menuName.substring(0, n) + "..."; 191 //} 192 jmolPanel.frame.setTitle(menuName); 193 } 194 // if (jSpecViewFrame != null) 195 // setJSpecView("", true); 196 } 197 break; 198 case SCRIPT: 199 int msWalltime = ((Integer) data[3]).intValue(); 200 if (msWalltime == 0) { 201 if (data[2] != null && display.haveDisplay) { 202 jmolPanel.setStatus(1, (String) data[2]); 203 } 204 } 205 break; 206 case MODELKIT: 207 String state = (String) data[1]; 208 if (state.equals("ON")) { 209 if (display.buttonModelkit != null) 210 display.buttonModelkit.setSelected(true); 211 } else { 212 if (display.buttonRotate != null) 213 display.buttonRotate.setSelected(true); 214 } 215 break; 216 case MEASURE: 217 String mystatus = (String) data[3]; 218 if (mystatus.indexOf("Sequence") < 0) { 219 if (mystatus.indexOf("Pending") < 0 && display.haveDisplay) 220 jmolPanel.measurementTable.updateTables(); 221 if (mystatus.indexOf("Picked") >= 0) // picking mode 222 notifyAtomPicked(strInfo); 223 else if (mystatus.indexOf("Completed") < 0) 224 return; 225 } 226 break; 227 case PICK: 228 notifyAtomPicked(strInfo); 229 if (jmolPanel.gaussianDialog != null) 230 jmolPanel.gaussianDialog.updateModel(((Integer) data[2]).intValue()); 231 break; 232 case STRUCTUREMODIFIED: 233 // 0 DONE; 1 in process 234 int mode = ((Integer) data[1]).intValue(); 235 int atomIndex = ((Integer) data[2]).intValue(); 236 int modelIndexx = ((Integer) data[3]).intValue(); 237 notifyStructureModified(atomIndex, modelIndexx, mode); 238 if (jmolPanel.gaussianDialog != null) 239 jmolPanel.gaussianDialog.updateModel(-1); 240 break; 241 case SYNC: 242 //System.out.println("StatusListener sync; " + strInfo); 243 String lc = (strInfo == null ? "" : strInfo.toLowerCase()); 244 if (lc.startsWith("jspecview")) { 245 setJSpecView(strInfo.substring(9).trim(), false, false); 246 return; 247 } 248 if (lc.equals("getpreference")) { 249 data[0] = (data[2] == null ? jmolPanel.preferencesDialog 250 : jmolPanel.getPreference(data[2].toString())); 251 return; 252 } 253 if (strInfo != null && strInfo.toLowerCase().startsWith("nbo:")) { 254 if (nboOptions == null) 255 nboOptions= new Hashtable<String, Object>(); 256 nboOptions.put("options", strInfo); 257 jmolPanel.startNBO(nboOptions); 258 return; 259 } 260 jmolPanel.sendNioSyncRequest(null, ((Integer) data[3]).intValue(), 261 strInfo); 262 return; 263 case AUDIO: 264 case IMAGE: 265 case EVAL: 266 case APPLETREADY: 267 // see above -- not implemented in Jmol.jar 268 return; 269 // passed on to listener 270 case HOVER: 271 case ATOMMOVED: 272 case DRAGDROP: 273 case RESIZE: 274 case CLICK: 275 case ERROR: 276 case ECHO: 277 case MINIMIZATION: 278 break; 279 } 280 if (jmolPanel.isServer()) 281 jmolPanel.sendNioSyncRequest(null, JmolPanel.OUTSOCKET, 282 (type + ":" + strInfo).trim()); 283 JmolCallbackListener appConsole = (JmolCallbackListener) vwr 284 .getProperty("DATA_API", "getAppConsole", null); 285 if (appConsole != null) 286 appConsole.notifyCallback(type, data); 287 } 288 289 /** 290 * @param atomIndex 291 * @param modelIndex 292 * @param mode 293 */ notifyStructureModified(int atomIndex, int modelIndex, int mode)294 private void notifyStructureModified(int atomIndex, int modelIndex, int mode) { 295 modificationMode = mode; 296 if (mode < 0) { 297 switch (mode) { 298 case -1: // assign atom 299 case -2: // assign bond 300 case -3: // connect atoms 301 case -4: // delete atoms 302 case -5: // delete models 303 checkJSpecView(false); 304 return; 305 } 306 } 307 } 308 309 @Override setCallbackFunction(String callbackType, String callbackFunction)310 public void setCallbackFunction(String callbackType, String callbackFunction) { 311 //if (callbackType.equalsIgnoreCase("menu")) { 312 //jmol.setupNewFrame(vi/ewer); 313 //return; 314 //} 315 if (callbackType.equalsIgnoreCase("language")) { 316 JmolResourceHandler.clear(); 317 Dialog.setupUIManager(); 318 if (jmolPanel.webExport != null) { 319 WebExport.saveHistory(); 320 WebExport.dispose(); 321 jmolPanel.createWebExport(); 322 } 323 AppConsole appConsole = (AppConsole) vwr.getProperty("DATA_API", 324 "getAppConsole", null); 325 if (appConsole != null) 326 appConsole.sendConsoleEcho(null); 327 jmolPanel.updateLabels(); 328 return; 329 } 330 } 331 332 // / end of JmolCallbackListener interface /// 333 334 @Override eval(String strEval)335 public String eval(String strEval) { 336 String msg = "# this funcationality is implemented only for the applet.\n" + strEval; 337 sendConsoleMessage(msg); 338 return msg; 339 } 340 341 /** 342 * 343 * @param fileName 344 * @param type 345 * @param text_or_bytes 346 * @param quality 347 * @return null ("you do it" or canceled) or a message starting with OK or an 348 * error message 349 */ 350 @Override createImage(String fileName, String type, Object text_or_bytes, int quality)351 public String createImage(String fileName, String type, Object text_or_bytes, 352 int quality) { 353 return null; 354 } 355 notifyAtomPicked(String info)356 private void notifyAtomPicked(String info) { 357 if (display.haveDisplay) 358 jmolPanel.setStatus(1, info); 359 } 360 notifyFileLoaded(String fullPathName, String fileName, String modelName, String errorMsg, Boolean isAsync)361 private void notifyFileLoaded(String fullPathName, String fileName, 362 String modelName, String errorMsg, Boolean isAsync) { 363 if (errorMsg != null) { 364 return; 365 } 366 if (!display.haveDisplay) 367 return; 368 //System.out.println("StatusListener notifyFileLoaded: " + fileName); 369 // this code presumes only ptLoad = -1 (error), 0 (zap), or 3 (completed) 370 String title = "Jmol"; 371 if (fileName != null && fileName.startsWith("DROP_")) 372 fileName = fileName.substring(5); 373 if (modelName != null && fileName != null) 374 title = (fileName.contains("&") ? "" : fileName + " - ") + modelName; 375 else if (fileName != null) 376 title = fileName; 377 else if (modelName != null) 378 title = modelName; 379 jmolPanel.notifyFileOpen(fullPathName == null ? null : fullPathName + (isAsync == Boolean.TRUE ? " (*)" : ""), title); 380 checkJSpecView(fullPathName == null); 381 } 382 383 private int modificationMode; 384 sendConsoleMessage(String strStatus)385 private void sendConsoleMessage(String strStatus) { 386 JmolAppConsoleInterface appConsole = (JmolAppConsoleInterface) vwr 387 .getProperty("DATA_API", "getAppConsole", null); 388 if (appConsole != null) 389 appConsole.sendConsoleMessage(strStatus); 390 } 391 392 @Override showUrl(String url)393 public void showUrl(String url) { 394 try { 395 Class<?> c = Class.forName("java.awt.Desktop"); 396 Method getDesktop = c.getMethod("getDesktop", new Class[] {}); 397 Object deskTop = getDesktop.invoke(null, new Object[] {}); 398 Method browse = c.getMethod("browse", new Class[] { URI.class }); 399 Object arguments[] = { new URI(url) }; 400 browse.invoke(deskTop, arguments); 401 } catch (Exception e) { 402 Logger.error(e.getMessage()); 403 JmolAppConsoleInterface appConsole = (JmolAppConsoleInterface) vwr 404 .getProperty("DATA_API", "getAppConsole", null); 405 if (appConsole != null) { 406 appConsole 407 .sendConsoleMessage("Java 6 Desktop.browse() capability unavailable. Could not open " 408 + url); 409 } else { 410 Logger 411 .error("Java 6 Desktop.browse() capability unavailable. Could not open " 412 + url); 413 } 414 } 415 } 416 417 /** 418 * this is just a test method for isosurface FUNCTIONXY 419 * 420 * @param functionName 421 * @param nX 422 * @param nY 423 * @return f(x,y) as a 2D array 424 * 425 */ 426 @Override functionXY(String functionName, int nX, int nY)427 public float[][] functionXY(String functionName, int nX, int nY) { 428 nX = Math.abs(nX); 429 nY = Math.abs(nY); 430 float[][] f = new float[nX][nY]; 431 // boolean isSecond = (functionName.indexOf("2") >= 0); 432 for (int i = nX; --i >= 0;) 433 for (int j = nY; --j >= 0;) { 434 float x = i / 5f; // / 15f - 1; 435 float y = j / 5f; // / 15f - 1; 436 f[i][j] = /* (float) Math.sqrt */(x * x + y); 437 if (Float.isNaN(f[i][j])) 438 f[i][j] = -(float) Math.sqrt(-x * x - y); 439 // f[i][j] = (isSecond ? (float) ((i + j - nX) / (2f)) : (float) Math 440 // .sqrt(Math.abs(i * i + j * j)) / 2f); 441 // if (i < 10 && j < 10) 442 //System.out.println(" functionXY " + i + " " + j + " " + f[i][j]); 443 } 444 445 return f; // for user-defined isosurface functions (testing only -- bob 446 // hanson) 447 } 448 449 @Override functionXYZ(String functionName, int nX, int nY, int nZ)450 public float[][][] functionXYZ(String functionName, int nX, int nY, int nZ) { 451 nX = Math.abs(nX); 452 nY = Math.abs(nY); 453 nZ = Math.abs(nZ); 454 float[][][] f = new float[nX][nY][nZ]; 455 for (int i = nX; --i >= 0;) 456 for (int j = nY; --j >= 0;) 457 for (int k = nZ; --k >= 0;) { 458 float x = i / ((nX - 1) / 2f) - 1; 459 float y = j / ((nY - 1) / 2f) - 1; 460 float z = k / ((nZ - 1) / 2f) - 1; 461 f[i][j][k] = x * x + y * y - z * z;//(float) x * x + y - z * z; 462 // if (i == 22 || i == 23) 463 //System.out.println(" functionXYZ " + i + " " + j + " " + k + " " + 464 // f[i][j][k]); 465 } 466 return f; // for user-defined isosurface functions (testing only -- bob 467 // hanson) 468 } 469 470 @Override getRegistryInfo()471 public Map<String, Object> getRegistryInfo() { 472 return null; 473 } 474 475 @Override resizeInnerPanel(String data)476 public int[] resizeInnerPanel(String data) { 477 return jmolPanel.resizeInnerPanel(data); 478 } 479 480 private String lastSimulate; 481 checkJSpecView(boolean closeAll)482 private void checkJSpecView(boolean closeAll) { 483 if (jSpecViewFrame != null && modificationMode <= 0) { 484 jSpecViewForceNew = jSpecViewFrame.isVisible(); 485 setJSpecView(closeAll ? "none" : "", true, true); 486 jSpecViewForceNew = true; 487 } 488 } 489 setJSpecView(String peaks, boolean doLoadCheck, boolean isFileLoad)490 public void setJSpecView(String peaks, boolean doLoadCheck, boolean isFileLoad) { 491 if (peaks.startsWith(":")) 492 peaks = peaks.substring(1); 493 if (peaks.equals("none") || peaks.equals("NONESimulate:")) { 494 if (jSpecViewFrame != null) { 495 jSpecViewFrame.syncScript("close ALL"); 496 jSpecViewFrame.awaken(false); 497 } 498 return; 499 } 500 boolean isC13 = peaks.equals("C13Simulate:"); 501 boolean isSimulation = (peaks.equals("H1Simulate:") || isC13); 502 boolean isStartup = (peaks.length() == 0 || isSimulation); 503 boolean newSim = (isSimulation && !peaks.equals(lastSimulate)); 504 String data = null; 505 if (isSimulation) { 506 data = vwr.extractMolData(null); 507 if (data == null || data.length() == 0) 508 return; 509 } 510 if (jSpecViewFrame == null) { 511 jSpecViewFrame = new MainFrame(vwr.getBoolean(T.jmolinjspecview) ? (Component) vwr.display : null, this); 512 jSpecViewFrame.setSize(Math.max(1000, jmolPanel.frame.getWidth() + 50), 600); 513 jSpecViewFrame.setLocation(jmolPanel.frame.getLocation().x + 10, jmolPanel.frame 514 .getLocation().y + 100); 515 jSpecViewFrame.register("Jmol", this); 516 vwr.setBooleanProperty("_jspecview", true); 517 if (isStartup) { 518 doLoadCheck = true; 519 } 520 } 521 if (doLoadCheck || jSpecViewForceNew || newSim) { 522 String type = "" + vwr.getP("_modelType"); 523 if (type.equalsIgnoreCase("jcampdx")) { 524 jSpecViewForceNew = false; 525 String file = "" + vwr.getP("_modelFile"); 526 if (file.indexOf("/") < 0) 527 return; 528 peaks = "hidden true; load CHECK " + PT.esc(file) + ";hidden false" + (newSim && isC13 ? ";scaleby 0.5" : null); 529 } else if (isFileLoad && !jSpecViewForceNew && !newSim) { 530 return; 531 } else { 532 jSpecViewForceNew = false; 533 if (newSim) 534 lastSimulate = peaks; 535 String model = "" + vwr.getP("_modelNumber"); 536 if (data == null) { 537 peaks = "hidden false"; 538 } else { 539 data = PT.replaceAllCharacters(data, "&", "_"); 540 peaks = "hidden true; load CHECK " + (peaks.equals("H1Simulate:") ? "H1 " : "C13 ") 541 + PT.esc("id='~" + model + "';" + data) + ";hidden false #SYNC_PEAKS"; 542 } 543 isStartup = false; 544 } 545 } 546 547 if (!jSpecViewFrame.isVisible()) { 548 if (peaks.contains("<PeakData")) 549 return; 550 jSpecViewFrame.awaken(true); 551 display.setViewer(vwr); 552 } 553 if (isStartup) 554 peaks = "HIDDEN false"; 555 jSpecViewFrame.syncScript(peaks); 556 } 557 558 @Override register(String id, JmolSyncInterface jsi)559 public void register(String id, JmolSyncInterface jsi) { 560 // this would be a call from JSpecView requesting that Jmol 561 // register the JSpecView applet in the JmolAppletRegistry. 562 } 563 564 @Override syncScript(String script)565 public void syncScript(String script) { 566 // called from JSpecView to send "Select: <Peaks...." script 567 jmolPanel.syncScript(script); 568 } 569 570 571 // -- JSVInterface -- 572 573 private static String propertiesFileName = "jspecview.properties"; 574 575 @Override setProperties(Properties properties)576 public void setProperties(Properties properties) { 577 try { 578 FileInputStream fileIn = new FileInputStream(propertiesFileName); 579 properties.load(fileIn); 580 } catch (Exception e) { 581 } 582 } 583 584 @Override saveProperties(Properties properties)585 public void saveProperties(Properties properties) { 586 // Write out current properties 587 try { 588 FileOutputStream fileOut = new FileOutputStream(propertiesFileName); 589 properties.store(fileOut, "JSpecView Application Properties"); 590 } catch (Exception e) { 591 } 592 } 593 594 /** 595 * @param withDialog 596 * @param frame 597 */ 598 @Override exitJSpecView(boolean withDialog, Object frame)599 public void exitJSpecView(boolean withDialog, Object frame) { 600 // no exit from Jmol 601 } 602 603 /** 604 * no queuing here -- called by MainFrame 605 * 606 * @param script 607 */ 608 @Override runScript(String script)609 public void runScript(String script) { 610 jSpecViewFrame.runScriptNow(script); 611 612 } 613 614 /** 615 * @param msg 616 */ 617 @Override syncToJmol(String msg)618 public void syncToJmol(String msg) { 619 // not utilized in Jmol application -- jmolSyncInterface used instead 620 } 621 622 @Override getJSpecViewProperty(String type)623 public Map<String, Object> getJSpecViewProperty(String type) { 624 if (type.toLowerCase().startsWith("jspecview")) { 625 type = type.substring(9); 626 if (type.startsWith(":")) 627 type = type.substring(1); 628 return (jSpecViewFrame == null ? null : jSpecViewFrame.getJSpecViewProperty(type)); 629 } 630 return null; 631 } 632 633 } 634