1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2007-04-26 16:57:51 -0500 (Thu, 26 Apr 2007) $ 4 * $Revision: 7502 $ 5 * 6 * Copyright (C) 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 25 package org.jmol.console; 26 27 import java.awt.event.KeyEvent; 28 import java.util.Hashtable; 29 import java.util.Map; 30 31 import javajs.util.PT; 32 33 import org.jmol.api.JmolAbstractButton; 34 import org.jmol.api.JmolAppConsoleInterface; 35 import org.jmol.api.JmolCallbackListener; 36 import org.jmol.api.JmolScriptEditorInterface; 37 import org.jmol.c.CBK; 38 import org.jmol.i18n.GT; 39 import org.jmol.script.T; 40 import org.jmol.viewer.JC; 41 import org.jmol.viewer.Viewer; 42 43 public abstract class GenericConsole implements JmolAppConsoleInterface, JmolCallbackListener { 44 45 protected GenericTextArea input; 46 protected GenericTextArea output; 47 48 public Viewer vwr; 49 setViewer(Viewer vwr)50 protected void setViewer(Viewer vwr) { 51 this.vwr = vwr; 52 if (labels == null) { 53 Map<String, String> l = new Hashtable<String, String>(); 54 l.put("title", GT.$("Jmol Script Console") + " " + Viewer.getJmolVersion()); 55 setupLabels(l); 56 labels = l; 57 } 58 59 } 60 61 protected static Map<String, String> labels; 62 protected Map<String, Object> menuMap = new Hashtable<String, Object>(); 63 protected JmolAbstractButton editButton, runButton, historyButton, stateButton; 64 protected JmolAbstractButton clearOutButton, clearInButton, loadButton; 65 isMenuItem(Object source)66 abstract protected boolean isMenuItem(Object source); layoutWindow(String enabledButtons)67 abstract protected void layoutWindow(String enabledButtons); setTitle()68 abstract protected void setTitle(); 69 @Override setVisible(boolean visible)70 abstract public void setVisible(boolean visible); 71 @Override getScriptEditor()72 abstract public JmolScriptEditorInterface getScriptEditor(); 73 @Override dispose()74 abstract public void dispose(); 75 setButton(String text)76 abstract protected JmolAbstractButton setButton(String text); 77 addButton(JmolAbstractButton b, String label)78 protected JmolAbstractButton addButton(JmolAbstractButton b, String label) { 79 b.addConsoleListener(this); 80 menuMap.put(label, b); 81 return b; 82 } 83 getLabel1()84 protected JmolAbstractButton getLabel1() { 85 return null; 86 } 87 setupLabels(Map<String, String> labels)88 protected void setupLabels(Map<String, String> labels) { 89 // these three are for ImageDialog 90 labels.put("saveas", GT.$("&Save As...")); 91 labels.put("file", GT.$("&File")); 92 labels.put("close", GT.$("&Close")); 93 setupLabels0(labels); 94 } 95 setupLabels0(Map<String, String> labels)96 protected void setupLabels0(Map<String, String> labels) { 97 labels.put("help", GT.$("&Help")); 98 labels.put("search", GT.$("&Search...")); 99 labels.put("commands", GT.$("&Commands")); 100 labels.put("functions", GT.$("Math &Functions")); 101 labels.put("parameters", GT.$("Set &Parameters")); 102 labels.put("more", GT.$("&More")); 103 labels.put("Editor", GT.$("Editor")); 104 labels.put("State", GT.$("State")); 105 labels.put("Run", GT.$("Run")); 106 labels.put("Clear Output", GT.$("Clear Output")); 107 labels.put("Clear Input", GT.$("Clear Input")); 108 labels.put("History", GT.$("History")); 109 labels.put("Load", GT.$("Load")); 110 labels.put("label1", GT 111 .$("press CTRL-ENTER for new line or paste model data and press Load")); 112 labels.put("default", 113 GT.$("Messages will appear here. Enter commands in the box below. Click the console Help menu item for on-line help, which will appear in a new browser window.")); 114 } 115 setLabels()116 protected void setLabels() { 117 boolean doTranslate = GT.setDoTranslate(true); 118 editButton = setButton("Editor"); 119 stateButton = setButton("State"); 120 runButton = setButton("Run"); 121 clearOutButton = setButton("Clear Output"); 122 clearInButton = setButton("Clear Input"); 123 historyButton = setButton("History"); 124 loadButton = setButton("Load"); 125 defaultMessage = getLabel("default"); 126 setTitle(); 127 GT.setDoTranslate(doTranslate); 128 } 129 getLabel(String key)130 public static String getLabel(String key) { 131 return labels.get(key); 132 } 133 displayConsole()134 protected void displayConsole() { 135 layoutWindow(null); 136 outputMsg(defaultMessage); 137 } 138 139 protected String defaultMessage; 140 protected JmolAbstractButton label1; 141 updateLabels()142 protected void updateLabels() { 143 return; 144 } 145 nextFileName(String stub, int nTab)146 abstract protected String nextFileName(String stub, int nTab); 147 public int nTab = 0; 148 private String incompleteCmd; 149 completeCommand(String thisCmd)150 public String completeCommand(String thisCmd) { 151 if (thisCmd.length() == 0) 152 return null; 153 String strCommand = (nTab <= 0 || incompleteCmd == null ? thisCmd 154 : incompleteCmd); 155 incompleteCmd = strCommand; 156 String[] splitCmd = GenericConsole.splitCommandLine(thisCmd); 157 if (splitCmd == null) 158 return null; 159 boolean asCommand = splitCmd[2] == null; 160 boolean inBrace = (splitCmd[3] != null); 161 String notThis = splitCmd[asCommand ? 1 : 2]; 162 String s = splitCmd[1]; 163 if (notThis.length() == 0) 164 return null; 165 T token = T.getTokenFromName(s.trim().toLowerCase()); 166 int cmdtok = (token == null ? 0 : token.tok); 167 boolean isSelect = T.tokAttr(cmdtok, T.atomExpressionCommand); 168 splitCmd = GenericConsole.splitCommandLine(strCommand); 169 String cmd = null; 170 if (!asCommand && (notThis.charAt(0) == '"' || notThis.charAt(0) == '\'')) { 171 char q = notThis.charAt(0); 172 notThis = PT.trim(notThis, "\"\'"); 173 String stub = PT.trim(splitCmd[2], "\"\'"); 174 cmd = nextFileName(stub, nTab); 175 if (cmd != null) 176 cmd = splitCmd[0] + splitCmd[1] + q + cmd + q; 177 } else { 178 Map<String, Object> map = null; 179 if (!asCommand) { 180 notThis = s; 181 if (inBrace || splitCmd[2].startsWith("$") 182 //|| T.isIDcmd(cmdtok) 183 || isSelect) { 184 map = new Hashtable<String, Object>(); 185 vwr.getObjectMap(map, inBrace || isSelect ? '{' : splitCmd[2].startsWith("$") ? '$' : '0'); 186 } 187 } 188 cmd = T.completeCommand(map, s.equalsIgnoreCase("set "), asCommand, asCommand ? splitCmd[1] 189 : splitCmd[2], nTab); 190 cmd = splitCmd[0] 191 + (cmd == null ? notThis : asCommand ? cmd : splitCmd[1] + cmd); 192 } 193 return (cmd == null || cmd.equals(strCommand) ? null : cmd); 194 } 195 doAction(Object source)196 protected void doAction(Object source) { 197 if (source == runButton) { 198 execute(null); 199 } else if (source == editButton) { 200 vwr.getProperty("DATA_API","scriptEditor", null); 201 } else if (source == historyButton) { 202 clearContent(vwr.getSetHistory(Integer.MAX_VALUE)); 203 } else if (source == stateButton) { 204 clearContent(vwr.getStateInfo()); 205 // problem here is that in some browsers, you cannot clip from 206 // the editor. 207 //vwr.getProperty("DATA_API","scriptEditor", new String[] { "current state" , vwr.getStateInfo() }); 208 } else 209 if (source == clearInButton) { 210 input.setText(""); 211 return; 212 } 213 if (source == clearOutButton) { 214 output.setText(""); 215 return; 216 } 217 if (source == loadButton) { 218 vwr.loadInlineAppend(input.getText(), false); 219 return; 220 } 221 if (isMenuItem(source)) { 222 execute(((JmolAbstractButton) source).getName()); 223 return; 224 } 225 } 226 execute(String strCommand)227 protected void execute(String strCommand) { 228 String cmd = (strCommand == null ? input.getText() : strCommand); 229 if (strCommand == null) 230 input.setText(null); 231 String strErrorMessage = vwr.script(cmd + JC.SCRIPT_EDITOR_IGNORE); 232 if (strErrorMessage != null && !strErrorMessage.equals("pending")) 233 outputMsg(strErrorMessage); 234 } 235 destroyConsole()236 protected void destroyConsole() { 237 // if the vwr is an applet, when we close the console 238 // we 239 if (vwr.isApplet) 240 vwr.getProperty("DATA_API", "getAppConsole", Boolean.FALSE); 241 } 242 setAbstractButtonLabels(Map<String, Object> menuMap, Map<String, String> labels)243 public static void setAbstractButtonLabels(Map<String, Object> menuMap, 244 Map<String, String> labels) { 245 for (String key: menuMap.keySet()) { 246 JmolAbstractButton m = (JmolAbstractButton) menuMap.get(key); 247 String label = labels.get(key); 248 if (key.indexOf("Tip") == key.length() - 3) { 249 m.setToolTipText(labels.get(key)); 250 } else { 251 char mnemonic = getMnemonic(label); 252 if (mnemonic != ' ') 253 m.setMnemonic(mnemonic); 254 label = getLabelWithoutMnemonic(label); 255 m.setText(label); 256 } 257 } 258 } 259 getLabelWithoutMnemonic(String label)260 public static String getLabelWithoutMnemonic(String label) { 261 if (label == null) { 262 return null; 263 } 264 int index = label.indexOf('&'); 265 if (index == -1) { 266 return label; 267 } 268 return label.substring(0, index) + 269 ((index < label.length() - 1) ? label.substring(index + 1) : ""); 270 } 271 getMnemonic(String label)272 static char getMnemonic(String label) { 273 if (label == null) { 274 return ' '; 275 } 276 int index = label.indexOf('&'); 277 if ((index == -1) || (index == label.length() - 1)){ 278 return ' '; 279 } 280 return label.charAt(index + 1); 281 } 282 map(Object button, String key, String label, Map<String, Object> menuMap)283 public static void map(Object button, String key, String label, 284 Map<String, Object> menuMap) { 285 char mnemonic = getMnemonic(label); 286 if (mnemonic != ' ') 287 ((JmolAbstractButton) button).setMnemonic(mnemonic); 288 if (menuMap != null) { 289 if (key.indexOf("NMR.")>=0)System.out.println("genericconsole mapping " + key + " to " + label); 290 menuMap.put(key, button); 291 } 292 } 293 294 ///////////// JmolCallbackListener interface 295 296 // Allowing for just the callbacks needed to provide status feedback to the console. 297 // For applications that embed Jmol, see the example application Integration.java. 298 299 @Override notifyEnabled(CBK type)300 public boolean notifyEnabled(CBK type) { 301 // See org.jmol.viewer.JmolConstants.java for a complete list 302 switch (type) { 303 case ECHO: 304 case MEASURE: 305 case MESSAGE: 306 case PICK: 307 return true; 308 case ANIMFRAME: 309 case APPLETREADY: 310 case ATOMMOVED: 311 case CLICK: 312 case DRAGDROP: 313 case ERROR: 314 case EVAL: 315 case HOVER: 316 case IMAGE: 317 case LOADSTRUCT: 318 case MINIMIZATION: 319 case SERVICE: 320 case RESIZE: 321 case SCRIPT: 322 case SYNC: 323 case STRUCTUREMODIFIED: 324 break; 325 } 326 return false; 327 } 328 329 @Override 330 @SuppressWarnings("incomplete-switch") notifyCallback(CBK type, Object[] data)331 public void notifyCallback(CBK type, Object[] data) { 332 String strInfo = (data == null || data[1] == null ? null : data[1] 333 .toString()); 334 switch (type) { 335 case ECHO: 336 sendConsoleEcho(strInfo); 337 break; 338 case MEASURE: 339 String mystatus = (String) data[3]; 340 if (mystatus.indexOf("Picked") >= 0 || mystatus.indexOf("Sequence") >= 0) // picking mode 341 sendConsoleMessage(strInfo); 342 else if (mystatus.indexOf("Completed") >= 0) 343 sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2, 344 strInfo.length() - 1)); 345 break; 346 case MESSAGE: 347 sendConsoleMessage(data == null ? null : strInfo); 348 break; 349 case PICK: 350 sendConsoleMessage(strInfo); 351 break; 352 } 353 } 354 355 @Override getText()356 public String getText() { 357 return output.getText(); 358 } 359 360 @Override sendConsoleEcho(String strEcho)361 public void sendConsoleEcho(String strEcho) { 362 if (strEcho == null) { 363 // null here means new language 364 updateLabels(); 365 outputMsg(null); 366 strEcho = defaultMessage; 367 } else if (strEcho.equals("\0")) { 368 /** 369 * @j2sNative 370 * 371 * Clazz.Console.clear(); 372 */ 373 {} 374 strEcho = null; 375 } 376 outputMsg(strEcho); 377 } 378 outputMsg(String message)379 private void outputMsg(String message) { 380 int n = (message == null ? -1 : message.length()); 381 switch (n) { 382 case -1: 383 output.setText(""); 384 return; 385 default: 386 if (message.charAt(n - 1) == '\n') 387 break; 388 //$FALL-THROUGH$ 389 case 0: 390 message += "\n"; 391 } 392 output.append(message); 393 } 394 clearContent(String text)395 protected void clearContent(String text) { 396 output.setText(text); 397 } 398 399 @Override sendConsoleMessage(String strInfo)400 public void sendConsoleMessage(String strInfo) { 401 // null here indicates "clear console" 402 if (strInfo != null && output.getText().startsWith(defaultMessage)) 403 outputMsg(null); 404 outputMsg(strInfo); 405 } 406 407 @Override setCallbackFunction(String callbackType, String callbackFunction)408 public void setCallbackFunction(String callbackType, String callbackFunction) { 409 // application-dependent option 410 } 411 412 @Override zap()413 public void zap() { 414 } 415 416 // key listener actions 417 recallCommand(boolean up)418 protected void recallCommand(boolean up) { 419 String cmd = vwr.getSetHistory(up ? -1 : 1); 420 if (cmd != null) 421 input.setText(PT.escUnicode(cmd)); 422 } 423 424 /** 425 * 426 * @param kcode 427 * @param kid 428 * @param isControlDown 429 * @return 1 = consume; 2 = super.process; 3 = both 430 */ processKey(int kcode, int kid, boolean isControlDown)431 protected int processKey(int kcode, int kid, boolean isControlDown) { 432 int mode = 0; 433 switch (kid) { 434 case KeyEvent.KEY_PRESSED: 435 switch (kcode) { 436 case KeyEvent.VK_TAB: 437 String s = input.getText(); 438 if (s.endsWith("\n") || s.endsWith("\t")) 439 return 0; 440 mode = 1; 441 if (input.getCaretPosition() == s.length()) { 442 String cmd = completeCommand(s); 443 if (cmd != null) 444 input.setText(PT.escUnicode(cmd).replace('\t',' ')); 445 nTab++; 446 return mode; 447 } 448 break; 449 case KeyEvent.VK_ESCAPE: 450 mode = 1; 451 input.setText(""); 452 break; 453 } 454 nTab = 0; 455 if (kcode == KeyEvent.VK_ENTER && !isControlDown) { 456 execute(null); 457 return mode; 458 } 459 if (kcode == KeyEvent.VK_UP || kcode == KeyEvent.VK_DOWN) { 460 recallCommand(kcode == KeyEvent.VK_UP); 461 return mode; 462 } 463 break; 464 case KeyEvent.KEY_RELEASED: 465 if (kcode == KeyEvent.VK_ENTER && !isControlDown) 466 return mode; 467 break; 468 } 469 return mode | 2; 470 } 471 472 /** 473 * separate a command line into three sections: 474 * 475 * prefix....;cmd ........ token 476 * 477 * where token can be a just-finished single or double quote or 478 * a string of characters 479 * 480 * @param cmd 481 * @return String[] {prefix, cmd..... token} 482 */ splitCommandLine(String cmd)483 private static String[] splitCommandLine(String cmd) { 484 String[] sout = new String[4]; 485 boolean isEscaped1 = false; 486 boolean isEscaped2 = false; 487 boolean isEscaped = false; 488 if (cmd.length() == 0) 489 return null; 490 int ptQ = -1; 491 int ptCmd = 0; 492 int ptToken = 0; 493 int nBrace = 0; 494 char ch; 495 for (int i = 0; i < cmd.length(); i++) { 496 switch(ch = cmd.charAt(i)) { 497 case '"': 498 if (!isEscaped && !isEscaped1) { 499 isEscaped2 = !isEscaped2; 500 if (isEscaped2) 501 ptQ = ptToken = i; 502 } 503 break; 504 case '\'': 505 if (!isEscaped && !isEscaped2) { 506 isEscaped1 = !isEscaped1; 507 if (isEscaped1) 508 ptQ = ptToken = i; 509 } 510 break; 511 case '\\': 512 isEscaped = !isEscaped; 513 continue; 514 case ' ': 515 if (!isEscaped && !isEscaped1 && !isEscaped2) { 516 ptToken = i + 1; 517 ptQ = -1; 518 } 519 break; 520 case ';': 521 if (!isEscaped1 && !isEscaped2) { 522 ptCmd = ptToken = i + 1; 523 ptQ = -1; 524 nBrace = 0; 525 } 526 break; 527 case '{': 528 case '}': 529 if (!isEscaped1 && !isEscaped2) { 530 nBrace += (ch == '{' ? 1 : -1); 531 ptToken = i + 1; 532 ptQ = -1; 533 } 534 break; 535 default: 536 if (!isEscaped1 && !isEscaped2) 537 ptQ = -1; 538 } 539 isEscaped = false; 540 } 541 sout[0] = cmd.substring(0, ptCmd); 542 sout[1] = (ptToken == ptCmd ? cmd.substring(ptCmd) : cmd.substring(ptCmd, (ptToken > ptQ ? ptToken : ptQ))); 543 sout[2] = (ptToken == ptCmd ? null : cmd.substring(ptToken)); 544 sout[3] = (nBrace > 0 ? "{" : null); 545 return sout; 546 } 547 548 549 } 550