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