0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino JavaScript Debugger code, released
0017: * November 21, 2000.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * SeeBeyond Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 2000
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Igor Bukanov
0026: * Matt Gould
0027: * Cameron McCormack
0028: * Christopher Oliver
0029: * Hannes Wallnoefer
0030: *
0031: * Alternatively, the contents of this file may be used under the terms of
0032: * the GNU General Public License Version 2 or later (the "GPL"), in which
0033: * case the provisions of the GPL are applicable instead of those above. If
0034: * you wish to allow use of your version of this file only under the terms of
0035: * the GPL and not to allow others to use your version of this file under the
0036: * MPL, indicate your decision by deleting the provisions above and replacing
0037: * them with the notice and other provisions required by the GPL. If you do
0038: * not delete the provisions above, a recipient may use your version of this
0039: * file under either the MPL or the GPL.
0040: *
0041: * ***** END LICENSE BLOCK ***** */
0042: package org.mozilla.javascript.tools.debugger;
0043:
0044: import javax.swing.*;
0045: import javax.swing.text.*;
0046: import javax.swing.event.*;
0047: import javax.swing.table.*;
0048: import java.awt.*;
0049: import java.awt.event.*;
0050:
0051: import java.util.*;
0052: import java.io.*;
0053: import javax.swing.tree.DefaultTreeCellRenderer;
0054: import javax.swing.tree.TreePath;
0055: import java.lang.reflect.Method;
0056:
0057: import org.mozilla.javascript.Kit;
0058: import org.mozilla.javascript.SecurityUtilities;
0059:
0060: import org.mozilla.javascript.tools.shell.ConsoleTextArea;
0061:
0062: import org.mozilla.javascript.tools.debugger.downloaded.JTreeTable;
0063: import org.mozilla.javascript.tools.debugger.downloaded.TreeTableModel;
0064: import org.mozilla.javascript.tools.debugger.downloaded.TreeTableModelAdapter;
0065:
0066: /**
0067: * GUI for the Rhino debugger.
0068: */
0069: public class SwingGui extends JFrame implements GuiCallback {
0070:
0071: /**
0072: * Serializable magic number.
0073: */
0074: private static final long serialVersionUID = -8217029773456711621L;
0075:
0076: /**
0077: * The debugger.
0078: */
0079: Dim dim;
0080:
0081: /**
0082: * The action to run when the 'Exit' menu item is chosen or the
0083: * frame is closed.
0084: */
0085: private Runnable exitAction;
0086:
0087: /**
0088: * The {@link JDesktopPane} that holds the script windows.
0089: */
0090: private JDesktopPane desk;
0091:
0092: /**
0093: * The {@link JPanel} that shows information about the context.
0094: */
0095: private ContextWindow context;
0096:
0097: /**
0098: * The menu bar.
0099: */
0100: private Menubar menubar;
0101:
0102: /**
0103: * The tool bar.
0104: */
0105: private JToolBar toolBar;
0106:
0107: /**
0108: * The console that displays I/O from the script.
0109: */
0110: private JSInternalConsole console;
0111:
0112: /**
0113: * The {@link JSplitPane} that separates {@link #desk} from
0114: * {@link org.mozilla.javascript.Context}.
0115: */
0116: private JSplitPane split1;
0117:
0118: /**
0119: * The status bar.
0120: */
0121: private JLabel statusBar;
0122:
0123: /**
0124: * Hash table of internal frame names to the internal frames themselves.
0125: */
0126: private Hashtable toplevels = new Hashtable();
0127:
0128: /**
0129: * Hash table of script URLs to their internal frames.
0130: */
0131: private Hashtable fileWindows = new Hashtable();
0132:
0133: /**
0134: * The {@link FileWindow} that last had the focus.
0135: */
0136: private FileWindow currentWindow;
0137:
0138: /**
0139: * File choose dialog for loading a script.
0140: */
0141: JFileChooser dlg;
0142:
0143: /**
0144: * The AWT EventQueue. Used for manually pumping AWT events from
0145: * {@link #dispatchNextGuiEvent()}.
0146: */
0147: private EventQueue awtEventQueue;
0148:
0149: /**
0150: * Creates a new SwingGui.
0151: */
0152: public SwingGui(Dim dim, String title) {
0153: super (title);
0154: this .dim = dim;
0155: init();
0156: dim.setGuiCallback(this );
0157: }
0158:
0159: /**
0160: * Returns the Menubar of this debugger frame.
0161: */
0162: public Menubar getMenubar() {
0163: return menubar;
0164: }
0165:
0166: /**
0167: * Sets the {@link Runnable} that will be run when the "Exit" menu
0168: * item is chosen.
0169: */
0170: public void setExitAction(Runnable r) {
0171: exitAction = r;
0172: }
0173:
0174: /**
0175: * Returns the debugger console component.
0176: */
0177: public JSInternalConsole getConsole() {
0178: return console;
0179: }
0180:
0181: /**
0182: * Sets the visibility of the debugger GUI.
0183: */
0184: public void setVisible(boolean b) {
0185: super .setVisible(b);
0186: if (b) {
0187: // this needs to be done after the window is visible
0188: console.consoleTextArea.requestFocus();
0189: context.split.setDividerLocation(0.5);
0190: try {
0191: console.setMaximum(true);
0192: console.setSelected(true);
0193: console.show();
0194: console.consoleTextArea.requestFocus();
0195: } catch (Exception exc) {
0196: }
0197: }
0198: }
0199:
0200: /**
0201: * Records a new internal frame.
0202: */
0203: void addTopLevel(String key, JFrame frame) {
0204: if (frame != this ) {
0205: toplevels.put(key, frame);
0206: }
0207: }
0208:
0209: /**
0210: * Constructs the debugger GUI.
0211: */
0212: private void init() {
0213: menubar = new Menubar(this );
0214: setJMenuBar(menubar);
0215: toolBar = new JToolBar();
0216: JButton button;
0217: JButton breakButton, goButton, stepIntoButton, stepOverButton, stepOutButton;
0218: String[] toolTips = { "Break (Pause)", "Go (F5)",
0219: "Step Into (F11)", "Step Over (F7)", "Step Out (F8)" };
0220: int count = 0;
0221: button = breakButton = new JButton("Break");
0222: button.setToolTipText("Break");
0223: button.setActionCommand("Break");
0224: button.addActionListener(menubar);
0225: button.setEnabled(true);
0226: button.setToolTipText(toolTips[count++]);
0227:
0228: button = goButton = new JButton("Go");
0229: button.setToolTipText("Go");
0230: button.setActionCommand("Go");
0231: button.addActionListener(menubar);
0232: button.setEnabled(false);
0233: button.setToolTipText(toolTips[count++]);
0234:
0235: button = stepIntoButton = new JButton("Step Into");
0236: button.setToolTipText("Step Into");
0237: button.setActionCommand("Step Into");
0238: button.addActionListener(menubar);
0239: button.setEnabled(false);
0240: button.setToolTipText(toolTips[count++]);
0241:
0242: button = stepOverButton = new JButton("Step Over");
0243: button.setToolTipText("Step Over");
0244: button.setActionCommand("Step Over");
0245: button.setEnabled(false);
0246: button.addActionListener(menubar);
0247: button.setToolTipText(toolTips[count++]);
0248:
0249: button = stepOutButton = new JButton("Step Out");
0250: button.setToolTipText("Step Out");
0251: button.setActionCommand("Step Out");
0252: button.setEnabled(false);
0253: button.addActionListener(menubar);
0254: button.setToolTipText(toolTips[count++]);
0255:
0256: Dimension dim = stepOverButton.getPreferredSize();
0257: breakButton.setPreferredSize(dim);
0258: breakButton.setMinimumSize(dim);
0259: breakButton.setMaximumSize(dim);
0260: breakButton.setSize(dim);
0261: goButton.setPreferredSize(dim);
0262: goButton.setMinimumSize(dim);
0263: goButton.setMaximumSize(dim);
0264: stepIntoButton.setPreferredSize(dim);
0265: stepIntoButton.setMinimumSize(dim);
0266: stepIntoButton.setMaximumSize(dim);
0267: stepOverButton.setPreferredSize(dim);
0268: stepOverButton.setMinimumSize(dim);
0269: stepOverButton.setMaximumSize(dim);
0270: stepOutButton.setPreferredSize(dim);
0271: stepOutButton.setMinimumSize(dim);
0272: stepOutButton.setMaximumSize(dim);
0273: toolBar.add(breakButton);
0274: toolBar.add(goButton);
0275: toolBar.add(stepIntoButton);
0276: toolBar.add(stepOverButton);
0277: toolBar.add(stepOutButton);
0278:
0279: JPanel contentPane = new JPanel();
0280: contentPane.setLayout(new BorderLayout());
0281: getContentPane().add(toolBar, BorderLayout.NORTH);
0282: getContentPane().add(contentPane, BorderLayout.CENTER);
0283: desk = new JDesktopPane();
0284: desk.setPreferredSize(new Dimension(600, 300));
0285: desk.setMinimumSize(new Dimension(150, 50));
0286: desk.add(console = new JSInternalConsole("JavaScript Console"));
0287: context = new ContextWindow(this );
0288: context.setPreferredSize(new Dimension(600, 120));
0289: context.setMinimumSize(new Dimension(50, 50));
0290:
0291: split1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, desk,
0292: context);
0293: split1.setOneTouchExpandable(true);
0294: SwingGui.setResizeWeight(split1, 0.66);
0295: contentPane.add(split1, BorderLayout.CENTER);
0296: statusBar = new JLabel();
0297: statusBar.setText("Thread: ");
0298: contentPane.add(statusBar, BorderLayout.SOUTH);
0299: dlg = new JFileChooser();
0300:
0301: javax.swing.filechooser.FileFilter filter = new javax.swing.filechooser.FileFilter() {
0302: public boolean accept(File f) {
0303: if (f.isDirectory()) {
0304: return true;
0305: }
0306: String n = f.getName();
0307: int i = n.lastIndexOf('.');
0308: if (i > 0 && i < n.length() - 1) {
0309: String ext = n.substring(i + 1).toLowerCase();
0310: if (ext.equals("js")) {
0311: return true;
0312: }
0313: }
0314: return false;
0315: }
0316:
0317: public String getDescription() {
0318: return "JavaScript Files (*.js)";
0319: }
0320: };
0321: dlg.addChoosableFileFilter(filter);
0322: addWindowListener(new WindowAdapter() {
0323: public void windowClosing(WindowEvent e) {
0324: exit();
0325: }
0326: });
0327: }
0328:
0329: /**
0330: * Runs the {@link #exitAction}.
0331: */
0332: private void exit() {
0333: if (exitAction != null) {
0334: SwingUtilities.invokeLater(exitAction);
0335: }
0336: dim.setReturnValue(Dim.EXIT);
0337: }
0338:
0339: /**
0340: * Returns the {@link FileWindow} for the given URL.
0341: */
0342: FileWindow getFileWindow(String url) {
0343: if (url == null || url.equals("<stdin>")) {
0344: return null;
0345: }
0346: return (FileWindow) fileWindows.get(url);
0347: }
0348:
0349: /**
0350: * Returns a short version of the given URL.
0351: */
0352: static String getShortName(String url) {
0353: int lastSlash = url.lastIndexOf('/');
0354: if (lastSlash < 0) {
0355: lastSlash = url.lastIndexOf('\\');
0356: }
0357: String shortName = url;
0358: if (lastSlash >= 0 && lastSlash + 1 < url.length()) {
0359: shortName = url.substring(lastSlash + 1);
0360: }
0361: return shortName;
0362: }
0363:
0364: /**
0365: * Closes the given {@link FileWindow}.
0366: */
0367: void removeWindow(FileWindow w) {
0368: fileWindows.remove(w.getUrl());
0369: JMenu windowMenu = getWindowMenu();
0370: int count = windowMenu.getItemCount();
0371: JMenuItem lastItem = windowMenu.getItem(count - 1);
0372: String name = getShortName(w.getUrl());
0373: for (int i = 5; i < count; i++) {
0374: JMenuItem item = windowMenu.getItem(i);
0375: if (item == null)
0376: continue; // separator
0377: String text = item.getText();
0378: //1 D:\foo.js
0379: //2 D:\bar.js
0380: int pos = text.indexOf(' ');
0381: if (text.substring(pos + 1).equals(name)) {
0382: windowMenu.remove(item);
0383: // Cascade [0]
0384: // Tile [1]
0385: // ------- [2]
0386: // Console [3]
0387: // ------- [4]
0388: if (count == 6) {
0389: // remove the final separator
0390: windowMenu.remove(4);
0391: } else {
0392: int j = i - 4;
0393: for (; i < count - 1; i++) {
0394: JMenuItem this Item = windowMenu.getItem(i);
0395: if (this Item != null) {
0396: //1 D:\foo.js
0397: //2 D:\bar.js
0398: text = this Item.getText();
0399: if (text.equals("More Windows...")) {
0400: break;
0401: } else {
0402: pos = text.indexOf(' ');
0403: this Item.setText((char) ('0' + j) + " "
0404: + text.substring(pos + 1));
0405: this Item.setMnemonic('0' + j);
0406: j++;
0407: }
0408: }
0409: }
0410: if (count - 6 == 0 && lastItem != item) {
0411: if (lastItem.getText()
0412: .equals("More Windows...")) {
0413: windowMenu.remove(lastItem);
0414: }
0415: }
0416: }
0417: break;
0418: }
0419: }
0420: windowMenu.revalidate();
0421: }
0422:
0423: /**
0424: * Shows the line at which execution in the given stack frame just stopped.
0425: */
0426: void showStopLine(Dim.StackFrame frame) {
0427: String sourceName = frame.getUrl();
0428: if (sourceName == null || sourceName.equals("<stdin>")) {
0429: if (console.isVisible()) {
0430: console.show();
0431: }
0432: } else {
0433: showFileWindow(sourceName, -1);
0434: int lineNumber = frame.getLineNumber();
0435: FileWindow w = getFileWindow(sourceName);
0436: if (w != null) {
0437: setFilePosition(w, lineNumber);
0438: }
0439: }
0440: }
0441:
0442: /**
0443: * Shows a {@link FileWindow} for the given source, creating it
0444: * if it doesn't exist yet. if <code>lineNumber</code> is greater
0445: * than -1, it indicates the line number to select and display.
0446: * @param sourceUrl the source URL
0447: * @param lineNumber the line number to select, or -1
0448: */
0449: protected void showFileWindow(String sourceUrl, int lineNumber) {
0450: FileWindow w = getFileWindow(sourceUrl);
0451: if (w == null) {
0452: Dim.SourceInfo si = dim.sourceInfo(sourceUrl);
0453: createFileWindow(si, -1);
0454: w = getFileWindow(sourceUrl);
0455: }
0456: if (lineNumber > -1) {
0457: int start = w.getPosition(lineNumber - 1);
0458: int end = w.getPosition(lineNumber) - 1;
0459: w.textArea.select(start);
0460: w.textArea.setCaretPosition(start);
0461: w.textArea.moveCaretPosition(end);
0462: }
0463: try {
0464: if (w.isIcon()) {
0465: w.setIcon(false);
0466: }
0467: w.setVisible(true);
0468: w.moveToFront();
0469: w.setSelected(true);
0470: requestFocus();
0471: w.requestFocus();
0472: w.textArea.requestFocus();
0473: } catch (Exception exc) {
0474: }
0475: }
0476:
0477: /**
0478: * Creates and shows a new {@link FileWindow} for the given source.
0479: */
0480: protected void createFileWindow(Dim.SourceInfo sourceInfo, int line) {
0481: boolean activate = true;
0482:
0483: String url = sourceInfo.url();
0484: FileWindow w = new FileWindow(this , sourceInfo);
0485: fileWindows.put(url, w);
0486: if (line != -1) {
0487: if (currentWindow != null) {
0488: currentWindow.setPosition(-1);
0489: }
0490: try {
0491: w.setPosition(w.textArea.getLineStartOffset(line - 1));
0492: } catch (BadLocationException exc) {
0493: try {
0494: w.setPosition(w.textArea.getLineStartOffset(0));
0495: } catch (BadLocationException ee) {
0496: w.setPosition(-1);
0497: }
0498: }
0499: }
0500: desk.add(w);
0501: if (line != -1) {
0502: currentWindow = w;
0503: }
0504: menubar.addFile(url);
0505: w.setVisible(true);
0506:
0507: if (activate) {
0508: try {
0509: w.setMaximum(true);
0510: w.setSelected(true);
0511: w.moveToFront();
0512: } catch (Exception exc) {
0513: }
0514: }
0515: }
0516:
0517: /**
0518: * Update the source text for <code>sourceInfo</code>. This returns true
0519: * if a {@link FileWindow} for the given source exists and could be updated.
0520: * Otherwise, this does nothing and returns false.
0521: * @param sourceInfo the source info
0522: * @return true if a {@link FileWindow} for the given source exists
0523: * and could be updated, false otherwise.
0524: */
0525: protected boolean updateFileWindow(Dim.SourceInfo sourceInfo) {
0526: String fileName = sourceInfo.url();
0527: FileWindow w = getFileWindow(fileName);
0528: if (w != null) {
0529: w.updateText(sourceInfo);
0530: w.show();
0531: return true;
0532: }
0533: return false;
0534: }
0535:
0536: /**
0537: * Moves the current position in the given {@link FileWindow} to the
0538: * given line.
0539: */
0540: private void setFilePosition(FileWindow w, int line) {
0541: boolean activate = true;
0542: JTextArea ta = w.textArea;
0543: try {
0544: if (line == -1) {
0545: w.setPosition(-1);
0546: if (currentWindow == w) {
0547: currentWindow = null;
0548: }
0549: } else {
0550: int loc = ta.getLineStartOffset(line - 1);
0551: if (currentWindow != null && currentWindow != w) {
0552: currentWindow.setPosition(-1);
0553: }
0554: w.setPosition(loc);
0555: currentWindow = w;
0556: }
0557: } catch (BadLocationException exc) {
0558: // fix me
0559: }
0560: if (activate) {
0561: if (w.isIcon()) {
0562: desk.getDesktopManager().deiconifyFrame(w);
0563: }
0564: desk.getDesktopManager().activateFrame(w);
0565: try {
0566: w.show();
0567: w.toFront(); // required for correct frame layering (JDK 1.4.1)
0568: w.setSelected(true);
0569: } catch (Exception exc) {
0570: }
0571: }
0572: }
0573:
0574: /**
0575: * Handles script interruption.
0576: */
0577: void enterInterruptImpl(Dim.StackFrame lastFrame,
0578: String threadTitle, String alertMessage) {
0579: statusBar.setText("Thread: " + threadTitle);
0580:
0581: showStopLine(lastFrame);
0582:
0583: if (alertMessage != null) {
0584: MessageDialogWrapper.showMessageDialog(this , alertMessage,
0585: "Exception in Script", JOptionPane.ERROR_MESSAGE);
0586: }
0587:
0588: updateEnabled(true);
0589:
0590: Dim.ContextData contextData = lastFrame.contextData();
0591:
0592: JComboBox ctx = context.context;
0593: Vector toolTips = context.toolTips;
0594: context.disableUpdate();
0595: int frameCount = contextData.frameCount();
0596: ctx.removeAllItems();
0597: // workaround for JDK 1.4 bug that caches selected value even after
0598: // removeAllItems() is called
0599: ctx.setSelectedItem(null);
0600: toolTips.removeAllElements();
0601: for (int i = 0; i < frameCount; i++) {
0602: Dim.StackFrame frame = contextData.getFrame(i);
0603: String url = frame.getUrl();
0604: int lineNumber = frame.getLineNumber();
0605: String shortName = url;
0606: if (url.length() > 20) {
0607: shortName = "..." + url.substring(url.length() - 17);
0608: }
0609: String location = "\"" + shortName + "\", line "
0610: + lineNumber;
0611: ctx.insertItemAt(location, i);
0612: location = "\"" + url + "\", line " + lineNumber;
0613: toolTips.addElement(location);
0614: }
0615: context.enableUpdate();
0616: ctx.setSelectedIndex(0);
0617: ctx.setMinimumSize(new Dimension(50,
0618: ctx.getMinimumSize().height));
0619: }
0620:
0621: /**
0622: * Returns the 'Window' menu.
0623: */
0624: private JMenu getWindowMenu() {
0625: return menubar.getMenu(3);
0626: }
0627:
0628: /**
0629: * Displays a {@link JFileChooser} and returns the selected filename.
0630: */
0631: private String chooseFile(String title) {
0632: dlg.setDialogTitle(title);
0633: File CWD = null;
0634: String dir = SecurityUtilities.getSystemProperty("user.dir");
0635: if (dir != null) {
0636: CWD = new File(dir);
0637: }
0638: if (CWD != null) {
0639: dlg.setCurrentDirectory(CWD);
0640: }
0641: int returnVal = dlg.showOpenDialog(this );
0642: if (returnVal == JFileChooser.APPROVE_OPTION) {
0643: try {
0644: String result = dlg.getSelectedFile()
0645: .getCanonicalPath();
0646: CWD = dlg.getSelectedFile().getParentFile();
0647: Properties props = System.getProperties();
0648: props.put("user.dir", CWD.getPath());
0649: System.setProperties(props);
0650: return result;
0651: } catch (IOException ignored) {
0652: } catch (SecurityException ignored) {
0653: }
0654: }
0655: return null;
0656: }
0657:
0658: /**
0659: * Returns the current selected internal frame.
0660: */
0661: private JInternalFrame getSelectedFrame() {
0662: JInternalFrame[] frames = desk.getAllFrames();
0663: for (int i = 0; i < frames.length; i++) {
0664: if (frames[i].isShowing()) {
0665: return frames[i];
0666: }
0667: }
0668: return frames[frames.length - 1];
0669: }
0670:
0671: /**
0672: * Enables or disables the menu and tool bars with respect to the
0673: * state of script execution.
0674: */
0675: private void updateEnabled(boolean interrupted) {
0676: ((Menubar) getJMenuBar()).updateEnabled(interrupted);
0677: for (int ci = 0, cc = toolBar.getComponentCount(); ci < cc; ci++) {
0678: boolean enableButton;
0679: if (ci == 0) {
0680: // Break
0681: enableButton = !interrupted;
0682: } else {
0683: enableButton = interrupted;
0684: }
0685: toolBar.getComponent(ci).setEnabled(enableButton);
0686: }
0687: if (interrupted) {
0688: toolBar.setEnabled(true);
0689: // raise the debugger window
0690: int state = getExtendedState();
0691: if (state == Frame.ICONIFIED) {
0692: setExtendedState(Frame.NORMAL);
0693: }
0694: toFront();
0695: context.enable();
0696: } else {
0697: if (currentWindow != null)
0698: currentWindow.setPosition(-1);
0699: context.disable();
0700: }
0701: }
0702:
0703: /**
0704: * Calls {@link JSplitPane#setResizeWeight} via reflection.
0705: * For compatibility, since JDK < 1.3 does not have this method.
0706: */
0707: static void setResizeWeight(JSplitPane pane, double weight) {
0708: try {
0709: Method m = JSplitPane.class.getMethod("setResizeWeight",
0710: new Class[] { double.class });
0711: m.invoke(pane, new Object[] { new Double(weight) });
0712: } catch (NoSuchMethodException exc) {
0713: } catch (IllegalAccessException exc) {
0714: } catch (java.lang.reflect.InvocationTargetException exc) {
0715: }
0716: }
0717:
0718: /**
0719: * Reads the file with the given name and returns its contents as a String.
0720: */
0721: private String readFile(String fileName) {
0722: String text;
0723: try {
0724: Reader r = new FileReader(fileName);
0725: try {
0726: text = Kit.readReader(r);
0727: } finally {
0728: r.close();
0729: }
0730: } catch (IOException ex) {
0731: MessageDialogWrapper.showMessageDialog(this , ex
0732: .getMessage(), "Error reading " + fileName,
0733: JOptionPane.ERROR_MESSAGE);
0734: text = null;
0735: }
0736: return text;
0737: }
0738:
0739: // GuiCallback
0740:
0741: /**
0742: * Called when the source text for a script has been updated.
0743: */
0744: public void updateSourceText(Dim.SourceInfo sourceInfo) {
0745: RunProxy proxy = new RunProxy(this , RunProxy.UPDATE_SOURCE_TEXT);
0746: proxy.sourceInfo = sourceInfo;
0747: SwingUtilities.invokeLater(proxy);
0748: }
0749:
0750: /**
0751: * Called when the interrupt loop has been entered.
0752: */
0753: public void enterInterrupt(Dim.StackFrame lastFrame,
0754: String threadTitle, String alertMessage) {
0755: if (SwingUtilities.isEventDispatchThread()) {
0756: enterInterruptImpl(lastFrame, threadTitle, alertMessage);
0757: } else {
0758: RunProxy proxy = new RunProxy(this ,
0759: RunProxy.ENTER_INTERRUPT);
0760: proxy.lastFrame = lastFrame;
0761: proxy.threadTitle = threadTitle;
0762: proxy.alertMessage = alertMessage;
0763: SwingUtilities.invokeLater(proxy);
0764: }
0765: }
0766:
0767: /**
0768: * Returns whether the current thread is the GUI event thread.
0769: */
0770: public boolean isGuiEventThread() {
0771: return SwingUtilities.isEventDispatchThread();
0772: }
0773:
0774: /**
0775: * Processes the next GUI event.
0776: */
0777: public void dispatchNextGuiEvent() throws InterruptedException {
0778: EventQueue queue = awtEventQueue;
0779: if (queue == null) {
0780: queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
0781: awtEventQueue = queue;
0782: }
0783: AWTEvent event = queue.getNextEvent();
0784: if (event instanceof ActiveEvent) {
0785: ((ActiveEvent) event).dispatch();
0786: } else {
0787: Object source = event.getSource();
0788: if (source instanceof Component) {
0789: Component comp = (Component) source;
0790: comp.dispatchEvent(event);
0791: } else if (source instanceof MenuComponent) {
0792: ((MenuComponent) source).dispatchEvent(event);
0793: }
0794: }
0795: }
0796:
0797: // ActionListener
0798:
0799: /**
0800: * Performs an action from the menu or toolbar.
0801: */
0802: public void actionPerformed(ActionEvent e) {
0803: String cmd = e.getActionCommand();
0804: int returnValue = -1;
0805: if (cmd.equals("Cut") || cmd.equals("Copy")
0806: || cmd.equals("Paste")) {
0807: JInternalFrame f = getSelectedFrame();
0808: if (f != null && f instanceof ActionListener) {
0809: ((ActionListener) f).actionPerformed(e);
0810: }
0811: } else if (cmd.equals("Step Over")) {
0812: returnValue = Dim.STEP_OVER;
0813: } else if (cmd.equals("Step Into")) {
0814: returnValue = Dim.STEP_INTO;
0815: } else if (cmd.equals("Step Out")) {
0816: returnValue = Dim.STEP_OUT;
0817: } else if (cmd.equals("Go")) {
0818: returnValue = Dim.GO;
0819: } else if (cmd.equals("Break")) {
0820: dim.setBreak();
0821: } else if (cmd.equals("Exit")) {
0822: exit();
0823: } else if (cmd.equals("Open")) {
0824: String fileName = chooseFile("Select a file to compile");
0825: if (fileName != null) {
0826: String text = readFile(fileName);
0827: if (text != null) {
0828: RunProxy proxy = new RunProxy(this ,
0829: RunProxy.OPEN_FILE);
0830: proxy.fileName = fileName;
0831: proxy.text = text;
0832: new Thread(proxy).start();
0833: }
0834: }
0835: } else if (cmd.equals("Load")) {
0836: String fileName = chooseFile("Select a file to execute");
0837: if (fileName != null) {
0838: String text = readFile(fileName);
0839: if (text != null) {
0840: RunProxy proxy = new RunProxy(this ,
0841: RunProxy.LOAD_FILE);
0842: proxy.fileName = fileName;
0843: proxy.text = text;
0844: new Thread(proxy).start();
0845: }
0846: }
0847: } else if (cmd.equals("More Windows...")) {
0848: MoreWindows dlg = new MoreWindows(this , fileWindows,
0849: "Window", "Files");
0850: dlg.showDialog(this );
0851: } else if (cmd.equals("Console")) {
0852: if (console.isIcon()) {
0853: desk.getDesktopManager().deiconifyFrame(console);
0854: }
0855: console.show();
0856: desk.getDesktopManager().activateFrame(console);
0857: console.consoleTextArea.requestFocus();
0858: } else if (cmd.equals("Cut")) {
0859: } else if (cmd.equals("Copy")) {
0860: } else if (cmd.equals("Paste")) {
0861: } else if (cmd.equals("Go to function...")) {
0862: FindFunction dlg = new FindFunction(this , "Go to function",
0863: "Function");
0864: dlg.showDialog(this );
0865: } else if (cmd.equals("Tile")) {
0866: JInternalFrame[] frames = desk.getAllFrames();
0867: int count = frames.length;
0868: int rows, cols;
0869: rows = cols = (int) Math.sqrt(count);
0870: if (rows * cols < count) {
0871: cols++;
0872: if (rows * cols < count) {
0873: rows++;
0874: }
0875: }
0876: Dimension size = desk.getSize();
0877: int w = size.width / cols;
0878: int h = size.height / rows;
0879: int x = 0;
0880: int y = 0;
0881: for (int i = 0; i < rows; i++) {
0882: for (int j = 0; j < cols; j++) {
0883: int index = (i * cols) + j;
0884: if (index >= frames.length) {
0885: break;
0886: }
0887: JInternalFrame f = frames[index];
0888: try {
0889: f.setIcon(false);
0890: f.setMaximum(false);
0891: } catch (Exception exc) {
0892: }
0893: desk.getDesktopManager().setBoundsForFrame(f, x, y,
0894: w, h);
0895: x += w;
0896: }
0897: y += h;
0898: x = 0;
0899: }
0900: } else if (cmd.equals("Cascade")) {
0901: JInternalFrame[] frames = desk.getAllFrames();
0902: int count = frames.length;
0903: int x, y, w, h;
0904: x = y = 0;
0905: h = desk.getHeight();
0906: int d = h / count;
0907: if (d > 30)
0908: d = 30;
0909: for (int i = count - 1; i >= 0; i--, x += d, y += d) {
0910: JInternalFrame f = frames[i];
0911: try {
0912: f.setIcon(false);
0913: f.setMaximum(false);
0914: } catch (Exception exc) {
0915: }
0916: Dimension dimen = f.getPreferredSize();
0917: w = dimen.width;
0918: h = dimen.height;
0919: desk.getDesktopManager().setBoundsForFrame(f, x, y, w,
0920: h);
0921: }
0922: } else {
0923: Object obj = getFileWindow(cmd);
0924: if (obj != null) {
0925: FileWindow w = (FileWindow) obj;
0926: try {
0927: if (w.isIcon()) {
0928: w.setIcon(false);
0929: }
0930: w.setVisible(true);
0931: w.moveToFront();
0932: w.setSelected(true);
0933: } catch (Exception exc) {
0934: }
0935: }
0936: }
0937: if (returnValue != -1) {
0938: updateEnabled(false);
0939: dim.setReturnValue(returnValue);
0940: }
0941: }
0942: }
0943:
0944: /**
0945: * Helper class for showing a message dialog.
0946: */
0947: class MessageDialogWrapper {
0948:
0949: /**
0950: * Shows a message dialog, wrapping the <code>msg</code> at 60
0951: * columns.
0952: */
0953: public static void showMessageDialog(Component parent, String msg,
0954: String title, int flags) {
0955: if (msg.length() > 60) {
0956: StringBuffer buf = new StringBuffer();
0957: int len = msg.length();
0958: int j = 0;
0959: int i;
0960: for (i = 0; i < len; i++, j++) {
0961: char c = msg.charAt(i);
0962: buf.append(c);
0963: if (Character.isWhitespace(c)) {
0964: int k;
0965: for (k = i + 1; k < len; k++) {
0966: if (Character.isWhitespace(msg.charAt(k))) {
0967: break;
0968: }
0969: }
0970: if (k < len) {
0971: int nextWordLen = k - i;
0972: if (j + nextWordLen > 60) {
0973: buf.append('\n');
0974: j = 0;
0975: }
0976: }
0977: }
0978: }
0979: msg = buf.toString();
0980: }
0981: JOptionPane.showMessageDialog(parent, msg, title, flags);
0982: }
0983: }
0984:
0985: /**
0986: * Extension of JTextArea for script evaluation input.
0987: */
0988: class EvalTextArea extends JTextArea implements KeyListener,
0989: DocumentListener {
0990:
0991: /**
0992: * Serializable magic number.
0993: */
0994: private static final long serialVersionUID = -3918033649601064194L;
0995:
0996: /**
0997: * The debugger GUI.
0998: */
0999: private SwingGui debugGui;
1000:
1001: /**
1002: * History of expressions that have been evaluated
1003: */
1004: private Vector history;
1005:
1006: /**
1007: * Index of the selected history item.
1008: */
1009: private int historyIndex = -1;
1010:
1011: /**
1012: * Position in the display where output should go.
1013: */
1014: private int outputMark;
1015:
1016: /**
1017: * Creates a new EvalTextArea.
1018: */
1019: public EvalTextArea(SwingGui debugGui) {
1020: this .debugGui = debugGui;
1021: history = new java.util.Vector();
1022: Document doc = getDocument();
1023: doc.addDocumentListener(this );
1024: addKeyListener(this );
1025: setLineWrap(true);
1026: setFont(new Font("Monospaced", 0, 12));
1027: append("% ");
1028: outputMark = doc.getLength();
1029: }
1030:
1031: /**
1032: * Selects a subrange of the text.
1033: */
1034: public void select(int start, int end) {
1035: //requestFocus();
1036: super .select(start, end);
1037: }
1038:
1039: /**
1040: * Called when Enter is pressed.
1041: */
1042: private synchronized void returnPressed() {
1043: Document doc = getDocument();
1044: int len = doc.getLength();
1045: Segment segment = new Segment();
1046: try {
1047: doc.getText(outputMark, len - outputMark, segment);
1048: } catch (javax.swing.text.BadLocationException ignored) {
1049: ignored.printStackTrace();
1050: }
1051: String text = segment.toString();
1052: if (debugGui.dim.stringIsCompilableUnit(text)) {
1053: if (text.trim().length() > 0) {
1054: history.addElement(text);
1055: historyIndex = history.size();
1056: }
1057: append("\n");
1058: String result = debugGui.dim.eval(text);
1059: if (result.length() > 0) {
1060: append(result);
1061: append("\n");
1062: }
1063: append("% ");
1064: outputMark = doc.getLength();
1065: } else {
1066: append("\n");
1067: }
1068: }
1069:
1070: /**
1071: * Writes output into the text area.
1072: */
1073: public synchronized void write(String str) {
1074: insert(str, outputMark);
1075: int len = str.length();
1076: outputMark += len;
1077: select(outputMark, outputMark);
1078: }
1079:
1080: // KeyListener
1081:
1082: /**
1083: * Called when a key is pressed.
1084: */
1085: public void keyPressed(KeyEvent e) {
1086: int code = e.getKeyCode();
1087: if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT) {
1088: if (outputMark == getCaretPosition()) {
1089: e.consume();
1090: }
1091: } else if (code == KeyEvent.VK_HOME) {
1092: int caretPos = getCaretPosition();
1093: if (caretPos == outputMark) {
1094: e.consume();
1095: } else if (caretPos > outputMark) {
1096: if (!e.isControlDown()) {
1097: if (e.isShiftDown()) {
1098: moveCaretPosition(outputMark);
1099: } else {
1100: setCaretPosition(outputMark);
1101: }
1102: e.consume();
1103: }
1104: }
1105: } else if (code == KeyEvent.VK_ENTER) {
1106: returnPressed();
1107: e.consume();
1108: } else if (code == KeyEvent.VK_UP) {
1109: historyIndex--;
1110: if (historyIndex >= 0) {
1111: if (historyIndex >= history.size()) {
1112: historyIndex = history.size() - 1;
1113: }
1114: if (historyIndex >= 0) {
1115: String str = (String) history
1116: .elementAt(historyIndex);
1117: int len = getDocument().getLength();
1118: replaceRange(str, outputMark, len);
1119: int caretPos = outputMark + str.length();
1120: select(caretPos, caretPos);
1121: } else {
1122: historyIndex++;
1123: }
1124: } else {
1125: historyIndex++;
1126: }
1127: e.consume();
1128: } else if (code == KeyEvent.VK_DOWN) {
1129: int caretPos = outputMark;
1130: if (history.size() > 0) {
1131: historyIndex++;
1132: if (historyIndex < 0) {
1133: historyIndex = 0;
1134: }
1135: int len = getDocument().getLength();
1136: if (historyIndex < history.size()) {
1137: String str = (String) history
1138: .elementAt(historyIndex);
1139: replaceRange(str, outputMark, len);
1140: caretPos = outputMark + str.length();
1141: } else {
1142: historyIndex = history.size();
1143: replaceRange("", outputMark, len);
1144: }
1145: }
1146: select(caretPos, caretPos);
1147: e.consume();
1148: }
1149: }
1150:
1151: /**
1152: * Called when a key is typed.
1153: */
1154: public void keyTyped(KeyEvent e) {
1155: int keyChar = e.getKeyChar();
1156: if (keyChar == 0x8 /* KeyEvent.VK_BACK_SPACE */) {
1157: if (outputMark == getCaretPosition()) {
1158: e.consume();
1159: }
1160: } else if (getCaretPosition() < outputMark) {
1161: setCaretPosition(outputMark);
1162: }
1163: }
1164:
1165: /**
1166: * Called when a key is released.
1167: */
1168: public synchronized void keyReleased(KeyEvent e) {
1169: }
1170:
1171: // DocumentListener
1172:
1173: /**
1174: * Called when text was inserted into the text area.
1175: */
1176: public synchronized void insertUpdate(DocumentEvent e) {
1177: int len = e.getLength();
1178: int off = e.getOffset();
1179: if (outputMark > off) {
1180: outputMark += len;
1181: }
1182: }
1183:
1184: /**
1185: * Called when text was removed from the text area.
1186: */
1187: public synchronized void removeUpdate(DocumentEvent e) {
1188: int len = e.getLength();
1189: int off = e.getOffset();
1190: if (outputMark > off) {
1191: if (outputMark >= off + len) {
1192: outputMark -= len;
1193: } else {
1194: outputMark = off;
1195: }
1196: }
1197: }
1198:
1199: /**
1200: * Attempts to clean up the damage done by {@link #updateUI()}.
1201: */
1202: public synchronized void postUpdateUI() {
1203: //requestFocus();
1204: setCaret(getCaret());
1205: select(outputMark, outputMark);
1206: }
1207:
1208: /**
1209: * Called when text has changed in the text area.
1210: */
1211: public synchronized void changedUpdate(DocumentEvent e) {
1212: }
1213: }
1214:
1215: /**
1216: * An internal frame for evaluating script.
1217: */
1218: class EvalWindow extends JInternalFrame implements ActionListener {
1219:
1220: /**
1221: * Serializable magic number.
1222: */
1223: private static final long serialVersionUID = -2860585845212160176L;
1224:
1225: /**
1226: * The text area into which expressions can be typed.
1227: */
1228: private EvalTextArea evalTextArea;
1229:
1230: /**
1231: * Creates a new EvalWindow.
1232: */
1233: public EvalWindow(String name, SwingGui debugGui) {
1234: super (name, true, false, true, true);
1235: evalTextArea = new EvalTextArea(debugGui);
1236: evalTextArea.setRows(24);
1237: evalTextArea.setColumns(80);
1238: JScrollPane scroller = new JScrollPane(evalTextArea);
1239: setContentPane(scroller);
1240: //scroller.setPreferredSize(new Dimension(600, 400));
1241: pack();
1242: setVisible(true);
1243: }
1244:
1245: /**
1246: * Sets whether the text area is enabled.
1247: */
1248: public void setEnabled(boolean b) {
1249: super .setEnabled(b);
1250: evalTextArea.setEnabled(b);
1251: }
1252:
1253: // ActionListener
1254:
1255: /**
1256: * Performs an action on the text area.
1257: */
1258: public void actionPerformed(ActionEvent e) {
1259: String cmd = e.getActionCommand();
1260: if (cmd.equals("Cut")) {
1261: evalTextArea.cut();
1262: } else if (cmd.equals("Copy")) {
1263: evalTextArea.copy();
1264: } else if (cmd.equals("Paste")) {
1265: evalTextArea.paste();
1266: }
1267: }
1268: }
1269:
1270: /**
1271: * Internal frame for the console.
1272: */
1273: class JSInternalConsole extends JInternalFrame implements
1274: ActionListener {
1275:
1276: /**
1277: * Serializable magic number.
1278: */
1279: private static final long serialVersionUID = -5523468828771087292L;
1280:
1281: /**
1282: * Creates a new JSInternalConsole.
1283: */
1284: public JSInternalConsole(String name) {
1285: super (name, true, false, true, true);
1286: consoleTextArea = new ConsoleTextArea(null);
1287: consoleTextArea.setRows(24);
1288: consoleTextArea.setColumns(80);
1289: JScrollPane scroller = new JScrollPane(consoleTextArea);
1290: setContentPane(scroller);
1291: pack();
1292: addInternalFrameListener(new InternalFrameAdapter() {
1293: public void internalFrameActivated(InternalFrameEvent e) {
1294: // hack
1295: if (consoleTextArea.hasFocus()) {
1296: consoleTextArea.getCaret().setVisible(false);
1297: consoleTextArea.getCaret().setVisible(true);
1298: }
1299: }
1300: });
1301: }
1302:
1303: /**
1304: * The console text area.
1305: */
1306: ConsoleTextArea consoleTextArea;
1307:
1308: /**
1309: * Returns the input stream of the console text area.
1310: */
1311: public InputStream getIn() {
1312: return consoleTextArea.getIn();
1313: }
1314:
1315: /**
1316: * Returns the output stream of the console text area.
1317: */
1318: public PrintStream getOut() {
1319: return consoleTextArea.getOut();
1320: }
1321:
1322: /**
1323: * Returns the error stream of the console text area.
1324: */
1325: public PrintStream getErr() {
1326: return consoleTextArea.getErr();
1327: }
1328:
1329: // ActionListener
1330:
1331: /**
1332: * Performs an action on the text area.
1333: */
1334: public void actionPerformed(ActionEvent e) {
1335: String cmd = e.getActionCommand();
1336: if (cmd.equals("Cut")) {
1337: consoleTextArea.cut();
1338: } else if (cmd.equals("Copy")) {
1339: consoleTextArea.copy();
1340: } else if (cmd.equals("Paste")) {
1341: consoleTextArea.paste();
1342: }
1343: }
1344: }
1345:
1346: /**
1347: * Popup menu class for right-clicking on {@link FileTextArea}s.
1348: */
1349: class FilePopupMenu extends JPopupMenu {
1350:
1351: /**
1352: * Serializable magic number.
1353: */
1354: private static final long serialVersionUID = 3589525009546013565L;
1355:
1356: /**
1357: * The popup x position.
1358: */
1359: int x;
1360:
1361: /**
1362: * The popup y position.
1363: */
1364: int y;
1365:
1366: /**
1367: * Creates a new FilePopupMenu.
1368: */
1369: public FilePopupMenu(FileTextArea w) {
1370: JMenuItem item;
1371: add(item = new JMenuItem("Set Breakpoint"));
1372: item.addActionListener(w);
1373: add(item = new JMenuItem("Clear Breakpoint"));
1374: item.addActionListener(w);
1375: add(item = new JMenuItem("Run"));
1376: item.addActionListener(w);
1377: }
1378:
1379: /**
1380: * Displays the menu at the given coordinates.
1381: */
1382: public void show(JComponent comp, int x, int y) {
1383: this .x = x;
1384: this .y = y;
1385: super .show(comp, x, y);
1386: }
1387: }
1388:
1389: /**
1390: * Text area to display script source.
1391: */
1392: class FileTextArea extends JTextArea implements ActionListener,
1393: PopupMenuListener, KeyListener, MouseListener {
1394:
1395: /**
1396: * Serializable magic number.
1397: */
1398: private static final long serialVersionUID = -25032065448563720L;
1399:
1400: /**
1401: * The owning {@link FileWindow}.
1402: */
1403: private FileWindow w;
1404:
1405: /**
1406: * The popup menu.
1407: */
1408: private FilePopupMenu popup;
1409:
1410: /**
1411: * Creates a new FileTextArea.
1412: */
1413: public FileTextArea(FileWindow w) {
1414: this .w = w;
1415: popup = new FilePopupMenu(this );
1416: popup.addPopupMenuListener(this );
1417: addMouseListener(this );
1418: addKeyListener(this );
1419: setFont(new Font("Monospaced", 0, 12));
1420: }
1421:
1422: /**
1423: * Moves the selection to the given offset.
1424: */
1425: public void select(int pos) {
1426: if (pos >= 0) {
1427: try {
1428: int line = getLineOfOffset(pos);
1429: Rectangle rect = modelToView(pos);
1430: if (rect == null) {
1431: select(pos, pos);
1432: } else {
1433: try {
1434: Rectangle nrect = modelToView(getLineStartOffset(line + 1));
1435: if (nrect != null) {
1436: rect = nrect;
1437: }
1438: } catch (Exception exc) {
1439: }
1440: JViewport vp = (JViewport) getParent();
1441: Rectangle viewRect = vp.getViewRect();
1442: if (viewRect.y + viewRect.height > rect.y) {
1443: // need to scroll up
1444: select(pos, pos);
1445: } else {
1446: // need to scroll down
1447: rect.y += (viewRect.height - rect.height) / 2;
1448: scrollRectToVisible(rect);
1449: select(pos, pos);
1450: }
1451: }
1452: } catch (BadLocationException exc) {
1453: select(pos, pos);
1454: //exc.printStackTrace();
1455: }
1456: }
1457: }
1458:
1459: /**
1460: * Checks if the popup menu should be shown.
1461: */
1462: private void checkPopup(MouseEvent e) {
1463: if (e.isPopupTrigger()) {
1464: popup.show(this , e.getX(), e.getY());
1465: }
1466: }
1467:
1468: // MouseListener
1469:
1470: /**
1471: * Called when a mouse button is pressed.
1472: */
1473: public void mousePressed(MouseEvent e) {
1474: checkPopup(e);
1475: }
1476:
1477: /**
1478: * Called when the mouse is clicked.
1479: */
1480: public void mouseClicked(MouseEvent e) {
1481: checkPopup(e);
1482: requestFocus();
1483: getCaret().setVisible(true);
1484: }
1485:
1486: /**
1487: * Called when the mouse enters the component.
1488: */
1489: public void mouseEntered(MouseEvent e) {
1490: }
1491:
1492: /**
1493: * Called when the mouse exits the component.
1494: */
1495: public void mouseExited(MouseEvent e) {
1496: }
1497:
1498: /**
1499: * Called when a mouse button is released.
1500: */
1501: public void mouseReleased(MouseEvent e) {
1502: checkPopup(e);
1503: }
1504:
1505: // PopupMenuListener
1506:
1507: /**
1508: * Called before the popup menu will become visible.
1509: */
1510: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
1511: }
1512:
1513: /**
1514: * Called before the popup menu will become invisible.
1515: */
1516: public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
1517: }
1518:
1519: /**
1520: * Called when the popup menu is cancelled.
1521: */
1522: public void popupMenuCanceled(PopupMenuEvent e) {
1523: }
1524:
1525: // ActionListener
1526:
1527: /**
1528: * Performs an action.
1529: */
1530: public void actionPerformed(ActionEvent e) {
1531: int pos = viewToModel(new Point(popup.x, popup.y));
1532: popup.setVisible(false);
1533: String cmd = e.getActionCommand();
1534: int line = -1;
1535: try {
1536: line = getLineOfOffset(pos);
1537: } catch (Exception exc) {
1538: }
1539: if (cmd.equals("Set Breakpoint")) {
1540: w.setBreakPoint(line + 1);
1541: } else if (cmd.equals("Clear Breakpoint")) {
1542: w.clearBreakPoint(line + 1);
1543: } else if (cmd.equals("Run")) {
1544: w.load();
1545: }
1546: }
1547:
1548: // KeyListener
1549:
1550: /**
1551: * Called when a key is pressed.
1552: */
1553: public void keyPressed(KeyEvent e) {
1554: switch (e.getKeyCode()) {
1555: case KeyEvent.VK_BACK_SPACE:
1556: case KeyEvent.VK_ENTER:
1557: case KeyEvent.VK_DELETE:
1558: case KeyEvent.VK_TAB:
1559: e.consume();
1560: break;
1561: }
1562: }
1563:
1564: /**
1565: * Called when a key is typed.
1566: */
1567: public void keyTyped(KeyEvent e) {
1568: e.consume();
1569: }
1570:
1571: /**
1572: * Called when a key is released.
1573: */
1574: public void keyReleased(KeyEvent e) {
1575: e.consume();
1576: }
1577: }
1578:
1579: /**
1580: * Dialog to list the available windows.
1581: */
1582: class MoreWindows extends JDialog implements ActionListener {
1583:
1584: /**
1585: * Serializable magic number.
1586: */
1587: private static final long serialVersionUID = 5177066296457377546L;
1588:
1589: /**
1590: * Last selected value.
1591: */
1592: private String value;
1593:
1594: /**
1595: * The list component.
1596: */
1597: private JList list;
1598:
1599: /**
1600: * Our parent frame.
1601: */
1602: private SwingGui swingGui;
1603:
1604: /**
1605: * The "Select" button.
1606: */
1607: private JButton setButton;
1608:
1609: /**
1610: * The "Cancel" button.
1611: */
1612: private JButton cancelButton;
1613:
1614: /**
1615: * Creates a new MoreWindows.
1616: */
1617: MoreWindows(SwingGui frame, Hashtable fileWindows, String title,
1618: String labelText) {
1619: super (frame, title, true);
1620: this .swingGui = frame;
1621: //buttons
1622: cancelButton = new JButton("Cancel");
1623: setButton = new JButton("Select");
1624: cancelButton.addActionListener(this );
1625: setButton.addActionListener(this );
1626: getRootPane().setDefaultButton(setButton);
1627:
1628: //dim part of the dialog
1629: list = new JList(new DefaultListModel());
1630: DefaultListModel model = (DefaultListModel) list.getModel();
1631: model.clear();
1632: //model.fireIntervalRemoved(model, 0, size);
1633: Enumeration e = fileWindows.keys();
1634: while (e.hasMoreElements()) {
1635: String data = e.nextElement().toString();
1636: model.addElement(data);
1637: }
1638: list.setSelectedIndex(0);
1639: //model.fireIntervalAdded(model, 0, data.length);
1640: setButton.setEnabled(true);
1641: list
1642: .setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
1643: list.addMouseListener(new MouseHandler());
1644: JScrollPane listScroller = new JScrollPane(list);
1645: listScroller.setPreferredSize(new Dimension(320, 240));
1646: //XXX: Must do the following, too, or else the scroller thinks
1647: //XXX: it's taller than it is:
1648: listScroller.setMinimumSize(new Dimension(250, 80));
1649: listScroller.setAlignmentX(LEFT_ALIGNMENT);
1650:
1651: //Create a container so that we can add a title around
1652: //the scroll pane. Can't add a title directly to the
1653: //scroll pane because its background would be white.
1654: //Lay out the label and scroll pane from top to button.
1655: JPanel listPane = new JPanel();
1656: listPane.setLayout(new BoxLayout(listPane, BoxLayout.Y_AXIS));
1657: JLabel label = new JLabel(labelText);
1658: label.setLabelFor(list);
1659: listPane.add(label);
1660: listPane.add(Box.createRigidArea(new Dimension(0, 5)));
1661: listPane.add(listScroller);
1662: listPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10,
1663: 10));
1664:
1665: //Lay out the buttons from left to right.
1666: JPanel buttonPane = new JPanel();
1667: buttonPane
1668: .setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
1669: buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10,
1670: 10));
1671: buttonPane.add(Box.createHorizontalGlue());
1672: buttonPane.add(cancelButton);
1673: buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
1674: buttonPane.add(setButton);
1675:
1676: //Put everything together, using the content pane's BorderLayout.
1677: Container contentPane = getContentPane();
1678: contentPane.add(listPane, BorderLayout.CENTER);
1679: contentPane.add(buttonPane, BorderLayout.SOUTH);
1680: pack();
1681: addKeyListener(new KeyAdapter() {
1682: public void keyPressed(KeyEvent ke) {
1683: int code = ke.getKeyCode();
1684: if (code == KeyEvent.VK_ESCAPE) {
1685: ke.consume();
1686: value = null;
1687: setVisible(false);
1688: }
1689: }
1690: });
1691: }
1692:
1693: /**
1694: * Shows the dialog.
1695: */
1696: public String showDialog(Component comp) {
1697: value = null;
1698: setLocationRelativeTo(comp);
1699: setVisible(true);
1700: return value;
1701: }
1702:
1703: // ActionListener
1704:
1705: /**
1706: * Performs an action.
1707: */
1708: public void actionPerformed(ActionEvent e) {
1709: String cmd = e.getActionCommand();
1710: if (cmd.equals("Cancel")) {
1711: setVisible(false);
1712: value = null;
1713: } else if (cmd.equals("Select")) {
1714: value = (String) list.getSelectedValue();
1715: setVisible(false);
1716: swingGui.showFileWindow(value, -1);
1717: }
1718: }
1719:
1720: /**
1721: * MouseListener implementation for {@link #list}.
1722: */
1723: private class MouseHandler extends MouseAdapter {
1724: public void mouseClicked(MouseEvent e) {
1725: if (e.getClickCount() == 2) {
1726: setButton.doClick();
1727: }
1728: }
1729: }
1730: }
1731:
1732: /**
1733: * Find function dialog.
1734: */
1735: class FindFunction extends JDialog implements ActionListener {
1736:
1737: /**
1738: * Serializable magic number.
1739: */
1740: private static final long serialVersionUID = 559491015232880916L;
1741:
1742: /**
1743: * Last selected function.
1744: */
1745: private String value;
1746:
1747: /**
1748: * List of functions.
1749: */
1750: private JList list;
1751:
1752: /**
1753: * The debug GUI frame.
1754: */
1755: private SwingGui debugGui;
1756:
1757: /**
1758: * The "Select" button.
1759: */
1760: private JButton setButton;
1761:
1762: /**
1763: * The "Cancel" button.
1764: */
1765: private JButton cancelButton;
1766:
1767: /**
1768: * Creates a new FindFunction.
1769: */
1770: public FindFunction(SwingGui debugGui, String title,
1771: String labelText) {
1772: super (debugGui, title, true);
1773: this .debugGui = debugGui;
1774:
1775: cancelButton = new JButton("Cancel");
1776: setButton = new JButton("Select");
1777: cancelButton.addActionListener(this );
1778: setButton.addActionListener(this );
1779: getRootPane().setDefaultButton(setButton);
1780:
1781: list = new JList(new DefaultListModel());
1782: DefaultListModel model = (DefaultListModel) list.getModel();
1783: model.clear();
1784:
1785: String[] a = debugGui.dim.functionNames();
1786: java.util.Arrays.sort(a);
1787: for (int i = 0; i < a.length; i++) {
1788: model.addElement(a[i]);
1789: }
1790: list.setSelectedIndex(0);
1791:
1792: setButton.setEnabled(a.length > 0);
1793: list
1794: .setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
1795: list.addMouseListener(new MouseHandler());
1796: JScrollPane listScroller = new JScrollPane(list);
1797: listScroller.setPreferredSize(new Dimension(320, 240));
1798: listScroller.setMinimumSize(new Dimension(250, 80));
1799: listScroller.setAlignmentX(LEFT_ALIGNMENT);
1800:
1801: //Create a container so that we can add a title around
1802: //the scroll pane. Can't add a title directly to the
1803: //scroll pane because its background would be white.
1804: //Lay out the label and scroll pane from top to button.
1805: JPanel listPane = new JPanel();
1806: listPane.setLayout(new BoxLayout(listPane, BoxLayout.Y_AXIS));
1807: JLabel label = new JLabel(labelText);
1808: label.setLabelFor(list);
1809: listPane.add(label);
1810: listPane.add(Box.createRigidArea(new Dimension(0, 5)));
1811: listPane.add(listScroller);
1812: listPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10,
1813: 10));
1814:
1815: //Lay out the buttons from left to right.
1816: JPanel buttonPane = new JPanel();
1817: buttonPane
1818: .setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
1819: buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10,
1820: 10));
1821: buttonPane.add(Box.createHorizontalGlue());
1822: buttonPane.add(cancelButton);
1823: buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
1824: buttonPane.add(setButton);
1825:
1826: //Put everything together, using the content pane's BorderLayout.
1827: Container contentPane = getContentPane();
1828: contentPane.add(listPane, BorderLayout.CENTER);
1829: contentPane.add(buttonPane, BorderLayout.SOUTH);
1830: pack();
1831: addKeyListener(new KeyAdapter() {
1832: public void keyPressed(KeyEvent ke) {
1833: int code = ke.getKeyCode();
1834: if (code == KeyEvent.VK_ESCAPE) {
1835: ke.consume();
1836: value = null;
1837: setVisible(false);
1838: }
1839: }
1840: });
1841: }
1842:
1843: /**
1844: * Shows the dialog.
1845: */
1846: public String showDialog(Component comp) {
1847: value = null;
1848: setLocationRelativeTo(comp);
1849: setVisible(true);
1850: return value;
1851: }
1852:
1853: // ActionListener
1854:
1855: /**
1856: * Performs an action.
1857: */
1858: public void actionPerformed(ActionEvent e) {
1859: String cmd = e.getActionCommand();
1860: if (cmd.equals("Cancel")) {
1861: setVisible(false);
1862: value = null;
1863: } else if (cmd.equals("Select")) {
1864: if (list.getSelectedIndex() < 0) {
1865: return;
1866: }
1867: try {
1868: value = (String) list.getSelectedValue();
1869: } catch (ArrayIndexOutOfBoundsException exc) {
1870: return;
1871: }
1872: setVisible(false);
1873: Dim.FunctionSource item = debugGui.dim
1874: .functionSourceByName(value);
1875: if (item != null) {
1876: Dim.SourceInfo si = item.sourceInfo();
1877: String url = si.url();
1878: int lineNumber = item.firstLine();
1879: debugGui.showFileWindow(url, lineNumber);
1880: }
1881: }
1882: }
1883:
1884: /**
1885: * MouseListener implementation for {@link #list}.
1886: */
1887: class MouseHandler extends MouseAdapter {
1888: public void mouseClicked(MouseEvent e) {
1889: if (e.getClickCount() == 2) {
1890: setButton.doClick();
1891: }
1892: }
1893: }
1894: }
1895:
1896: /**
1897: * Gutter for FileWindows.
1898: */
1899: class FileHeader extends JPanel implements MouseListener {
1900:
1901: /**
1902: * Serializable magic number.
1903: */
1904: private static final long serialVersionUID = -2858905404778259127L;
1905:
1906: /**
1907: * The line that the mouse was pressed on.
1908: */
1909: private int pressLine = -1;
1910:
1911: /**
1912: * The owning FileWindow.
1913: */
1914: private FileWindow fileWindow;
1915:
1916: /**
1917: * Creates a new FileHeader.
1918: */
1919: public FileHeader(FileWindow fileWindow) {
1920: this .fileWindow = fileWindow;
1921: addMouseListener(this );
1922: update();
1923: }
1924:
1925: /**
1926: * Updates the gutter.
1927: */
1928: public void update() {
1929: FileTextArea textArea = fileWindow.textArea;
1930: Font font = textArea.getFont();
1931: setFont(font);
1932: FontMetrics metrics = getFontMetrics(font);
1933: int h = metrics.getHeight();
1934: int lineCount = textArea.getLineCount() + 1;
1935: String dummy = Integer.toString(lineCount);
1936: if (dummy.length() < 2) {
1937: dummy = "99";
1938: }
1939: Dimension d = new Dimension();
1940: d.width = metrics.stringWidth(dummy) + 16;
1941: d.height = lineCount * h + 100;
1942: setPreferredSize(d);
1943: setSize(d);
1944: }
1945:
1946: /**
1947: * Paints the component.
1948: */
1949: public void paint(Graphics g) {
1950: super .paint(g);
1951: FileTextArea textArea = fileWindow.textArea;
1952: Font font = textArea.getFont();
1953: g.setFont(font);
1954: FontMetrics metrics = getFontMetrics(font);
1955: Rectangle clip = g.getClipBounds();
1956: g.setColor(getBackground());
1957: g.fillRect(clip.x, clip.y, clip.width, clip.height);
1958: int ascent = metrics.getMaxAscent();
1959: int h = metrics.getHeight();
1960: int lineCount = textArea.getLineCount() + 1;
1961: String dummy = Integer.toString(lineCount);
1962: if (dummy.length() < 2) {
1963: dummy = "99";
1964: }
1965: int startLine = clip.y / h;
1966: int endLine = (clip.y + clip.height) / h + 1;
1967: int width = getWidth();
1968: if (endLine > lineCount)
1969: endLine = lineCount;
1970: for (int i = startLine; i < endLine; i++) {
1971: String text;
1972: int pos = -2;
1973: try {
1974: pos = textArea.getLineStartOffset(i);
1975: } catch (BadLocationException ignored) {
1976: }
1977: boolean isBreakPoint = fileWindow.isBreakPoint(i + 1);
1978: text = Integer.toString(i + 1) + " ";
1979: int y = i * h;
1980: g.setColor(Color.blue);
1981: g.drawString(text, 0, y + ascent);
1982: int x = width - ascent;
1983: if (isBreakPoint) {
1984: g.setColor(new Color(0x80, 0x00, 0x00));
1985: int dy = y + ascent - 9;
1986: g.fillOval(x, dy, 9, 9);
1987: g.drawOval(x, dy, 8, 8);
1988: g.drawOval(x, dy, 9, 9);
1989: }
1990: if (pos == fileWindow.currentPos) {
1991: Polygon arrow = new Polygon();
1992: int dx = x;
1993: y += ascent - 10;
1994: int dy = y;
1995: arrow.addPoint(dx, dy + 3);
1996: arrow.addPoint(dx + 5, dy + 3);
1997: for (x = dx + 5; x <= dx + 10; x++, y++) {
1998: arrow.addPoint(x, y);
1999: }
2000: for (x = dx + 9; x >= dx + 5; x--, y++) {
2001: arrow.addPoint(x, y);
2002: }
2003: arrow.addPoint(dx + 5, dy + 7);
2004: arrow.addPoint(dx, dy + 7);
2005: g.setColor(Color.yellow);
2006: g.fillPolygon(arrow);
2007: g.setColor(Color.black);
2008: g.drawPolygon(arrow);
2009: }
2010: }
2011: }
2012:
2013: // MouseListener
2014:
2015: /**
2016: * Called when the mouse enters the component.
2017: */
2018: public void mouseEntered(MouseEvent e) {
2019: }
2020:
2021: /**
2022: * Called when a mouse button is pressed.
2023: */
2024: public void mousePressed(MouseEvent e) {
2025: Font font = fileWindow.textArea.getFont();
2026: FontMetrics metrics = getFontMetrics(font);
2027: int h = metrics.getHeight();
2028: pressLine = e.getY() / h;
2029: }
2030:
2031: /**
2032: * Called when the mouse is clicked.
2033: */
2034: public void mouseClicked(MouseEvent e) {
2035: }
2036:
2037: /**
2038: * Called when the mouse exits the component.
2039: */
2040: public void mouseExited(MouseEvent e) {
2041: }
2042:
2043: /**
2044: * Called when a mouse button is released.
2045: */
2046: public void mouseReleased(MouseEvent e) {
2047: if (e.getComponent() == this
2048: && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
2049: int y = e.getY();
2050: Font font = fileWindow.textArea.getFont();
2051: FontMetrics metrics = getFontMetrics(font);
2052: int h = metrics.getHeight();
2053: int line = y / h;
2054: if (line == pressLine) {
2055: fileWindow.toggleBreakPoint(line + 1);
2056: } else {
2057: pressLine = -1;
2058: }
2059: }
2060: }
2061: }
2062:
2063: /**
2064: * An internal frame for script files.
2065: */
2066: class FileWindow extends JInternalFrame implements ActionListener {
2067:
2068: /**
2069: * Serializable magic number.
2070: */
2071: private static final long serialVersionUID = -6212382604952082370L;
2072:
2073: /**
2074: * The debugger GUI.
2075: */
2076: private SwingGui debugGui;
2077:
2078: /**
2079: * The SourceInfo object that describes the file.
2080: */
2081: private Dim.SourceInfo sourceInfo;
2082:
2083: /**
2084: * The FileTextArea that displays the file.
2085: */
2086: FileTextArea textArea;
2087:
2088: /**
2089: * The FileHeader that is the gutter for {@link #textArea}.
2090: */
2091: private FileHeader fileHeader;
2092:
2093: /**
2094: * Scroll pane for containing {@link #textArea}.
2095: */
2096: private JScrollPane p;
2097:
2098: /**
2099: * The current offset position.
2100: */
2101: int currentPos;
2102:
2103: /**
2104: * Loads the file.
2105: */
2106: void load() {
2107: String url = getUrl();
2108: if (url != null) {
2109: RunProxy proxy = new RunProxy(debugGui, RunProxy.LOAD_FILE);
2110: proxy.fileName = url;
2111: proxy.text = sourceInfo.source();
2112: new Thread(proxy).start();
2113: }
2114: }
2115:
2116: /**
2117: * Returns the offset position for the given line.
2118: */
2119: public int getPosition(int line) {
2120: int result = -1;
2121: try {
2122: result = textArea.getLineStartOffset(line);
2123: } catch (javax.swing.text.BadLocationException exc) {
2124: }
2125: return result;
2126: }
2127:
2128: /**
2129: * Returns whether the given line has a breakpoint.
2130: */
2131: public boolean isBreakPoint(int line) {
2132: return sourceInfo.breakableLine(line)
2133: && sourceInfo.breakpoint(line);
2134: }
2135:
2136: /**
2137: * Toggles the breakpoint on the given line.
2138: */
2139: public void toggleBreakPoint(int line) {
2140: if (!isBreakPoint(line)) {
2141: setBreakPoint(line);
2142: } else {
2143: clearBreakPoint(line);
2144: }
2145: }
2146:
2147: /**
2148: * Sets a breakpoint on the given line.
2149: */
2150: public void setBreakPoint(int line) {
2151: if (sourceInfo.breakableLine(line)) {
2152: boolean changed = sourceInfo.breakpoint(line, true);
2153: if (changed) {
2154: fileHeader.repaint();
2155: }
2156: }
2157: }
2158:
2159: /**
2160: * Clears a breakpoint from the given line.
2161: */
2162: public void clearBreakPoint(int line) {
2163: if (sourceInfo.breakableLine(line)) {
2164: boolean changed = sourceInfo.breakpoint(line, false);
2165: if (changed) {
2166: fileHeader.repaint();
2167: }
2168: }
2169: }
2170:
2171: /**
2172: * Creates a new FileWindow.
2173: */
2174: public FileWindow(SwingGui debugGui, Dim.SourceInfo sourceInfo) {
2175: super (SwingGui.getShortName(sourceInfo.url()), true, true,
2176: true, true);
2177: this .debugGui = debugGui;
2178: this .sourceInfo = sourceInfo;
2179: updateToolTip();
2180: currentPos = -1;
2181: textArea = new FileTextArea(this );
2182: textArea.setRows(24);
2183: textArea.setColumns(80);
2184: p = new JScrollPane();
2185: fileHeader = new FileHeader(this );
2186: p.setViewportView(textArea);
2187: p.setRowHeaderView(fileHeader);
2188: setContentPane(p);
2189: pack();
2190: updateText(sourceInfo);
2191: textArea.select(0);
2192: }
2193:
2194: /**
2195: * Updates the tool tip contents.
2196: */
2197: private void updateToolTip() {
2198: // Try to set tool tip on frame. On Mac OS X 10.5,
2199: // the number of components is different, so try to be safe.
2200: int n = getComponentCount() - 1;
2201: if (n > 1) {
2202: n = 1;
2203: } else if (n < 0) {
2204: return;
2205: }
2206: Component c = getComponent(n);
2207: // this will work at least for Metal L&F
2208: if (c != null && c instanceof JComponent) {
2209: ((JComponent) c).setToolTipText(getUrl());
2210: }
2211: }
2212:
2213: /**
2214: * Returns the URL of the source.
2215: */
2216: public String getUrl() {
2217: return sourceInfo.url();
2218: }
2219:
2220: /**
2221: * Called when the text of the script has changed.
2222: */
2223: public void updateText(Dim.SourceInfo sourceInfo) {
2224: this .sourceInfo = sourceInfo;
2225: String newText = sourceInfo.source();
2226: if (!textArea.getText().equals(newText)) {
2227: textArea.setText(newText);
2228: int pos = 0;
2229: if (currentPos != -1) {
2230: pos = currentPos;
2231: }
2232: textArea.select(pos);
2233: }
2234: fileHeader.update();
2235: fileHeader.repaint();
2236: }
2237:
2238: /**
2239: * Sets the cursor position.
2240: */
2241: public void setPosition(int pos) {
2242: textArea.select(pos);
2243: currentPos = pos;
2244: fileHeader.repaint();
2245: }
2246:
2247: /**
2248: * Selects a range of characters.
2249: */
2250: public void select(int start, int end) {
2251: int docEnd = textArea.getDocument().getLength();
2252: textArea.select(docEnd, docEnd);
2253: textArea.select(start, end);
2254: }
2255:
2256: /**
2257: * Disposes this FileWindow.
2258: */
2259: public void dispose() {
2260: debugGui.removeWindow(this );
2261: super .dispose();
2262: }
2263:
2264: // ActionListener
2265:
2266: /**
2267: * Performs an action.
2268: */
2269: public void actionPerformed(ActionEvent e) {
2270: String cmd = e.getActionCommand();
2271: if (cmd.equals("Cut")) {
2272: // textArea.cut();
2273: } else if (cmd.equals("Copy")) {
2274: textArea.copy();
2275: } else if (cmd.equals("Paste")) {
2276: // textArea.paste();
2277: }
2278: }
2279: }
2280:
2281: /**
2282: * Table model class for watched expressions.
2283: */
2284: class MyTableModel extends AbstractTableModel {
2285:
2286: /**
2287: * Serializable magic number.
2288: */
2289: private static final long serialVersionUID = 2971618907207577000L;
2290:
2291: /**
2292: * The debugger GUI.
2293: */
2294: private SwingGui debugGui;
2295:
2296: /**
2297: * Vector of watched expressions.
2298: */
2299: private Vector expressions;
2300:
2301: /**
2302: * Vector of values from evaluated from {@link #expressions}.
2303: */
2304: private Vector values;
2305:
2306: /**
2307: * Creates a new MyTableModel.
2308: */
2309: public MyTableModel(SwingGui debugGui) {
2310: this .debugGui = debugGui;
2311: expressions = new Vector();
2312: values = new Vector();
2313: expressions.addElement("");
2314: values.addElement("");
2315: }
2316:
2317: /**
2318: * Returns the number of columns in the table (2).
2319: */
2320: public int getColumnCount() {
2321: return 2;
2322: }
2323:
2324: /**
2325: * Returns the number of rows in the table.
2326: */
2327: public int getRowCount() {
2328: return expressions.size();
2329: }
2330:
2331: /**
2332: * Returns the name of the given column.
2333: */
2334: public String getColumnName(int column) {
2335: switch (column) {
2336: case 0:
2337: return "Expression";
2338: case 1:
2339: return "Value";
2340: }
2341: return null;
2342: }
2343:
2344: /**
2345: * Returns whether the given cell is editable.
2346: */
2347: public boolean isCellEditable(int row, int column) {
2348: return true;
2349: }
2350:
2351: /**
2352: * Returns the value in the given cell.
2353: */
2354: public Object getValueAt(int row, int column) {
2355: switch (column) {
2356: case 0:
2357: return expressions.elementAt(row);
2358: case 1:
2359: return values.elementAt(row);
2360: }
2361: return "";
2362: }
2363:
2364: /**
2365: * Sets the value in the given cell.
2366: */
2367: public void setValueAt(Object value, int row, int column) {
2368: switch (column) {
2369: case 0:
2370: String expr = value.toString();
2371: expressions.setElementAt(expr, row);
2372: String result = "";
2373: if (expr.length() > 0) {
2374: result = debugGui.dim.eval(expr);
2375: if (result == null)
2376: result = "";
2377: }
2378: values.setElementAt(result, row);
2379: updateModel();
2380: if (row + 1 == expressions.size()) {
2381: expressions.addElement("");
2382: values.addElement("");
2383: fireTableRowsInserted(row + 1, row + 1);
2384: }
2385: break;
2386: case 1:
2387: // just reset column 2; ignore edits
2388: fireTableDataChanged();
2389: }
2390: }
2391:
2392: /**
2393: * Re-evaluates the expressions in the table.
2394: */
2395: void updateModel() {
2396: for (int i = 0; i < expressions.size(); ++i) {
2397: Object value = expressions.elementAt(i);
2398: String expr = value.toString();
2399: String result = "";
2400: if (expr.length() > 0) {
2401: result = debugGui.dim.eval(expr);
2402: if (result == null)
2403: result = "";
2404: } else {
2405: result = "";
2406: }
2407: result = result.replace('\n', ' ');
2408: values.setElementAt(result, i);
2409: }
2410: fireTableDataChanged();
2411: }
2412: }
2413:
2414: /**
2415: * A table for evaluated expressions.
2416: */
2417: class Evaluator extends JTable {
2418:
2419: /**
2420: * Serializable magic number.
2421: */
2422: private static final long serialVersionUID = 8133672432982594256L;
2423:
2424: /**
2425: * The {@link TableModel} for this table.
2426: */
2427: MyTableModel tableModel;
2428:
2429: /**
2430: * Creates a new Evaluator.
2431: */
2432: public Evaluator(SwingGui debugGui) {
2433: super (new MyTableModel(debugGui));
2434: tableModel = (MyTableModel) getModel();
2435: }
2436: }
2437:
2438: /**
2439: * Tree model for script object inspection.
2440: */
2441: class VariableModel implements TreeTableModel {
2442:
2443: /**
2444: * Serializable magic number.
2445: */
2446: private static final String[] cNames = { " Name", " Value" };
2447:
2448: /**
2449: * Tree column types.
2450: */
2451: private static final Class[] cTypes = { TreeTableModel.class,
2452: String.class };
2453:
2454: /**
2455: * Empty {@link VariableNode} array.
2456: */
2457: private static final VariableNode[] CHILDLESS = new VariableNode[0];
2458:
2459: /**
2460: * The debugger.
2461: */
2462: private Dim debugger;
2463:
2464: /**
2465: * The root node.
2466: */
2467: private VariableNode root;
2468:
2469: /**
2470: * Creates a new VariableModel.
2471: */
2472: public VariableModel() {
2473: }
2474:
2475: /**
2476: * Creates a new VariableModel.
2477: */
2478: public VariableModel(Dim debugger, Object scope) {
2479: this .debugger = debugger;
2480: this .root = new VariableNode(scope, "this");
2481: }
2482:
2483: // TreeTableModel
2484:
2485: /**
2486: * Returns the root node of the tree.
2487: */
2488: public Object getRoot() {
2489: if (debugger == null) {
2490: return null;
2491: }
2492: return root;
2493: }
2494:
2495: /**
2496: * Returns the number of children of the given node.
2497: */
2498: public int getChildCount(Object nodeObj) {
2499: if (debugger == null) {
2500: return 0;
2501: }
2502: VariableNode node = (VariableNode) nodeObj;
2503: return children(node).length;
2504: }
2505:
2506: /**
2507: * Returns a child of the given node.
2508: */
2509: public Object getChild(Object nodeObj, int i) {
2510: if (debugger == null) {
2511: return null;
2512: }
2513: VariableNode node = (VariableNode) nodeObj;
2514: return children(node)[i];
2515: }
2516:
2517: /**
2518: * Returns whether the given node is a leaf node.
2519: */
2520: public boolean isLeaf(Object nodeObj) {
2521: if (debugger == null) {
2522: return true;
2523: }
2524: VariableNode node = (VariableNode) nodeObj;
2525: return children(node).length == 0;
2526: }
2527:
2528: /**
2529: * Returns the index of a node under its parent.
2530: */
2531: public int getIndexOfChild(Object parentObj, Object childObj) {
2532: if (debugger == null) {
2533: return -1;
2534: }
2535: VariableNode parent = (VariableNode) parentObj;
2536: VariableNode child = (VariableNode) childObj;
2537: VariableNode[] children = children(parent);
2538: for (int i = 0; i != children.length; i++) {
2539: if (children[i] == child) {
2540: return i;
2541: }
2542: }
2543: return -1;
2544: }
2545:
2546: /**
2547: * Returns whether the given cell is editable.
2548: */
2549: public boolean isCellEditable(Object node, int column) {
2550: return column == 0;
2551: }
2552:
2553: /**
2554: * Sets the value at the given cell.
2555: */
2556: public void setValueAt(Object value, Object node, int column) {
2557: }
2558:
2559: /**
2560: * Adds a TreeModelListener to this tree.
2561: */
2562: public void addTreeModelListener(TreeModelListener l) {
2563: }
2564:
2565: /**
2566: * Removes a TreeModelListener from this tree.
2567: */
2568: public void removeTreeModelListener(TreeModelListener l) {
2569: }
2570:
2571: public void valueForPathChanged(TreePath path, Object newValue) {
2572: }
2573:
2574: // TreeTableNode
2575:
2576: /**
2577: * Returns the number of columns.
2578: */
2579: public int getColumnCount() {
2580: return cNames.length;
2581: }
2582:
2583: /**
2584: * Returns the name of the given column.
2585: */
2586: public String getColumnName(int column) {
2587: return cNames[column];
2588: }
2589:
2590: /**
2591: * Returns the type of value stored in the given column.
2592: */
2593: public Class getColumnClass(int column) {
2594: return cTypes[column];
2595: }
2596:
2597: /**
2598: * Returns the value at the given cell.
2599: */
2600: public Object getValueAt(Object nodeObj, int column) {
2601: if (debugger == null) {
2602: return null;
2603: }
2604: VariableNode node = (VariableNode) nodeObj;
2605: switch (column) {
2606: case 0: // Name
2607: return node.toString();
2608: case 1: // Value
2609: String result;
2610: try {
2611: result = debugger.objectToString(getValue(node));
2612: } catch (RuntimeException exc) {
2613: result = exc.getMessage();
2614: }
2615: StringBuffer buf = new StringBuffer();
2616: int len = result.length();
2617: for (int i = 0; i < len; i++) {
2618: char ch = result.charAt(i);
2619: if (Character.isISOControl(ch)) {
2620: ch = ' ';
2621: }
2622: buf.append(ch);
2623: }
2624: return buf.toString();
2625: }
2626: return null;
2627: }
2628:
2629: /**
2630: * Returns an array of the children of the given node.
2631: */
2632: private VariableNode[] children(VariableNode node) {
2633: if (node.children != null) {
2634: return node.children;
2635: }
2636:
2637: VariableNode[] children;
2638:
2639: Object value = getValue(node);
2640: Object[] ids = debugger.getObjectIds(value);
2641: if (ids == null || ids.length == 0) {
2642: children = CHILDLESS;
2643: } else {
2644: Arrays.sort(ids, new Comparator() {
2645: public int compare(Object l, Object r) {
2646: if (l instanceof String) {
2647: if (r instanceof Integer) {
2648: return -1;
2649: }
2650: return ((String) l)
2651: .compareToIgnoreCase((String) r);
2652: } else {
2653: if (r instanceof String) {
2654: return 1;
2655: }
2656: int lint = ((Integer) l).intValue();
2657: int rint = ((Integer) r).intValue();
2658: return lint - rint;
2659: }
2660: }
2661: });
2662: children = new VariableNode[ids.length];
2663: for (int i = 0; i != ids.length; ++i) {
2664: children[i] = new VariableNode(value, ids[i]);
2665: }
2666: }
2667: node.children = children;
2668: return children;
2669: }
2670:
2671: /**
2672: * Returns the value of the given node.
2673: */
2674: public Object getValue(VariableNode node) {
2675: try {
2676: return debugger.getObjectProperty(node.object, node.id);
2677: } catch (Exception exc) {
2678: return "undefined";
2679: }
2680: }
2681:
2682: /**
2683: * A variable node in the tree.
2684: */
2685: private static class VariableNode {
2686:
2687: /**
2688: * The script object.
2689: */
2690: private Object object;
2691:
2692: /**
2693: * The object name. Either a String or an Integer.
2694: */
2695: private Object id;
2696:
2697: /**
2698: * Array of child nodes. This is filled with the properties of
2699: * the object.
2700: */
2701: private VariableNode[] children;
2702:
2703: /**
2704: * Creates a new VariableNode.
2705: */
2706: public VariableNode(Object object, Object id) {
2707: this .object = object;
2708: this .id = id;
2709: }
2710:
2711: /**
2712: * Returns a string representation of this node.
2713: */
2714: public String toString() {
2715: return id instanceof String ? (String) id : "["
2716: + ((Integer) id).intValue() + "]";
2717: }
2718: }
2719: }
2720:
2721: /**
2722: * A tree table for browsing script objects.
2723: */
2724: class MyTreeTable extends JTreeTable {
2725:
2726: /**
2727: * Serializable magic number.
2728: */
2729: private static final long serialVersionUID = 3457265548184453049L;
2730:
2731: /**
2732: * Creates a new MyTreeTable.
2733: */
2734: public MyTreeTable(VariableModel model) {
2735: super (model);
2736: }
2737:
2738: /**
2739: * Initializes a tree for this tree table.
2740: */
2741: public JTree resetTree(TreeTableModel treeTableModel) {
2742: tree = new TreeTableCellRenderer(treeTableModel);
2743:
2744: // Install a tableModel representing the visible rows in the tree.
2745: super .setModel(new TreeTableModelAdapter(treeTableModel, tree));
2746:
2747: // Force the JTable and JTree to share their row selection models.
2748: ListToTreeSelectionModelWrapper selectionWrapper = new ListToTreeSelectionModelWrapper();
2749: tree.setSelectionModel(selectionWrapper);
2750: setSelectionModel(selectionWrapper.getListSelectionModel());
2751:
2752: // Make the tree and table row heights the same.
2753: if (tree.getRowHeight() < 1) {
2754: // Metal looks better like this.
2755: setRowHeight(18);
2756: }
2757:
2758: // Install the tree editor renderer and editor.
2759: setDefaultRenderer(TreeTableModel.class, tree);
2760: setDefaultEditor(TreeTableModel.class,
2761: new TreeTableCellEditor());
2762: setShowGrid(true);
2763: setIntercellSpacing(new Dimension(1, 1));
2764: tree.setRootVisible(false);
2765: tree.setShowsRootHandles(true);
2766: DefaultTreeCellRenderer r = (DefaultTreeCellRenderer) tree
2767: .getCellRenderer();
2768: r.setOpenIcon(null);
2769: r.setClosedIcon(null);
2770: r.setLeafIcon(null);
2771: return tree;
2772: }
2773:
2774: /**
2775: * Returns whether the cell under the coordinates of the mouse
2776: * in the {@link EventObject} is editable.
2777: */
2778: public boolean isCellEditable(EventObject e) {
2779: if (e instanceof MouseEvent) {
2780: MouseEvent me = (MouseEvent) e;
2781: // If the modifiers are not 0 (or the left mouse button),
2782: // tree may try and toggle the selection, and table
2783: // will then try and toggle, resulting in the
2784: // selection remaining the same. To avoid this, we
2785: // only dispatch when the modifiers are 0 (or the left mouse
2786: // button).
2787: if (me.getModifiers() == 0
2788: || ((me.getModifiers() & (InputEvent.BUTTON1_MASK | 1024)) != 0 && (me
2789: .getModifiers() & (InputEvent.SHIFT_MASK
2790: | InputEvent.CTRL_MASK
2791: | InputEvent.ALT_MASK
2792: | InputEvent.BUTTON2_MASK
2793: | InputEvent.BUTTON3_MASK | 64 | //SHIFT_DOWN_MASK
2794: 128 | //CTRL_DOWN_MASK
2795: 512 | // ALT_DOWN_MASK
2796: 2048 | //BUTTON2_DOWN_MASK
2797: 4096 //BUTTON3_DOWN_MASK
2798: )) == 0)) {
2799: int row = rowAtPoint(me.getPoint());
2800: for (int counter = getColumnCount() - 1; counter >= 0; counter--) {
2801: if (TreeTableModel.class == getColumnClass(counter)) {
2802: MouseEvent newME = new MouseEvent(
2803: MyTreeTable.this .tree, me.getID(), me
2804: .getWhen(), me.getModifiers(),
2805: me.getX()
2806: - getCellRect(row, counter,
2807: true).x, me.getY(), me
2808: .getClickCount(), me
2809: .isPopupTrigger());
2810: MyTreeTable.this .tree.dispatchEvent(newME);
2811: break;
2812: }
2813: }
2814: }
2815: if (me.getClickCount() >= 3) {
2816: return true;
2817: }
2818: return false;
2819: }
2820: if (e == null) {
2821: return true;
2822: }
2823: return false;
2824: }
2825: }
2826:
2827: /**
2828: * Panel that shows information about the context.
2829: */
2830: class ContextWindow extends JPanel implements ActionListener {
2831:
2832: /**
2833: * Serializable magic number.
2834: */
2835: private static final long serialVersionUID = 2306040975490228051L;
2836:
2837: /**
2838: * The debugger GUI.
2839: */
2840: private SwingGui debugGui;
2841:
2842: /**
2843: * The combo box that holds the stack frames.
2844: */
2845: JComboBox context;
2846:
2847: /**
2848: * Tool tips for the stack frames.
2849: */
2850: Vector toolTips;
2851:
2852: /**
2853: * Tabbed pane for "this" and "locals".
2854: */
2855: private JTabbedPane tabs;
2856:
2857: /**
2858: * Tabbed pane for "watch" and "evaluate".
2859: */
2860: private JTabbedPane tabs2;
2861:
2862: /**
2863: * The table showing the "this" object.
2864: */
2865: private MyTreeTable this Table;
2866:
2867: /**
2868: * The table showing the stack local variables.
2869: */
2870: private MyTreeTable localsTable;
2871:
2872: /**
2873: * The {@link #evaluator}'s table model.
2874: */
2875: private MyTableModel tableModel;
2876:
2877: /**
2878: * The script evaluator table.
2879: */
2880: private Evaluator evaluator;
2881:
2882: /**
2883: * The script evaluation text area.
2884: */
2885: private EvalTextArea cmdLine;
2886:
2887: /**
2888: * The split pane.
2889: */
2890: JSplitPane split;
2891:
2892: /**
2893: * Whether the ContextWindow is enabled.
2894: */
2895: private boolean enabled;
2896:
2897: /**
2898: * Creates a new ContextWindow.
2899: */
2900: public ContextWindow(final SwingGui debugGui) {
2901: this .debugGui = debugGui;
2902: enabled = false;
2903: JPanel left = new JPanel();
2904: JToolBar t1 = new JToolBar();
2905: t1.setName("Variables");
2906: t1.setLayout(new GridLayout());
2907: t1.add(left);
2908: JPanel p1 = new JPanel();
2909: p1.setLayout(new GridLayout());
2910: JPanel p2 = new JPanel();
2911: p2.setLayout(new GridLayout());
2912: p1.add(t1);
2913: JLabel label = new JLabel("Context:");
2914: context = new JComboBox();
2915: context.setLightWeightPopupEnabled(false);
2916: toolTips = new java.util.Vector();
2917: label.setBorder(context.getBorder());
2918: context.addActionListener(this );
2919: context.setActionCommand("ContextSwitch");
2920: GridBagLayout layout = new GridBagLayout();
2921: left.setLayout(layout);
2922: GridBagConstraints lc = new GridBagConstraints();
2923: lc.insets.left = 5;
2924: lc.anchor = GridBagConstraints.WEST;
2925: lc.ipadx = 5;
2926: layout.setConstraints(label, lc);
2927: left.add(label);
2928: GridBagConstraints c = new GridBagConstraints();
2929: c.gridwidth = GridBagConstraints.REMAINDER;
2930: c.fill = GridBagConstraints.HORIZONTAL;
2931: c.anchor = GridBagConstraints.WEST;
2932: layout.setConstraints(context, c);
2933: left.add(context);
2934: tabs = new JTabbedPane(SwingConstants.BOTTOM);
2935: tabs.setPreferredSize(new Dimension(500, 300));
2936: this Table = new MyTreeTable(new VariableModel());
2937: JScrollPane jsp = new JScrollPane(this Table);
2938: jsp.getViewport().setViewSize(new Dimension(5, 2));
2939: tabs.add("this", jsp);
2940: localsTable = new MyTreeTable(new VariableModel());
2941: localsTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
2942: localsTable.setPreferredSize(null);
2943: jsp = new JScrollPane(localsTable);
2944: tabs.add("Locals", jsp);
2945: c.weightx = c.weighty = 1;
2946: c.gridheight = GridBagConstraints.REMAINDER;
2947: c.fill = GridBagConstraints.BOTH;
2948: c.anchor = GridBagConstraints.WEST;
2949: layout.setConstraints(tabs, c);
2950: left.add(tabs);
2951: evaluator = new Evaluator(debugGui);
2952: cmdLine = new EvalTextArea(debugGui);
2953: //cmdLine.requestFocus();
2954: tableModel = evaluator.tableModel;
2955: jsp = new JScrollPane(evaluator);
2956: JToolBar t2 = new JToolBar();
2957: t2.setName("Evaluate");
2958: tabs2 = new JTabbedPane(SwingConstants.BOTTOM);
2959: tabs2.add("Watch", jsp);
2960: tabs2.add("Evaluate", new JScrollPane(cmdLine));
2961: tabs2.setPreferredSize(new Dimension(500, 300));
2962: t2.setLayout(new GridLayout());
2963: t2.add(tabs2);
2964: p2.add(t2);
2965: evaluator.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
2966: split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, p1, p2);
2967: split.setOneTouchExpandable(true);
2968: SwingGui.setResizeWeight(split, 0.5);
2969: setLayout(new BorderLayout());
2970: add(split, BorderLayout.CENTER);
2971:
2972: final JToolBar finalT1 = t1;
2973: final JToolBar finalT2 = t2;
2974: final JPanel finalP1 = p1;
2975: final JPanel finalP2 = p2;
2976: final JSplitPane finalSplit = split;
2977: final JPanel finalThis = this ;
2978:
2979: ComponentListener clistener = new ComponentListener() {
2980: boolean t2Docked = true;
2981:
2982: void check(Component comp) {
2983: Component this Parent = finalThis.getParent();
2984: if (this Parent == null) {
2985: return;
2986: }
2987: Component parent = finalT1.getParent();
2988: boolean leftDocked = true;
2989: boolean rightDocked = true;
2990: boolean adjustVerticalSplit = false;
2991: if (parent != null) {
2992: if (parent != finalP1) {
2993: while (!(parent instanceof JFrame)) {
2994: parent = parent.getParent();
2995: }
2996: JFrame frame = (JFrame) parent;
2997: debugGui.addTopLevel("Variables", frame);
2998:
2999: // We need the following hacks because:
3000: // - We want an undocked toolbar to be
3001: // resizable.
3002: // - We are using JToolbar as a container of a
3003: // JComboBox. Without this JComboBox's popup
3004: // can get left floating when the toolbar is
3005: // re-docked.
3006: //
3007: // We make the frame resizable and then
3008: // remove JToolbar's window listener
3009: // and insert one of our own that first ensures
3010: // the JComboBox's popup window is closed
3011: // and then calls JToolbar's window listener.
3012: if (!frame.isResizable()) {
3013: frame.setResizable(true);
3014: frame
3015: .setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
3016: final EventListener[] l = frame
3017: .getListeners(WindowListener.class);
3018: frame
3019: .removeWindowListener((WindowListener) l[0]);
3020: frame
3021: .addWindowListener(new WindowAdapter() {
3022: public void windowClosing(
3023: WindowEvent e) {
3024: context.hidePopup();
3025: ((WindowListener) l[0])
3026: .windowClosing(e);
3027: }
3028: });
3029: //adjustVerticalSplit = true;
3030: }
3031: leftDocked = false;
3032: } else {
3033: leftDocked = true;
3034: }
3035: }
3036: parent = finalT2.getParent();
3037: if (parent != null) {
3038: if (parent != finalP2) {
3039: while (!(parent instanceof JFrame)) {
3040: parent = parent.getParent();
3041: }
3042: JFrame frame = (JFrame) parent;
3043: debugGui.addTopLevel("Evaluate", frame);
3044: frame.setResizable(true);
3045: rightDocked = false;
3046: } else {
3047: rightDocked = true;
3048: }
3049: }
3050: if (leftDocked && t2Docked && rightDocked && t2Docked) {
3051: // no change
3052: return;
3053: }
3054: t2Docked = rightDocked;
3055: JSplitPane split = (JSplitPane) this Parent;
3056: if (leftDocked) {
3057: if (rightDocked) {
3058: finalSplit.setDividerLocation(0.5);
3059: } else {
3060: finalSplit.setDividerLocation(1.0);
3061: }
3062: if (adjustVerticalSplit) {
3063: split.setDividerLocation(0.66);
3064: }
3065:
3066: } else if (rightDocked) {
3067: finalSplit.setDividerLocation(0.0);
3068: split.setDividerLocation(0.66);
3069: } else {
3070: // both undocked
3071: split.setDividerLocation(1.0);
3072: }
3073: }
3074:
3075: public void componentHidden(ComponentEvent e) {
3076: check(e.getComponent());
3077: }
3078:
3079: public void componentMoved(ComponentEvent e) {
3080: check(e.getComponent());
3081: }
3082:
3083: public void componentResized(ComponentEvent e) {
3084: check(e.getComponent());
3085: }
3086:
3087: public void componentShown(ComponentEvent e) {
3088: check(e.getComponent());
3089: }
3090: };
3091: p1.addContainerListener(new ContainerListener() {
3092: public void componentAdded(ContainerEvent e) {
3093: Component this Parent = finalThis.getParent();
3094: JSplitPane split = (JSplitPane) this Parent;
3095: if (e.getChild() == finalT1) {
3096: if (finalT2.getParent() == finalP2) {
3097: // both docked
3098: finalSplit.setDividerLocation(0.5);
3099: } else {
3100: // left docked only
3101: finalSplit.setDividerLocation(1.0);
3102: }
3103: split.setDividerLocation(0.66);
3104: }
3105: }
3106:
3107: public void componentRemoved(ContainerEvent e) {
3108: Component this Parent = finalThis.getParent();
3109: JSplitPane split = (JSplitPane) this Parent;
3110: if (e.getChild() == finalT1) {
3111: if (finalT2.getParent() == finalP2) {
3112: // right docked only
3113: finalSplit.setDividerLocation(0.0);
3114: split.setDividerLocation(0.66);
3115: } else {
3116: // both undocked
3117: split.setDividerLocation(1.0);
3118: }
3119: }
3120: }
3121: });
3122: t1.addComponentListener(clistener);
3123: t2.addComponentListener(clistener);
3124: disable();
3125: }
3126:
3127: /**
3128: * Disables the component.
3129: */
3130: public void disable() {
3131: context.setEnabled(false);
3132: this Table.setEnabled(false);
3133: localsTable.setEnabled(false);
3134: evaluator.setEnabled(false);
3135: cmdLine.setEnabled(false);
3136: }
3137:
3138: /**
3139: * Enables the component.
3140: */
3141: public void enable() {
3142: context.setEnabled(true);
3143: this Table.setEnabled(true);
3144: localsTable.setEnabled(true);
3145: evaluator.setEnabled(true);
3146: cmdLine.setEnabled(true);
3147: }
3148:
3149: /**
3150: * Disables updating of the component.
3151: */
3152: public void disableUpdate() {
3153: enabled = false;
3154: }
3155:
3156: /**
3157: * Enables updating of the component.
3158: */
3159: public void enableUpdate() {
3160: enabled = true;
3161: }
3162:
3163: // ActionListener
3164:
3165: /**
3166: * Performs an action.
3167: */
3168: public void actionPerformed(ActionEvent e) {
3169: if (!enabled)
3170: return;
3171: if (e.getActionCommand().equals("ContextSwitch")) {
3172: Dim.ContextData contextData = debugGui.dim
3173: .currentContextData();
3174: if (contextData == null) {
3175: return;
3176: }
3177: int frameIndex = context.getSelectedIndex();
3178: context.setToolTipText(toolTips.elementAt(frameIndex)
3179: .toString());
3180: int frameCount = contextData.frameCount();
3181: if (frameIndex >= frameCount) {
3182: return;
3183: }
3184: Dim.StackFrame frame = contextData.getFrame(frameIndex);
3185: Object scope = frame.scope();
3186: Object this Obj = frame.this Obj();
3187: this Table
3188: .resetTree(new VariableModel(debugGui.dim, this Obj));
3189: VariableModel scopeModel;
3190: if (scope != this Obj) {
3191: scopeModel = new VariableModel(debugGui.dim, scope);
3192: } else {
3193: scopeModel = new VariableModel();
3194: }
3195: localsTable.resetTree(scopeModel);
3196: debugGui.dim.contextSwitch(frameIndex);
3197: debugGui.showStopLine(frame);
3198: tableModel.updateModel();
3199: }
3200: }
3201: }
3202:
3203: /**
3204: * The debugger frame menu bar.
3205: */
3206: class Menubar extends JMenuBar implements ActionListener {
3207:
3208: /**
3209: * Serializable magic number.
3210: */
3211: private static final long serialVersionUID = 3217170497245911461L;
3212:
3213: /**
3214: * Items that are enabled only when interrupted.
3215: */
3216: private Vector interruptOnlyItems = new Vector();
3217:
3218: /**
3219: * Items that are enabled only when running.
3220: */
3221: private Vector runOnlyItems = new Vector();
3222:
3223: /**
3224: * The debugger GUI.
3225: */
3226: private SwingGui debugGui;
3227:
3228: /**
3229: * The menu listing the internal frames.
3230: */
3231: private JMenu windowMenu;
3232:
3233: /**
3234: * The "Break on exceptions" menu item.
3235: */
3236: private JCheckBoxMenuItem breakOnExceptions;
3237:
3238: /**
3239: * The "Break on enter" menu item.
3240: */
3241: private JCheckBoxMenuItem breakOnEnter;
3242:
3243: /**
3244: * The "Break on return" menu item.
3245: */
3246: private JCheckBoxMenuItem breakOnReturn;
3247:
3248: /**
3249: * Creates a new Menubar.
3250: */
3251: Menubar(SwingGui debugGui) {
3252: super ();
3253: this .debugGui = debugGui;
3254: String[] fileItems = { "Open...", "Run...", "", "Exit" };
3255: String[] fileCmds = { "Open", "Load", "", "Exit" };
3256: char[] fileShortCuts = { '0', 'N', 0, 'X' };
3257: int[] fileAccelerators = { KeyEvent.VK_O, KeyEvent.VK_N, 0,
3258: KeyEvent.VK_Q };
3259: String[] editItems = { "Cut", "Copy", "Paste",
3260: "Go to function..." };
3261: char[] editShortCuts = { 'T', 'C', 'P', 'F' };
3262: String[] debugItems = { "Break", "Go", "Step Into",
3263: "Step Over", "Step Out" };
3264: char[] debugShortCuts = { 'B', 'G', 'I', 'O', 'T' };
3265: String[] plafItems = { "Metal", "Windows", "Motif" };
3266: char[] plafShortCuts = { 'M', 'W', 'F' };
3267: int[] debugAccelerators = { KeyEvent.VK_PAUSE, KeyEvent.VK_F5,
3268: KeyEvent.VK_F11, KeyEvent.VK_F7, KeyEvent.VK_F8, 0, 0 };
3269:
3270: JMenu fileMenu = new JMenu("File");
3271: fileMenu.setMnemonic('F');
3272: JMenu editMenu = new JMenu("Edit");
3273: editMenu.setMnemonic('E');
3274: JMenu plafMenu = new JMenu("Platform");
3275: plafMenu.setMnemonic('P');
3276: JMenu debugMenu = new JMenu("Debug");
3277: debugMenu.setMnemonic('D');
3278: windowMenu = new JMenu("Window");
3279: windowMenu.setMnemonic('W');
3280: for (int i = 0; i < fileItems.length; ++i) {
3281: if (fileItems[i].length() == 0) {
3282: fileMenu.addSeparator();
3283: } else {
3284: JMenuItem item = new JMenuItem(fileItems[i],
3285: fileShortCuts[i]);
3286: item.setActionCommand(fileCmds[i]);
3287: item.addActionListener(this );
3288: fileMenu.add(item);
3289: if (fileAccelerators[i] != 0) {
3290: KeyStroke k = KeyStroke.getKeyStroke(
3291: fileAccelerators[i], Event.CTRL_MASK);
3292: item.setAccelerator(k);
3293: }
3294: }
3295: }
3296: for (int i = 0; i < editItems.length; ++i) {
3297: JMenuItem item = new JMenuItem(editItems[i],
3298: editShortCuts[i]);
3299: item.addActionListener(this );
3300: editMenu.add(item);
3301: }
3302: for (int i = 0; i < plafItems.length; ++i) {
3303: JMenuItem item = new JMenuItem(plafItems[i],
3304: plafShortCuts[i]);
3305: item.addActionListener(this );
3306: plafMenu.add(item);
3307: }
3308: for (int i = 0; i < debugItems.length; ++i) {
3309: JMenuItem item = new JMenuItem(debugItems[i],
3310: debugShortCuts[i]);
3311: item.addActionListener(this );
3312: if (debugAccelerators[i] != 0) {
3313: KeyStroke k = KeyStroke.getKeyStroke(
3314: debugAccelerators[i], 0);
3315: item.setAccelerator(k);
3316: }
3317: if (i != 0) {
3318: interruptOnlyItems.add(item);
3319: } else {
3320: runOnlyItems.add(item);
3321: }
3322: debugMenu.add(item);
3323: }
3324: breakOnExceptions = new JCheckBoxMenuItem("Break on Exceptions");
3325: breakOnExceptions.setMnemonic('X');
3326: breakOnExceptions.addActionListener(this );
3327: breakOnExceptions.setSelected(false);
3328: debugMenu.add(breakOnExceptions);
3329:
3330: breakOnEnter = new JCheckBoxMenuItem("Break on Function Enter");
3331: breakOnEnter.setMnemonic('E');
3332: breakOnEnter.addActionListener(this );
3333: breakOnEnter.setSelected(false);
3334: debugMenu.add(breakOnEnter);
3335:
3336: breakOnReturn = new JCheckBoxMenuItem(
3337: "Break on Function Return");
3338: breakOnReturn.setMnemonic('R');
3339: breakOnReturn.addActionListener(this );
3340: breakOnReturn.setSelected(false);
3341: debugMenu.add(breakOnReturn);
3342:
3343: add(fileMenu);
3344: add(editMenu);
3345: //add(plafMenu);
3346: add(debugMenu);
3347: JMenuItem item;
3348: windowMenu.add(item = new JMenuItem("Cascade", 'A'));
3349: item.addActionListener(this );
3350: windowMenu.add(item = new JMenuItem("Tile", 'T'));
3351: item.addActionListener(this );
3352: windowMenu.addSeparator();
3353: windowMenu.add(item = new JMenuItem("Console", 'C'));
3354: item.addActionListener(this );
3355: add(windowMenu);
3356:
3357: updateEnabled(false);
3358: }
3359:
3360: /**
3361: * Returns the "Break on exceptions" menu item.
3362: */
3363: public JCheckBoxMenuItem getBreakOnExceptions() {
3364: return breakOnExceptions;
3365: }
3366:
3367: /**
3368: * Returns the "Break on enter" menu item.
3369: */
3370: public JCheckBoxMenuItem getBreakOnEnter() {
3371: return breakOnEnter;
3372: }
3373:
3374: /**
3375: * Returns the "Break on return" menu item.
3376: */
3377: public JCheckBoxMenuItem getBreakOnReturn() {
3378: return breakOnReturn;
3379: }
3380:
3381: /**
3382: * Returns the "Debug" menu.
3383: */
3384: public JMenu getDebugMenu() {
3385: return getMenu(2);
3386: }
3387:
3388: // ActionListener
3389:
3390: /**
3391: * Performs an action.
3392: */
3393: public void actionPerformed(ActionEvent e) {
3394: String cmd = e.getActionCommand();
3395: String plaf_name = null;
3396: if (cmd.equals("Metal")) {
3397: plaf_name = "javax.swing.plaf.metal.MetalLookAndFeel";
3398: } else if (cmd.equals("Windows")) {
3399: plaf_name = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
3400: } else if (cmd.equals("Motif")) {
3401: plaf_name = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
3402: } else {
3403: Object source = e.getSource();
3404: if (source == breakOnExceptions) {
3405: debugGui.dim.setBreakOnExceptions(breakOnExceptions
3406: .isSelected());
3407: } else if (source == breakOnEnter) {
3408: debugGui.dim.setBreakOnEnter(breakOnEnter.isSelected());
3409: } else if (source == breakOnReturn) {
3410: debugGui.dim.setBreakOnReturn(breakOnReturn
3411: .isSelected());
3412: } else {
3413: debugGui.actionPerformed(e);
3414: }
3415: return;
3416: }
3417: try {
3418: UIManager.setLookAndFeel(plaf_name);
3419: SwingUtilities.updateComponentTreeUI(debugGui);
3420: SwingUtilities.updateComponentTreeUI(debugGui.dlg);
3421: } catch (Exception ignored) {
3422: //ignored.printStackTrace();
3423: }
3424: }
3425:
3426: /**
3427: * Adds a file to the window menu.
3428: */
3429: public void addFile(String url) {
3430: int count = windowMenu.getItemCount();
3431: JMenuItem item;
3432: if (count == 4) {
3433: windowMenu.addSeparator();
3434: count++;
3435: }
3436: JMenuItem lastItem = windowMenu.getItem(count - 1);
3437: boolean hasMoreWin = false;
3438: int maxWin = 5;
3439: if (lastItem != null
3440: && lastItem.getText().equals("More Windows...")) {
3441: hasMoreWin = true;
3442: maxWin++;
3443: }
3444: if (!hasMoreWin && count - 4 == 5) {
3445: windowMenu
3446: .add(item = new JMenuItem("More Windows...", 'M'));
3447: item.setActionCommand("More Windows...");
3448: item.addActionListener(this );
3449: return;
3450: } else if (count - 4 <= maxWin) {
3451: if (hasMoreWin) {
3452: count--;
3453: windowMenu.remove(lastItem);
3454: }
3455: String shortName = SwingGui.getShortName(url);
3456:
3457: windowMenu.add(item = new JMenuItem(
3458: (char) ('0' + (count - 4)) + " " + shortName,
3459: '0' + (count - 4)));
3460: if (hasMoreWin) {
3461: windowMenu.add(lastItem);
3462: }
3463: } else {
3464: return;
3465: }
3466: item.setActionCommand(url);
3467: item.addActionListener(this );
3468: }
3469:
3470: /**
3471: * Updates the enabledness of menu items.
3472: */
3473: public void updateEnabled(boolean interrupted) {
3474: for (int i = 0; i != interruptOnlyItems.size(); ++i) {
3475: JMenuItem item = (JMenuItem) interruptOnlyItems
3476: .elementAt(i);
3477: item.setEnabled(interrupted);
3478: }
3479:
3480: for (int i = 0; i != runOnlyItems.size(); ++i) {
3481: JMenuItem item = (JMenuItem) runOnlyItems.elementAt(i);
3482: item.setEnabled(!interrupted);
3483: }
3484: }
3485: }
3486:
3487: /**
3488: * Class to consolidate all cases that require to implement Runnable
3489: * to avoid class generation bloat.
3490: */
3491: class RunProxy implements Runnable {
3492:
3493: // Constants for 'type'.
3494: static final int OPEN_FILE = 1;
3495: static final int LOAD_FILE = 2;
3496: static final int UPDATE_SOURCE_TEXT = 3;
3497: static final int ENTER_INTERRUPT = 4;
3498:
3499: /**
3500: * The debugger GUI.
3501: */
3502: private SwingGui debugGui;
3503:
3504: /**
3505: * The type of Runnable this object is. Takes one of the constants
3506: * defined in this class.
3507: */
3508: private int type;
3509:
3510: /**
3511: * The name of the file to open or load.
3512: */
3513: String fileName;
3514:
3515: /**
3516: * The source text to update.
3517: */
3518: String text;
3519:
3520: /**
3521: * The source for which to update the text.
3522: */
3523: Dim.SourceInfo sourceInfo;
3524:
3525: /**
3526: * The frame to interrupt in.
3527: */
3528: Dim.StackFrame lastFrame;
3529:
3530: /**
3531: * The name of the interrupted thread.
3532: */
3533: String threadTitle;
3534:
3535: /**
3536: * The message of the exception thrown that caused the thread
3537: * interruption, if any.
3538: */
3539: String alertMessage;
3540:
3541: /**
3542: * Creates a new RunProxy.
3543: */
3544: public RunProxy(SwingGui debugGui, int type) {
3545: this .debugGui = debugGui;
3546: this .type = type;
3547: }
3548:
3549: /**
3550: * Runs this Runnable.
3551: */
3552: public void run() {
3553: switch (type) {
3554: case OPEN_FILE:
3555: try {
3556: debugGui.dim.compileScript(fileName, text);
3557: } catch (RuntimeException ex) {
3558: MessageDialogWrapper.showMessageDialog(debugGui, ex
3559: .getMessage(), "Error Compiling " + fileName,
3560: JOptionPane.ERROR_MESSAGE);
3561: }
3562: break;
3563:
3564: case LOAD_FILE:
3565: try {
3566: debugGui.dim.evalScript(fileName, text);
3567: } catch (RuntimeException ex) {
3568: MessageDialogWrapper.showMessageDialog(debugGui, ex
3569: .getMessage(), "Run error for " + fileName,
3570: JOptionPane.ERROR_MESSAGE);
3571: }
3572: break;
3573:
3574: case UPDATE_SOURCE_TEXT: {
3575: String fileName = sourceInfo.url();
3576: if (!debugGui.updateFileWindow(sourceInfo)
3577: && !fileName.equals("<stdin>")) {
3578: debugGui.createFileWindow(sourceInfo, -1);
3579: }
3580: }
3581: break;
3582:
3583: case ENTER_INTERRUPT:
3584: debugGui.enterInterruptImpl(lastFrame, threadTitle,
3585: alertMessage);
3586: break;
3587:
3588: default:
3589: throw new IllegalArgumentException(String.valueOf(type));
3590:
3591: }
3592: }
3593: }
|