aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java')
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java3547
1 files changed, 3547 insertions, 0 deletions
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java
new file mode 100644
index 0000000..61dc065
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java
@@ -0,0 +1,3547 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino JavaScript Debugger code, released
+ * November 21, 2000.
+ *
+ * The Initial Developer of the Original Code is
+ * SeeBeyond Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Matt Gould
+ * Cameron McCormack
+ * Christopher Oliver
+ * Hannes Wallnoefer
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.debugger;
+
+import javax.swing.*;
+import javax.swing.text.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+import java.awt.*;
+import java.awt.event.*;
+
+import java.util.*;
+import java.io.*;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreePath;
+import java.lang.reflect.Method;
+
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.SecurityUtilities;
+
+import org.mozilla.javascript.tools.shell.ConsoleTextArea;
+
+import org.mozilla.javascript.tools.debugger.downloaded.JTreeTable;
+import org.mozilla.javascript.tools.debugger.downloaded.TreeTableModel;
+import org.mozilla.javascript.tools.debugger.downloaded.TreeTableModelAdapter;
+
+/**
+ * GUI for the Rhino debugger.
+ */
+public class SwingGui extends JFrame implements GuiCallback {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -8217029773456711621L;
+
+ /**
+ * The debugger.
+ */
+ Dim dim;
+
+ /**
+ * The action to run when the 'Exit' menu item is chosen or the
+ * frame is closed.
+ */
+ private Runnable exitAction;
+
+ /**
+ * The {@link JDesktopPane} that holds the script windows.
+ */
+ private JDesktopPane desk;
+
+ /**
+ * The {@link JPanel} that shows information about the context.
+ */
+ private ContextWindow context;
+
+ /**
+ * The menu bar.
+ */
+ private Menubar menubar;
+
+ /**
+ * The tool bar.
+ */
+ private JToolBar toolBar;
+
+ /**
+ * The console that displays I/O from the script.
+ */
+ private JSInternalConsole console;
+
+ /**
+ * The {@link JSplitPane} that separates {@link #desk} from
+ * {@link org.mozilla.javascript.Context}.
+ */
+ private JSplitPane split1;
+
+ /**
+ * The status bar.
+ */
+ private JLabel statusBar;
+
+ /**
+ * Hash table of internal frame names to the internal frames themselves.
+ */
+ private Hashtable toplevels = new Hashtable();
+
+ /**
+ * Hash table of script URLs to their internal frames.
+ */
+ private Hashtable fileWindows = new Hashtable();
+
+ /**
+ * The {@link FileWindow} that last had the focus.
+ */
+ private FileWindow currentWindow;
+
+ /**
+ * File choose dialog for loading a script.
+ */
+ JFileChooser dlg;
+
+ /**
+ * The AWT EventQueue. Used for manually pumping AWT events from
+ * {@link #dispatchNextGuiEvent()}.
+ */
+ private EventQueue awtEventQueue;
+
+ /**
+ * Creates a new SwingGui.
+ */
+ public SwingGui(Dim dim, String title) {
+ super(title);
+ this.dim = dim;
+ init();
+ dim.setGuiCallback(this);
+ }
+
+ /**
+ * Returns the Menubar of this debugger frame.
+ */
+ public Menubar getMenubar() {
+ return menubar;
+ }
+
+ /**
+ * Sets the {@link Runnable} that will be run when the "Exit" menu
+ * item is chosen.
+ */
+ public void setExitAction(Runnable r) {
+ exitAction = r;
+ }
+
+ /**
+ * Returns the debugger console component.
+ */
+ public JSInternalConsole getConsole() {
+ return console;
+ }
+
+ /**
+ * Sets the visibility of the debugger GUI.
+ */
+ public void setVisible(boolean b) {
+ super.setVisible(b);
+ if (b) {
+ // this needs to be done after the window is visible
+ console.consoleTextArea.requestFocus();
+ context.split.setDividerLocation(0.5);
+ try {
+ console.setMaximum(true);
+ console.setSelected(true);
+ console.show();
+ console.consoleTextArea.requestFocus();
+ } catch (Exception exc) {
+ }
+ }
+ }
+
+ /**
+ * Records a new internal frame.
+ */
+ void addTopLevel(String key, JFrame frame) {
+ if (frame != this) {
+ toplevels.put(key, frame);
+ }
+ }
+
+ /**
+ * Constructs the debugger GUI.
+ */
+ private void init() {
+ menubar = new Menubar(this);
+ setJMenuBar(menubar);
+ toolBar = new JToolBar();
+ JButton button;
+ JButton breakButton, goButton, stepIntoButton,
+ stepOverButton, stepOutButton;
+ String [] toolTips = {"Break (Pause)",
+ "Go (F5)",
+ "Step Into (F11)",
+ "Step Over (F7)",
+ "Step Out (F8)"};
+ int count = 0;
+ button = breakButton = new JButton("Break");
+ button.setToolTipText("Break");
+ button.setActionCommand("Break");
+ button.addActionListener(menubar);
+ button.setEnabled(true);
+ button.setToolTipText(toolTips[count++]);
+
+ button = goButton = new JButton("Go");
+ button.setToolTipText("Go");
+ button.setActionCommand("Go");
+ button.addActionListener(menubar);
+ button.setEnabled(false);
+ button.setToolTipText(toolTips[count++]);
+
+ button = stepIntoButton = new JButton("Step Into");
+ button.setToolTipText("Step Into");
+ button.setActionCommand("Step Into");
+ button.addActionListener(menubar);
+ button.setEnabled(false);
+ button.setToolTipText(toolTips[count++]);
+
+ button = stepOverButton = new JButton("Step Over");
+ button.setToolTipText("Step Over");
+ button.setActionCommand("Step Over");
+ button.setEnabled(false);
+ button.addActionListener(menubar);
+ button.setToolTipText(toolTips[count++]);
+
+ button = stepOutButton = new JButton("Step Out");
+ button.setToolTipText("Step Out");
+ button.setActionCommand("Step Out");
+ button.setEnabled(false);
+ button.addActionListener(menubar);
+ button.setToolTipText(toolTips[count++]);
+
+ Dimension dim = stepOverButton.getPreferredSize();
+ breakButton.setPreferredSize(dim);
+ breakButton.setMinimumSize(dim);
+ breakButton.setMaximumSize(dim);
+ breakButton.setSize(dim);
+ goButton.setPreferredSize(dim);
+ goButton.setMinimumSize(dim);
+ goButton.setMaximumSize(dim);
+ stepIntoButton.setPreferredSize(dim);
+ stepIntoButton.setMinimumSize(dim);
+ stepIntoButton.setMaximumSize(dim);
+ stepOverButton.setPreferredSize(dim);
+ stepOverButton.setMinimumSize(dim);
+ stepOverButton.setMaximumSize(dim);
+ stepOutButton.setPreferredSize(dim);
+ stepOutButton.setMinimumSize(dim);
+ stepOutButton.setMaximumSize(dim);
+ toolBar.add(breakButton);
+ toolBar.add(goButton);
+ toolBar.add(stepIntoButton);
+ toolBar.add(stepOverButton);
+ toolBar.add(stepOutButton);
+
+ JPanel contentPane = new JPanel();
+ contentPane.setLayout(new BorderLayout());
+ getContentPane().add(toolBar, BorderLayout.NORTH);
+ getContentPane().add(contentPane, BorderLayout.CENTER);
+ desk = new JDesktopPane();
+ desk.setPreferredSize(new Dimension(600, 300));
+ desk.setMinimumSize(new Dimension(150, 50));
+ desk.add(console = new JSInternalConsole("JavaScript Console"));
+ context = new ContextWindow(this);
+ context.setPreferredSize(new Dimension(600, 120));
+ context.setMinimumSize(new Dimension(50, 50));
+
+ split1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, desk,
+ context);
+ split1.setOneTouchExpandable(true);
+ SwingGui.setResizeWeight(split1, 0.66);
+ contentPane.add(split1, BorderLayout.CENTER);
+ statusBar = new JLabel();
+ statusBar.setText("Thread: ");
+ contentPane.add(statusBar, BorderLayout.SOUTH);
+ dlg = new JFileChooser();
+
+ javax.swing.filechooser.FileFilter filter =
+ new javax.swing.filechooser.FileFilter() {
+ public boolean accept(File f) {
+ if (f.isDirectory()) {
+ return true;
+ }
+ String n = f.getName();
+ int i = n.lastIndexOf('.');
+ if (i > 0 && i < n.length() -1) {
+ String ext = n.substring(i + 1).toLowerCase();
+ if (ext.equals("js")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getDescription() {
+ return "JavaScript Files (*.js)";
+ }
+ };
+ dlg.addChoosableFileFilter(filter);
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ exit();
+ }
+ });
+ }
+
+ /**
+ * Runs the {@link #exitAction}.
+ */
+ private void exit() {
+ if (exitAction != null) {
+ SwingUtilities.invokeLater(exitAction);
+ }
+ dim.setReturnValue(Dim.EXIT);
+ }
+
+ /**
+ * Returns the {@link FileWindow} for the given URL.
+ */
+ FileWindow getFileWindow(String url) {
+ if (url == null || url.equals("<stdin>")) {
+ return null;
+ }
+ return (FileWindow)fileWindows.get(url);
+ }
+
+ /**
+ * Returns a short version of the given URL.
+ */
+ static String getShortName(String url) {
+ int lastSlash = url.lastIndexOf('/');
+ if (lastSlash < 0) {
+ lastSlash = url.lastIndexOf('\\');
+ }
+ String shortName = url;
+ if (lastSlash >= 0 && lastSlash + 1 < url.length()) {
+ shortName = url.substring(lastSlash + 1);
+ }
+ return shortName;
+ }
+
+ /**
+ * Closes the given {@link FileWindow}.
+ */
+ void removeWindow(FileWindow w) {
+ fileWindows.remove(w.getUrl());
+ JMenu windowMenu = getWindowMenu();
+ int count = windowMenu.getItemCount();
+ JMenuItem lastItem = windowMenu.getItem(count -1);
+ String name = getShortName(w.getUrl());
+ for (int i = 5; i < count; i++) {
+ JMenuItem item = windowMenu.getItem(i);
+ if (item == null) continue; // separator
+ String text = item.getText();
+ //1 D:\foo.js
+ //2 D:\bar.js
+ int pos = text.indexOf(' ');
+ if (text.substring(pos + 1).equals(name)) {
+ windowMenu.remove(item);
+ // Cascade [0]
+ // Tile [1]
+ // ------- [2]
+ // Console [3]
+ // ------- [4]
+ if (count == 6) {
+ // remove the final separator
+ windowMenu.remove(4);
+ } else {
+ int j = i - 4;
+ for (;i < count -1; i++) {
+ JMenuItem thisItem = windowMenu.getItem(i);
+ if (thisItem != null) {
+ //1 D:\foo.js
+ //2 D:\bar.js
+ text = thisItem.getText();
+ if (text.equals("More Windows...")) {
+ break;
+ } else {
+ pos = text.indexOf(' ');
+ thisItem.setText((char)('0' + j) + " " +
+ text.substring(pos + 1));
+ thisItem.setMnemonic('0' + j);
+ j++;
+ }
+ }
+ }
+ if (count - 6 == 0 && lastItem != item) {
+ if (lastItem.getText().equals("More Windows...")) {
+ windowMenu.remove(lastItem);
+ }
+ }
+ }
+ break;
+ }
+ }
+ windowMenu.revalidate();
+ }
+
+ /**
+ * Shows the line at which execution in the given stack frame just stopped.
+ */
+ void showStopLine(Dim.StackFrame frame) {
+ String sourceName = frame.getUrl();
+ if (sourceName == null || sourceName.equals("<stdin>")) {
+ if (console.isVisible()) {
+ console.show();
+ }
+ } else {
+ showFileWindow(sourceName, -1);
+ int lineNumber = frame.getLineNumber();
+ FileWindow w = getFileWindow(sourceName);
+ if (w != null) {
+ setFilePosition(w, lineNumber);
+ }
+ }
+ }
+
+ /**
+ * Shows a {@link FileWindow} for the given source, creating it
+ * if it doesn't exist yet. if <code>lineNumber</code> is greater
+ * than -1, it indicates the line number to select and display.
+ * @param sourceUrl the source URL
+ * @param lineNumber the line number to select, or -1
+ */
+ protected void showFileWindow(String sourceUrl, int lineNumber) {
+ FileWindow w = getFileWindow(sourceUrl);
+ if (w == null) {
+ Dim.SourceInfo si = dim.sourceInfo(sourceUrl);
+ createFileWindow(si, -1);
+ w = getFileWindow(sourceUrl);
+ }
+ if (lineNumber > -1) {
+ int start = w.getPosition(lineNumber-1);
+ int end = w.getPosition(lineNumber)-1;
+ w.textArea.select(start);
+ w.textArea.setCaretPosition(start);
+ w.textArea.moveCaretPosition(end);
+ }
+ try {
+ if (w.isIcon()) {
+ w.setIcon(false);
+ }
+ w.setVisible(true);
+ w.moveToFront();
+ w.setSelected(true);
+ requestFocus();
+ w.requestFocus();
+ w.textArea.requestFocus();
+ } catch (Exception exc) {
+ }
+ }
+
+ /**
+ * Creates and shows a new {@link FileWindow} for the given source.
+ */
+ protected void createFileWindow(Dim.SourceInfo sourceInfo, int line) {
+ boolean activate = true;
+
+ String url = sourceInfo.url();
+ FileWindow w = new FileWindow(this, sourceInfo);
+ fileWindows.put(url, w);
+ if (line != -1) {
+ if (currentWindow != null) {
+ currentWindow.setPosition(-1);
+ }
+ try {
+ w.setPosition(w.textArea.getLineStartOffset(line-1));
+ } catch (BadLocationException exc) {
+ try {
+ w.setPosition(w.textArea.getLineStartOffset(0));
+ } catch (BadLocationException ee) {
+ w.setPosition(-1);
+ }
+ }
+ }
+ desk.add(w);
+ if (line != -1) {
+ currentWindow = w;
+ }
+ menubar.addFile(url);
+ w.setVisible(true);
+
+ if (activate) {
+ try {
+ w.setMaximum(true);
+ w.setSelected(true);
+ w.moveToFront();
+ } catch (Exception exc) {
+ }
+ }
+ }
+
+ /**
+ * Update the source text for <code>sourceInfo</code>. This returns true
+ * if a {@link FileWindow} for the given source exists and could be updated.
+ * Otherwise, this does nothing and returns false.
+ * @param sourceInfo the source info
+ * @return true if a {@link FileWindow} for the given source exists
+ * and could be updated, false otherwise.
+ */
+ protected boolean updateFileWindow(Dim.SourceInfo sourceInfo) {
+ String fileName = sourceInfo.url();
+ FileWindow w = getFileWindow(fileName);
+ if (w != null) {
+ w.updateText(sourceInfo);
+ w.show();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Moves the current position in the given {@link FileWindow} to the
+ * given line.
+ */
+ private void setFilePosition(FileWindow w, int line) {
+ boolean activate = true;
+ JTextArea ta = w.textArea;
+ try {
+ if (line == -1) {
+ w.setPosition(-1);
+ if (currentWindow == w) {
+ currentWindow = null;
+ }
+ } else {
+ int loc = ta.getLineStartOffset(line-1);
+ if (currentWindow != null && currentWindow != w) {
+ currentWindow.setPosition(-1);
+ }
+ w.setPosition(loc);
+ currentWindow = w;
+ }
+ } catch (BadLocationException exc) {
+ // fix me
+ }
+ if (activate) {
+ if (w.isIcon()) {
+ desk.getDesktopManager().deiconifyFrame(w);
+ }
+ desk.getDesktopManager().activateFrame(w);
+ try {
+ w.show();
+ w.toFront(); // required for correct frame layering (JDK 1.4.1)
+ w.setSelected(true);
+ } catch (Exception exc) {
+ }
+ }
+ }
+
+ /**
+ * Handles script interruption.
+ */
+ void enterInterruptImpl(Dim.StackFrame lastFrame,
+ String threadTitle, String alertMessage) {
+ statusBar.setText("Thread: " + threadTitle);
+
+ showStopLine(lastFrame);
+
+ if (alertMessage != null) {
+ MessageDialogWrapper.showMessageDialog(this,
+ alertMessage,
+ "Exception in Script",
+ JOptionPane.ERROR_MESSAGE);
+ }
+
+ updateEnabled(true);
+
+ Dim.ContextData contextData = lastFrame.contextData();
+
+ JComboBox ctx = context.context;
+ Vector toolTips = context.toolTips;
+ context.disableUpdate();
+ int frameCount = contextData.frameCount();
+ ctx.removeAllItems();
+ // workaround for JDK 1.4 bug that caches selected value even after
+ // removeAllItems() is called
+ ctx.setSelectedItem(null);
+ toolTips.removeAllElements();
+ for (int i = 0; i < frameCount; i++) {
+ Dim.StackFrame frame = contextData.getFrame(i);
+ String url = frame.getUrl();
+ int lineNumber = frame.getLineNumber();
+ String shortName = url;
+ if (url.length() > 20) {
+ shortName = "..." + url.substring(url.length() - 17);
+ }
+ String location = "\"" + shortName + "\", line " + lineNumber;
+ ctx.insertItemAt(location, i);
+ location = "\"" + url + "\", line " + lineNumber;
+ toolTips.addElement(location);
+ }
+ context.enableUpdate();
+ ctx.setSelectedIndex(0);
+ ctx.setMinimumSize(new Dimension(50, ctx.getMinimumSize().height));
+ }
+
+ /**
+ * Returns the 'Window' menu.
+ */
+ private JMenu getWindowMenu() {
+ return menubar.getMenu(3);
+ }
+
+ /**
+ * Displays a {@link JFileChooser} and returns the selected filename.
+ */
+ private String chooseFile(String title) {
+ dlg.setDialogTitle(title);
+ File CWD = null;
+ String dir = SecurityUtilities.getSystemProperty("user.dir");
+ if (dir != null) {
+ CWD = new File(dir);
+ }
+ if (CWD != null) {
+ dlg.setCurrentDirectory(CWD);
+ }
+ int returnVal = dlg.showOpenDialog(this);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ try {
+ String result = dlg.getSelectedFile().getCanonicalPath();
+ CWD = dlg.getSelectedFile().getParentFile();
+ Properties props = System.getProperties();
+ props.put("user.dir", CWD.getPath());
+ System.setProperties(props);
+ return result;
+ } catch (IOException ignored) {
+ } catch (SecurityException ignored) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the current selected internal frame.
+ */
+ private JInternalFrame getSelectedFrame() {
+ JInternalFrame[] frames = desk.getAllFrames();
+ for (int i = 0; i < frames.length; i++) {
+ if (frames[i].isShowing()) {
+ return frames[i];
+ }
+ }
+ return frames[frames.length - 1];
+ }
+
+ /**
+ * Enables or disables the menu and tool bars with respect to the
+ * state of script execution.
+ */
+ private void updateEnabled(boolean interrupted) {
+ ((Menubar)getJMenuBar()).updateEnabled(interrupted);
+ for (int ci = 0, cc = toolBar.getComponentCount(); ci < cc; ci++) {
+ boolean enableButton;
+ if (ci == 0) {
+ // Break
+ enableButton = !interrupted;
+ } else {
+ enableButton = interrupted;
+ }
+ toolBar.getComponent(ci).setEnabled(enableButton);
+ }
+ if (interrupted) {
+ toolBar.setEnabled(true);
+ // raise the debugger window
+ int state = getExtendedState();
+ if (state == Frame.ICONIFIED) {
+ setExtendedState(Frame.NORMAL);
+ }
+ toFront();
+ context.enable();
+ } else {
+ if (currentWindow != null) currentWindow.setPosition(-1);
+ context.disable();
+ }
+ }
+
+ /**
+ * Calls {@link JSplitPane#setResizeWeight} via reflection.
+ * For compatibility, since JDK &lt; 1.3 does not have this method.
+ */
+ static void setResizeWeight(JSplitPane pane, double weight) {
+ try {
+ Method m = JSplitPane.class.getMethod("setResizeWeight",
+ new Class[]{double.class});
+ m.invoke(pane, new Object[]{new Double(weight)});
+ } catch (NoSuchMethodException exc) {
+ } catch (IllegalAccessException exc) {
+ } catch (java.lang.reflect.InvocationTargetException exc) {
+ }
+ }
+
+ /**
+ * Reads the file with the given name and returns its contents as a String.
+ */
+ private String readFile(String fileName) {
+ String text;
+ try {
+ Reader r = new FileReader(fileName);
+ try {
+ text = Kit.readReader(r);
+ } finally {
+ r.close();
+ }
+ } catch (IOException ex) {
+ MessageDialogWrapper.showMessageDialog(this,
+ ex.getMessage(),
+ "Error reading "+fileName,
+ JOptionPane.ERROR_MESSAGE);
+ text = null;
+ }
+ return text;
+ }
+
+ // GuiCallback
+
+ /**
+ * Called when the source text for a script has been updated.
+ */
+ public void updateSourceText(Dim.SourceInfo sourceInfo) {
+ RunProxy proxy = new RunProxy(this, RunProxy.UPDATE_SOURCE_TEXT);
+ proxy.sourceInfo = sourceInfo;
+ SwingUtilities.invokeLater(proxy);
+ }
+
+ /**
+ * Called when the interrupt loop has been entered.
+ */
+ public void enterInterrupt(Dim.StackFrame lastFrame,
+ String threadTitle,
+ String alertMessage) {
+ if (SwingUtilities.isEventDispatchThread()) {
+ enterInterruptImpl(lastFrame, threadTitle, alertMessage);
+ } else {
+ RunProxy proxy = new RunProxy(this, RunProxy.ENTER_INTERRUPT);
+ proxy.lastFrame = lastFrame;
+ proxy.threadTitle = threadTitle;
+ proxy.alertMessage = alertMessage;
+ SwingUtilities.invokeLater(proxy);
+ }
+ }
+
+ /**
+ * Returns whether the current thread is the GUI event thread.
+ */
+ public boolean isGuiEventThread() {
+ return SwingUtilities.isEventDispatchThread();
+ }
+
+ /**
+ * Processes the next GUI event.
+ */
+ public void dispatchNextGuiEvent() throws InterruptedException {
+ EventQueue queue = awtEventQueue;
+ if (queue == null) {
+ queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
+ awtEventQueue = queue;
+ }
+ AWTEvent event = queue.getNextEvent();
+ if (event instanceof ActiveEvent) {
+ ((ActiveEvent)event).dispatch();
+ } else {
+ Object source = event.getSource();
+ if (source instanceof Component) {
+ Component comp = (Component)source;
+ comp.dispatchEvent(event);
+ } else if (source instanceof MenuComponent) {
+ ((MenuComponent)source).dispatchEvent(event);
+ }
+ }
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action from the menu or toolbar.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ int returnValue = -1;
+ if (cmd.equals("Cut") || cmd.equals("Copy") || cmd.equals("Paste")) {
+ JInternalFrame f = getSelectedFrame();
+ if (f != null && f instanceof ActionListener) {
+ ((ActionListener)f).actionPerformed(e);
+ }
+ } else if (cmd.equals("Step Over")) {
+ returnValue = Dim.STEP_OVER;
+ } else if (cmd.equals("Step Into")) {
+ returnValue = Dim.STEP_INTO;
+ } else if (cmd.equals("Step Out")) {
+ returnValue = Dim.STEP_OUT;
+ } else if (cmd.equals("Go")) {
+ returnValue = Dim.GO;
+ } else if (cmd.equals("Break")) {
+ dim.setBreak();
+ } else if (cmd.equals("Exit")) {
+ exit();
+ } else if (cmd.equals("Open")) {
+ String fileName = chooseFile("Select a file to compile");
+ if (fileName != null) {
+ String text = readFile(fileName);
+ if (text != null) {
+ RunProxy proxy = new RunProxy(this, RunProxy.OPEN_FILE);
+ proxy.fileName = fileName;
+ proxy.text = text;
+ new Thread(proxy).start();
+ }
+ }
+ } else if (cmd.equals("Load")) {
+ String fileName = chooseFile("Select a file to execute");
+ if (fileName != null) {
+ String text = readFile(fileName);
+ if (text != null) {
+ RunProxy proxy = new RunProxy(this, RunProxy.LOAD_FILE);
+ proxy.fileName = fileName;
+ proxy.text = text;
+ new Thread(proxy).start();
+ }
+ }
+ } else if (cmd.equals("More Windows...")) {
+ MoreWindows dlg = new MoreWindows(this, fileWindows,
+ "Window", "Files");
+ dlg.showDialog(this);
+ } else if (cmd.equals("Console")) {
+ if (console.isIcon()) {
+ desk.getDesktopManager().deiconifyFrame(console);
+ }
+ console.show();
+ desk.getDesktopManager().activateFrame(console);
+ console.consoleTextArea.requestFocus();
+ } else if (cmd.equals("Cut")) {
+ } else if (cmd.equals("Copy")) {
+ } else if (cmd.equals("Paste")) {
+ } else if (cmd.equals("Go to function...")) {
+ FindFunction dlg = new FindFunction(this, "Go to function",
+ "Function");
+ dlg.showDialog(this);
+ } else if (cmd.equals("Tile")) {
+ JInternalFrame[] frames = desk.getAllFrames();
+ int count = frames.length;
+ int rows, cols;
+ rows = cols = (int)Math.sqrt(count);
+ if (rows*cols < count) {
+ cols++;
+ if (rows * cols < count) {
+ rows++;
+ }
+ }
+ Dimension size = desk.getSize();
+ int w = size.width/cols;
+ int h = size.height/rows;
+ int x = 0;
+ int y = 0;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cols; j++) {
+ int index = (i*cols) + j;
+ if (index >= frames.length) {
+ break;
+ }
+ JInternalFrame f = frames[index];
+ try {
+ f.setIcon(false);
+ f.setMaximum(false);
+ } catch (Exception exc) {
+ }
+ desk.getDesktopManager().setBoundsForFrame(f, x, y,
+ w, h);
+ x += w;
+ }
+ y += h;
+ x = 0;
+ }
+ } else if (cmd.equals("Cascade")) {
+ JInternalFrame[] frames = desk.getAllFrames();
+ int count = frames.length;
+ int x, y, w, h;
+ x = y = 0;
+ h = desk.getHeight();
+ int d = h / count;
+ if (d > 30) d = 30;
+ for (int i = count -1; i >= 0; i--, x += d, y += d) {
+ JInternalFrame f = frames[i];
+ try {
+ f.setIcon(false);
+ f.setMaximum(false);
+ } catch (Exception exc) {
+ }
+ Dimension dimen = f.getPreferredSize();
+ w = dimen.width;
+ h = dimen.height;
+ desk.getDesktopManager().setBoundsForFrame(f, x, y, w, h);
+ }
+ } else {
+ Object obj = getFileWindow(cmd);
+ if (obj != null) {
+ FileWindow w = (FileWindow)obj;
+ try {
+ if (w.isIcon()) {
+ w.setIcon(false);
+ }
+ w.setVisible(true);
+ w.moveToFront();
+ w.setSelected(true);
+ } catch (Exception exc) {
+ }
+ }
+ }
+ if (returnValue != -1) {
+ updateEnabled(false);
+ dim.setReturnValue(returnValue);
+ }
+ }
+}
+
+/**
+ * Helper class for showing a message dialog.
+ */
+class MessageDialogWrapper {
+
+ /**
+ * Shows a message dialog, wrapping the <code>msg</code> at 60
+ * columns.
+ */
+ public static void showMessageDialog(Component parent, String msg,
+ String title, int flags) {
+ if (msg.length() > 60) {
+ StringBuffer buf = new StringBuffer();
+ int len = msg.length();
+ int j = 0;
+ int i;
+ for (i = 0; i < len; i++, j++) {
+ char c = msg.charAt(i);
+ buf.append(c);
+ if (Character.isWhitespace(c)) {
+ int k;
+ for (k = i + 1; k < len; k++) {
+ if (Character.isWhitespace(msg.charAt(k))) {
+ break;
+ }
+ }
+ if (k < len) {
+ int nextWordLen = k - i;
+ if (j + nextWordLen > 60) {
+ buf.append('\n');
+ j = 0;
+ }
+ }
+ }
+ }
+ msg = buf.toString();
+ }
+ JOptionPane.showMessageDialog(parent, msg, title, flags);
+ }
+}
+
+/**
+ * Extension of JTextArea for script evaluation input.
+ */
+class EvalTextArea
+ extends JTextArea
+ implements KeyListener, DocumentListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -3918033649601064194L;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * History of expressions that have been evaluated
+ */
+ private Vector history;
+
+ /**
+ * Index of the selected history item.
+ */
+ private int historyIndex = -1;
+
+ /**
+ * Position in the display where output should go.
+ */
+ private int outputMark;
+
+ /**
+ * Creates a new EvalTextArea.
+ */
+ public EvalTextArea(SwingGui debugGui) {
+ this.debugGui = debugGui;
+ history = new java.util.Vector();
+ Document doc = getDocument();
+ doc.addDocumentListener(this);
+ addKeyListener(this);
+ setLineWrap(true);
+ setFont(new Font("Monospaced", 0, 12));
+ append("% ");
+ outputMark = doc.getLength();
+ }
+
+ /**
+ * Selects a subrange of the text.
+ */
+ public void select(int start, int end) {
+ //requestFocus();
+ super.select(start, end);
+ }
+
+ /**
+ * Called when Enter is pressed.
+ */
+ private synchronized void returnPressed() {
+ Document doc = getDocument();
+ int len = doc.getLength();
+ Segment segment = new Segment();
+ try {
+ doc.getText(outputMark, len - outputMark, segment);
+ } catch (javax.swing.text.BadLocationException ignored) {
+ ignored.printStackTrace();
+ }
+ String text = segment.toString();
+ if (debugGui.dim.stringIsCompilableUnit(text)) {
+ if (text.trim().length() > 0) {
+ history.addElement(text);
+ historyIndex = history.size();
+ }
+ append("\n");
+ String result = debugGui.dim.eval(text);
+ if (result.length() > 0) {
+ append(result);
+ append("\n");
+ }
+ append("% ");
+ outputMark = doc.getLength();
+ } else {
+ append("\n");
+ }
+ }
+
+ /**
+ * Writes output into the text area.
+ */
+ public synchronized void write(String str) {
+ insert(str, outputMark);
+ int len = str.length();
+ outputMark += len;
+ select(outputMark, outputMark);
+ }
+
+ // KeyListener
+
+ /**
+ * Called when a key is pressed.
+ */
+ public void keyPressed(KeyEvent e) {
+ int code = e.getKeyCode();
+ if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT) {
+ if (outputMark == getCaretPosition()) {
+ e.consume();
+ }
+ } else if (code == KeyEvent.VK_HOME) {
+ int caretPos = getCaretPosition();
+ if (caretPos == outputMark) {
+ e.consume();
+ } else if (caretPos > outputMark) {
+ if (!e.isControlDown()) {
+ if (e.isShiftDown()) {
+ moveCaretPosition(outputMark);
+ } else {
+ setCaretPosition(outputMark);
+ }
+ e.consume();
+ }
+ }
+ } else if (code == KeyEvent.VK_ENTER) {
+ returnPressed();
+ e.consume();
+ } else if (code == KeyEvent.VK_UP) {
+ historyIndex--;
+ if (historyIndex >= 0) {
+ if (historyIndex >= history.size()) {
+ historyIndex = history.size() -1;
+ }
+ if (historyIndex >= 0) {
+ String str = (String)history.elementAt(historyIndex);
+ int len = getDocument().getLength();
+ replaceRange(str, outputMark, len);
+ int caretPos = outputMark + str.length();
+ select(caretPos, caretPos);
+ } else {
+ historyIndex++;
+ }
+ } else {
+ historyIndex++;
+ }
+ e.consume();
+ } else if (code == KeyEvent.VK_DOWN) {
+ int caretPos = outputMark;
+ if (history.size() > 0) {
+ historyIndex++;
+ if (historyIndex < 0) {historyIndex = 0;}
+ int len = getDocument().getLength();
+ if (historyIndex < history.size()) {
+ String str = (String)history.elementAt(historyIndex);
+ replaceRange(str, outputMark, len);
+ caretPos = outputMark + str.length();
+ } else {
+ historyIndex = history.size();
+ replaceRange("", outputMark, len);
+ }
+ }
+ select(caretPos, caretPos);
+ e.consume();
+ }
+ }
+
+ /**
+ * Called when a key is typed.
+ */
+ public void keyTyped(KeyEvent e) {
+ int keyChar = e.getKeyChar();
+ if (keyChar == 0x8 /* KeyEvent.VK_BACK_SPACE */) {
+ if (outputMark == getCaretPosition()) {
+ e.consume();
+ }
+ } else if (getCaretPosition() < outputMark) {
+ setCaretPosition(outputMark);
+ }
+ }
+
+ /**
+ * Called when a key is released.
+ */
+ public synchronized void keyReleased(KeyEvent e) {
+ }
+
+ // DocumentListener
+
+ /**
+ * Called when text was inserted into the text area.
+ */
+ public synchronized void insertUpdate(DocumentEvent e) {
+ int len = e.getLength();
+ int off = e.getOffset();
+ if (outputMark > off) {
+ outputMark += len;
+ }
+ }
+
+ /**
+ * Called when text was removed from the text area.
+ */
+ public synchronized void removeUpdate(DocumentEvent e) {
+ int len = e.getLength();
+ int off = e.getOffset();
+ if (outputMark > off) {
+ if (outputMark >= off + len) {
+ outputMark -= len;
+ } else {
+ outputMark = off;
+ }
+ }
+ }
+
+ /**
+ * Attempts to clean up the damage done by {@link #updateUI()}.
+ */
+ public synchronized void postUpdateUI() {
+ //requestFocus();
+ setCaret(getCaret());
+ select(outputMark, outputMark);
+ }
+
+ /**
+ * Called when text has changed in the text area.
+ */
+ public synchronized void changedUpdate(DocumentEvent e) {
+ }
+}
+
+/**
+ * An internal frame for evaluating script.
+ */
+class EvalWindow extends JInternalFrame implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -2860585845212160176L;
+
+ /**
+ * The text area into which expressions can be typed.
+ */
+ private EvalTextArea evalTextArea;
+
+ /**
+ * Creates a new EvalWindow.
+ */
+ public EvalWindow(String name, SwingGui debugGui) {
+ super(name, true, false, true, true);
+ evalTextArea = new EvalTextArea(debugGui);
+ evalTextArea.setRows(24);
+ evalTextArea.setColumns(80);
+ JScrollPane scroller = new JScrollPane(evalTextArea);
+ setContentPane(scroller);
+ //scroller.setPreferredSize(new Dimension(600, 400));
+ pack();
+ setVisible(true);
+ }
+
+ /**
+ * Sets whether the text area is enabled.
+ */
+ public void setEnabled(boolean b) {
+ super.setEnabled(b);
+ evalTextArea.setEnabled(b);
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action on the text area.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cut")) {
+ evalTextArea.cut();
+ } else if (cmd.equals("Copy")) {
+ evalTextArea.copy();
+ } else if (cmd.equals("Paste")) {
+ evalTextArea.paste();
+ }
+ }
+}
+
+/**
+ * Internal frame for the console.
+ */
+class JSInternalConsole extends JInternalFrame implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -5523468828771087292L;
+
+ /**
+ * Creates a new JSInternalConsole.
+ */
+ public JSInternalConsole(String name) {
+ super(name, true, false, true, true);
+ consoleTextArea = new ConsoleTextArea(null);
+ consoleTextArea.setRows(24);
+ consoleTextArea.setColumns(80);
+ JScrollPane scroller = new JScrollPane(consoleTextArea);
+ setContentPane(scroller);
+ pack();
+ addInternalFrameListener(new InternalFrameAdapter() {
+ public void internalFrameActivated(InternalFrameEvent e) {
+ // hack
+ if (consoleTextArea.hasFocus()) {
+ consoleTextArea.getCaret().setVisible(false);
+ consoleTextArea.getCaret().setVisible(true);
+ }
+ }
+ });
+ }
+
+ /**
+ * The console text area.
+ */
+ ConsoleTextArea consoleTextArea;
+
+ /**
+ * Returns the input stream of the console text area.
+ */
+ public InputStream getIn() {
+ return consoleTextArea.getIn();
+ }
+
+ /**
+ * Returns the output stream of the console text area.
+ */
+ public PrintStream getOut() {
+ return consoleTextArea.getOut();
+ }
+
+ /**
+ * Returns the error stream of the console text area.
+ */
+ public PrintStream getErr() {
+ return consoleTextArea.getErr();
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action on the text area.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cut")) {
+ consoleTextArea.cut();
+ } else if (cmd.equals("Copy")) {
+ consoleTextArea.copy();
+ } else if (cmd.equals("Paste")) {
+ consoleTextArea.paste();
+ }
+ }
+}
+
+/**
+ * Popup menu class for right-clicking on {@link FileTextArea}s.
+ */
+class FilePopupMenu extends JPopupMenu {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 3589525009546013565L;
+
+ /**
+ * The popup x position.
+ */
+ int x;
+
+ /**
+ * The popup y position.
+ */
+ int y;
+
+ /**
+ * Creates a new FilePopupMenu.
+ */
+ public FilePopupMenu(FileTextArea w) {
+ JMenuItem item;
+ add(item = new JMenuItem("Set Breakpoint"));
+ item.addActionListener(w);
+ add(item = new JMenuItem("Clear Breakpoint"));
+ item.addActionListener(w);
+ add(item = new JMenuItem("Run"));
+ item.addActionListener(w);
+ }
+
+ /**
+ * Displays the menu at the given coordinates.
+ */
+ public void show(JComponent comp, int x, int y) {
+ this.x = x;
+ this.y = y;
+ super.show(comp, x, y);
+ }
+}
+
+/**
+ * Text area to display script source.
+ */
+class FileTextArea
+ extends JTextArea
+ implements ActionListener, PopupMenuListener, KeyListener, MouseListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -25032065448563720L;
+
+ /**
+ * The owning {@link FileWindow}.
+ */
+ private FileWindow w;
+
+ /**
+ * The popup menu.
+ */
+ private FilePopupMenu popup;
+
+ /**
+ * Creates a new FileTextArea.
+ */
+ public FileTextArea(FileWindow w) {
+ this.w = w;
+ popup = new FilePopupMenu(this);
+ popup.addPopupMenuListener(this);
+ addMouseListener(this);
+ addKeyListener(this);
+ setFont(new Font("Monospaced", 0, 12));
+ }
+
+ /**
+ * Moves the selection to the given offset.
+ */
+ public void select(int pos) {
+ if (pos >= 0) {
+ try {
+ int line = getLineOfOffset(pos);
+ Rectangle rect = modelToView(pos);
+ if (rect == null) {
+ select(pos, pos);
+ } else {
+ try {
+ Rectangle nrect =
+ modelToView(getLineStartOffset(line + 1));
+ if (nrect != null) {
+ rect = nrect;
+ }
+ } catch (Exception exc) {
+ }
+ JViewport vp = (JViewport)getParent();
+ Rectangle viewRect = vp.getViewRect();
+ if (viewRect.y + viewRect.height > rect.y) {
+ // need to scroll up
+ select(pos, pos);
+ } else {
+ // need to scroll down
+ rect.y += (viewRect.height - rect.height)/2;
+ scrollRectToVisible(rect);
+ select(pos, pos);
+ }
+ }
+ } catch (BadLocationException exc) {
+ select(pos, pos);
+ //exc.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Checks if the popup menu should be shown.
+ */
+ private void checkPopup(MouseEvent e) {
+ if (e.isPopupTrigger()) {
+ popup.show(this, e.getX(), e.getY());
+ }
+ }
+
+ // MouseListener
+
+ /**
+ * Called when a mouse button is pressed.
+ */
+ public void mousePressed(MouseEvent e) {
+ checkPopup(e);
+ }
+
+ /**
+ * Called when the mouse is clicked.
+ */
+ public void mouseClicked(MouseEvent e) {
+ checkPopup(e);
+ requestFocus();
+ getCaret().setVisible(true);
+ }
+
+ /**
+ * Called when the mouse enters the component.
+ */
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ /**
+ * Called when the mouse exits the component.
+ */
+ public void mouseExited(MouseEvent e) {
+ }
+
+ /**
+ * Called when a mouse button is released.
+ */
+ public void mouseReleased(MouseEvent e) {
+ checkPopup(e);
+ }
+
+ // PopupMenuListener
+
+ /**
+ * Called before the popup menu will become visible.
+ */
+ public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
+ }
+
+ /**
+ * Called before the popup menu will become invisible.
+ */
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
+ }
+
+ /**
+ * Called when the popup menu is cancelled.
+ */
+ public void popupMenuCanceled(PopupMenuEvent e) {
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ int pos = viewToModel(new Point(popup.x, popup.y));
+ popup.setVisible(false);
+ String cmd = e.getActionCommand();
+ int line = -1;
+ try {
+ line = getLineOfOffset(pos);
+ } catch (Exception exc) {
+ }
+ if (cmd.equals("Set Breakpoint")) {
+ w.setBreakPoint(line + 1);
+ } else if (cmd.equals("Clear Breakpoint")) {
+ w.clearBreakPoint(line + 1);
+ } else if (cmd.equals("Run")) {
+ w.load();
+ }
+ }
+
+ // KeyListener
+
+ /**
+ * Called when a key is pressed.
+ */
+ public void keyPressed(KeyEvent e) {
+ switch (e.getKeyCode()) {
+ case KeyEvent.VK_BACK_SPACE:
+ case KeyEvent.VK_ENTER:
+ case KeyEvent.VK_DELETE:
+ case KeyEvent.VK_TAB:
+ e.consume();
+ break;
+ }
+ }
+
+ /**
+ * Called when a key is typed.
+ */
+ public void keyTyped(KeyEvent e) {
+ e.consume();
+ }
+
+ /**
+ * Called when a key is released.
+ */
+ public void keyReleased(KeyEvent e) {
+ e.consume();
+ }
+}
+
+/**
+ * Dialog to list the available windows.
+ */
+class MoreWindows extends JDialog implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 5177066296457377546L;
+
+ /**
+ * Last selected value.
+ */
+ private String value;
+
+ /**
+ * The list component.
+ */
+ private JList list;
+
+ /**
+ * Our parent frame.
+ */
+ private SwingGui swingGui;
+
+ /**
+ * The "Select" button.
+ */
+ private JButton setButton;
+
+ /**
+ * The "Cancel" button.
+ */
+ private JButton cancelButton;
+
+ /**
+ * Creates a new MoreWindows.
+ */
+ MoreWindows(SwingGui frame, Hashtable fileWindows, String title,
+ String labelText) {
+ super(frame, title, true);
+ this.swingGui = frame;
+ //buttons
+ cancelButton = new JButton("Cancel");
+ setButton = new JButton("Select");
+ cancelButton.addActionListener(this);
+ setButton.addActionListener(this);
+ getRootPane().setDefaultButton(setButton);
+
+ //dim part of the dialog
+ list = new JList(new DefaultListModel());
+ DefaultListModel model = (DefaultListModel)list.getModel();
+ model.clear();
+ //model.fireIntervalRemoved(model, 0, size);
+ Enumeration e = fileWindows.keys();
+ while (e.hasMoreElements()) {
+ String data = e.nextElement().toString();
+ model.addElement(data);
+ }
+ list.setSelectedIndex(0);
+ //model.fireIntervalAdded(model, 0, data.length);
+ setButton.setEnabled(true);
+ list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ list.addMouseListener(new MouseHandler());
+ JScrollPane listScroller = new JScrollPane(list);
+ listScroller.setPreferredSize(new Dimension(320, 240));
+ //XXX: Must do the following, too, or else the scroller thinks
+ //XXX: it's taller than it is:
+ listScroller.setMinimumSize(new Dimension(250, 80));
+ listScroller.setAlignmentX(LEFT_ALIGNMENT);
+
+ //Create a container so that we can add a title around
+ //the scroll pane. Can't add a title directly to the
+ //scroll pane because its background would be white.
+ //Lay out the label and scroll pane from top to button.
+ JPanel listPane = new JPanel();
+ listPane.setLayout(new BoxLayout(listPane, BoxLayout.Y_AXIS));
+ JLabel label = new JLabel(labelText);
+ label.setLabelFor (list);
+ listPane.add(label);
+ listPane.add(Box.createRigidArea(new Dimension(0,5)));
+ listPane.add(listScroller);
+ listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //Lay out the buttons from left to right.
+ JPanel buttonPane = new JPanel();
+ buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
+ buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
+ buttonPane.add(Box.createHorizontalGlue());
+ buttonPane.add(cancelButton);
+ buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+ buttonPane.add(setButton);
+
+ //Put everything together, using the content pane's BorderLayout.
+ Container contentPane = getContentPane();
+ contentPane.add(listPane, BorderLayout.CENTER);
+ contentPane.add(buttonPane, BorderLayout.SOUTH);
+ pack();
+ addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent ke) {
+ int code = ke.getKeyCode();
+ if (code == KeyEvent.VK_ESCAPE) {
+ ke.consume();
+ value = null;
+ setVisible(false);
+ }
+ }
+ });
+ }
+
+ /**
+ * Shows the dialog.
+ */
+ public String showDialog(Component comp) {
+ value = null;
+ setLocationRelativeTo(comp);
+ setVisible(true);
+ return value;
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cancel")) {
+ setVisible(false);
+ value = null;
+ } else if (cmd.equals("Select")) {
+ value = (String)list.getSelectedValue();
+ setVisible(false);
+ swingGui.showFileWindow(value, -1);
+ }
+ }
+
+ /**
+ * MouseListener implementation for {@link #list}.
+ */
+ private class MouseHandler extends MouseAdapter {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ setButton.doClick();
+ }
+ }
+ }
+}
+
+/**
+ * Find function dialog.
+ */
+class FindFunction extends JDialog implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 559491015232880916L;
+
+ /**
+ * Last selected function.
+ */
+ private String value;
+
+ /**
+ * List of functions.
+ */
+ private JList list;
+
+ /**
+ * The debug GUI frame.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The "Select" button.
+ */
+ private JButton setButton;
+
+ /**
+ * The "Cancel" button.
+ */
+ private JButton cancelButton;
+
+ /**
+ * Creates a new FindFunction.
+ */
+ public FindFunction(SwingGui debugGui, String title, String labelText) {
+ super(debugGui, title, true);
+ this.debugGui = debugGui;
+
+ cancelButton = new JButton("Cancel");
+ setButton = new JButton("Select");
+ cancelButton.addActionListener(this);
+ setButton.addActionListener(this);
+ getRootPane().setDefaultButton(setButton);
+
+ list = new JList(new DefaultListModel());
+ DefaultListModel model = (DefaultListModel)list.getModel();
+ model.clear();
+
+ String[] a = debugGui.dim.functionNames();
+ java.util.Arrays.sort(a);
+ for (int i = 0; i < a.length; i++) {
+ model.addElement(a[i]);
+ }
+ list.setSelectedIndex(0);
+
+ setButton.setEnabled(a.length > 0);
+ list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ list.addMouseListener(new MouseHandler());
+ JScrollPane listScroller = new JScrollPane(list);
+ listScroller.setPreferredSize(new Dimension(320, 240));
+ listScroller.setMinimumSize(new Dimension(250, 80));
+ listScroller.setAlignmentX(LEFT_ALIGNMENT);
+
+ //Create a container so that we can add a title around
+ //the scroll pane. Can't add a title directly to the
+ //scroll pane because its background would be white.
+ //Lay out the label and scroll pane from top to button.
+ JPanel listPane = new JPanel();
+ listPane.setLayout(new BoxLayout(listPane, BoxLayout.Y_AXIS));
+ JLabel label = new JLabel(labelText);
+ label.setLabelFor (list);
+ listPane.add(label);
+ listPane.add(Box.createRigidArea(new Dimension(0,5)));
+ listPane.add(listScroller);
+ listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //Lay out the buttons from left to right.
+ JPanel buttonPane = new JPanel();
+ buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
+ buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
+ buttonPane.add(Box.createHorizontalGlue());
+ buttonPane.add(cancelButton);
+ buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+ buttonPane.add(setButton);
+
+ //Put everything together, using the content pane's BorderLayout.
+ Container contentPane = getContentPane();
+ contentPane.add(listPane, BorderLayout.CENTER);
+ contentPane.add(buttonPane, BorderLayout.SOUTH);
+ pack();
+ addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent ke) {
+ int code = ke.getKeyCode();
+ if (code == KeyEvent.VK_ESCAPE) {
+ ke.consume();
+ value = null;
+ setVisible(false);
+ }
+ }
+ });
+ }
+
+ /**
+ * Shows the dialog.
+ */
+ public String showDialog(Component comp) {
+ value = null;
+ setLocationRelativeTo(comp);
+ setVisible(true);
+ return value;
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cancel")) {
+ setVisible(false);
+ value = null;
+ } else if (cmd.equals("Select")) {
+ if (list.getSelectedIndex() < 0) {
+ return;
+ }
+ try {
+ value = (String)list.getSelectedValue();
+ } catch (ArrayIndexOutOfBoundsException exc) {
+ return;
+ }
+ setVisible(false);
+ Dim.FunctionSource item = debugGui.dim.functionSourceByName(value);
+ if (item != null) {
+ Dim.SourceInfo si = item.sourceInfo();
+ String url = si.url();
+ int lineNumber = item.firstLine();
+ debugGui.showFileWindow(url, lineNumber);
+ }
+ }
+ }
+
+ /**
+ * MouseListener implementation for {@link #list}.
+ */
+ class MouseHandler extends MouseAdapter {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ setButton.doClick();
+ }
+ }
+ }
+}
+
+/**
+ * Gutter for FileWindows.
+ */
+class FileHeader extends JPanel implements MouseListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -2858905404778259127L;
+
+ /**
+ * The line that the mouse was pressed on.
+ */
+ private int pressLine = -1;
+
+ /**
+ * The owning FileWindow.
+ */
+ private FileWindow fileWindow;
+
+ /**
+ * Creates a new FileHeader.
+ */
+ public FileHeader(FileWindow fileWindow) {
+ this.fileWindow = fileWindow;
+ addMouseListener(this);
+ update();
+ }
+
+ /**
+ * Updates the gutter.
+ */
+ public void update() {
+ FileTextArea textArea = fileWindow.textArea;
+ Font font = textArea.getFont();
+ setFont(font);
+ FontMetrics metrics = getFontMetrics(font);
+ int h = metrics.getHeight();
+ int lineCount = textArea.getLineCount() + 1;
+ String dummy = Integer.toString(lineCount);
+ if (dummy.length() < 2) {
+ dummy = "99";
+ }
+ Dimension d = new Dimension();
+ d.width = metrics.stringWidth(dummy) + 16;
+ d.height = lineCount * h + 100;
+ setPreferredSize(d);
+ setSize(d);
+ }
+
+ /**
+ * Paints the component.
+ */
+ public void paint(Graphics g) {
+ super.paint(g);
+ FileTextArea textArea = fileWindow.textArea;
+ Font font = textArea.getFont();
+ g.setFont(font);
+ FontMetrics metrics = getFontMetrics(font);
+ Rectangle clip = g.getClipBounds();
+ g.setColor(getBackground());
+ g.fillRect(clip.x, clip.y, clip.width, clip.height);
+ int ascent = metrics.getMaxAscent();
+ int h = metrics.getHeight();
+ int lineCount = textArea.getLineCount() + 1;
+ String dummy = Integer.toString(lineCount);
+ if (dummy.length() < 2) {
+ dummy = "99";
+ }
+ int startLine = clip.y / h;
+ int endLine = (clip.y + clip.height) / h + 1;
+ int width = getWidth();
+ if (endLine > lineCount) endLine = lineCount;
+ for (int i = startLine; i < endLine; i++) {
+ String text;
+ int pos = -2;
+ try {
+ pos = textArea.getLineStartOffset(i);
+ } catch (BadLocationException ignored) {
+ }
+ boolean isBreakPoint = fileWindow.isBreakPoint(i + 1);
+ text = Integer.toString(i + 1) + " ";
+ int y = i * h;
+ g.setColor(Color.blue);
+ g.drawString(text, 0, y + ascent);
+ int x = width - ascent;
+ if (isBreakPoint) {
+ g.setColor(new Color(0x80, 0x00, 0x00));
+ int dy = y + ascent - 9;
+ g.fillOval(x, dy, 9, 9);
+ g.drawOval(x, dy, 8, 8);
+ g.drawOval(x, dy, 9, 9);
+ }
+ if (pos == fileWindow.currentPos) {
+ Polygon arrow = new Polygon();
+ int dx = x;
+ y += ascent - 10;
+ int dy = y;
+ arrow.addPoint(dx, dy + 3);
+ arrow.addPoint(dx + 5, dy + 3);
+ for (x = dx + 5; x <= dx + 10; x++, y++) {
+ arrow.addPoint(x, y);
+ }
+ for (x = dx + 9; x >= dx + 5; x--, y++) {
+ arrow.addPoint(x, y);
+ }
+ arrow.addPoint(dx + 5, dy + 7);
+ arrow.addPoint(dx, dy + 7);
+ g.setColor(Color.yellow);
+ g.fillPolygon(arrow);
+ g.setColor(Color.black);
+ g.drawPolygon(arrow);
+ }
+ }
+ }
+
+ // MouseListener
+
+ /**
+ * Called when the mouse enters the component.
+ */
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ /**
+ * Called when a mouse button is pressed.
+ */
+ public void mousePressed(MouseEvent e) {
+ Font font = fileWindow.textArea.getFont();
+ FontMetrics metrics = getFontMetrics(font);
+ int h = metrics.getHeight();
+ pressLine = e.getY() / h;
+ }
+
+ /**
+ * Called when the mouse is clicked.
+ */
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ /**
+ * Called when the mouse exits the component.
+ */
+ public void mouseExited(MouseEvent e) {
+ }
+
+ /**
+ * Called when a mouse button is released.
+ */
+ public void mouseReleased(MouseEvent e) {
+ if (e.getComponent() == this
+ && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
+ int y = e.getY();
+ Font font = fileWindow.textArea.getFont();
+ FontMetrics metrics = getFontMetrics(font);
+ int h = metrics.getHeight();
+ int line = y/h;
+ if (line == pressLine) {
+ fileWindow.toggleBreakPoint(line + 1);
+ } else {
+ pressLine = -1;
+ }
+ }
+ }
+}
+
+/**
+ * An internal frame for script files.
+ */
+class FileWindow extends JInternalFrame implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -6212382604952082370L;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The SourceInfo object that describes the file.
+ */
+ private Dim.SourceInfo sourceInfo;
+
+ /**
+ * The FileTextArea that displays the file.
+ */
+ FileTextArea textArea;
+
+ /**
+ * The FileHeader that is the gutter for {@link #textArea}.
+ */
+ private FileHeader fileHeader;
+
+ /**
+ * Scroll pane for containing {@link #textArea}.
+ */
+ private JScrollPane p;
+
+ /**
+ * The current offset position.
+ */
+ int currentPos;
+
+ /**
+ * Loads the file.
+ */
+ void load() {
+ String url = getUrl();
+ if (url != null) {
+ RunProxy proxy = new RunProxy(debugGui, RunProxy.LOAD_FILE);
+ proxy.fileName = url;
+ proxy.text = sourceInfo.source();
+ new Thread(proxy).start();
+ }
+ }
+
+ /**
+ * Returns the offset position for the given line.
+ */
+ public int getPosition(int line) {
+ int result = -1;
+ try {
+ result = textArea.getLineStartOffset(line);
+ } catch (javax.swing.text.BadLocationException exc) {
+ }
+ return result;
+ }
+
+ /**
+ * Returns whether the given line has a breakpoint.
+ */
+ public boolean isBreakPoint(int line) {
+ return sourceInfo.breakableLine(line) && sourceInfo.breakpoint(line);
+ }
+
+ /**
+ * Toggles the breakpoint on the given line.
+ */
+ public void toggleBreakPoint(int line) {
+ if (!isBreakPoint(line)) {
+ setBreakPoint(line);
+ } else {
+ clearBreakPoint(line);
+ }
+ }
+
+ /**
+ * Sets a breakpoint on the given line.
+ */
+ public void setBreakPoint(int line) {
+ if (sourceInfo.breakableLine(line)) {
+ boolean changed = sourceInfo.breakpoint(line, true);
+ if (changed) {
+ fileHeader.repaint();
+ }
+ }
+ }
+
+ /**
+ * Clears a breakpoint from the given line.
+ */
+ public void clearBreakPoint(int line) {
+ if (sourceInfo.breakableLine(line)) {
+ boolean changed = sourceInfo.breakpoint(line, false);
+ if (changed) {
+ fileHeader.repaint();
+ }
+ }
+ }
+
+ /**
+ * Creates a new FileWindow.
+ */
+ public FileWindow(SwingGui debugGui, Dim.SourceInfo sourceInfo) {
+ super(SwingGui.getShortName(sourceInfo.url()),
+ true, true, true, true);
+ this.debugGui = debugGui;
+ this.sourceInfo = sourceInfo;
+ updateToolTip();
+ currentPos = -1;
+ textArea = new FileTextArea(this);
+ textArea.setRows(24);
+ textArea.setColumns(80);
+ p = new JScrollPane();
+ fileHeader = new FileHeader(this);
+ p.setViewportView(textArea);
+ p.setRowHeaderView(fileHeader);
+ setContentPane(p);
+ pack();
+ updateText(sourceInfo);
+ textArea.select(0);
+ }
+
+ /**
+ * Updates the tool tip contents.
+ */
+ private void updateToolTip() {
+ // Try to set tool tip on frame. On Mac OS X 10.5,
+ // the number of components is different, so try to be safe.
+ int n = getComponentCount() - 1;
+ if (n > 1) {
+ n = 1;
+ } else if (n < 0) {
+ return;
+ }
+ Component c = getComponent(n);
+ // this will work at least for Metal L&F
+ if (c != null && c instanceof JComponent) {
+ ((JComponent)c).setToolTipText(getUrl());
+ }
+ }
+
+ /**
+ * Returns the URL of the source.
+ */
+ public String getUrl() {
+ return sourceInfo.url();
+ }
+
+ /**
+ * Called when the text of the script has changed.
+ */
+ public void updateText(Dim.SourceInfo sourceInfo) {
+ this.sourceInfo = sourceInfo;
+ String newText = sourceInfo.source();
+ if (!textArea.getText().equals(newText)) {
+ textArea.setText(newText);
+ int pos = 0;
+ if (currentPos != -1) {
+ pos = currentPos;
+ }
+ textArea.select(pos);
+ }
+ fileHeader.update();
+ fileHeader.repaint();
+ }
+
+ /**
+ * Sets the cursor position.
+ */
+ public void setPosition(int pos) {
+ textArea.select(pos);
+ currentPos = pos;
+ fileHeader.repaint();
+ }
+
+ /**
+ * Selects a range of characters.
+ */
+ public void select(int start, int end) {
+ int docEnd = textArea.getDocument().getLength();
+ textArea.select(docEnd, docEnd);
+ textArea.select(start, end);
+ }
+
+ /**
+ * Disposes this FileWindow.
+ */
+ public void dispose() {
+ debugGui.removeWindow(this);
+ super.dispose();
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cut")) {
+ // textArea.cut();
+ } else if (cmd.equals("Copy")) {
+ textArea.copy();
+ } else if (cmd.equals("Paste")) {
+ // textArea.paste();
+ }
+ }
+}
+
+/**
+ * Table model class for watched expressions.
+ */
+class MyTableModel extends AbstractTableModel {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 2971618907207577000L;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * Vector of watched expressions.
+ */
+ private Vector expressions;
+
+ /**
+ * Vector of values from evaluated from {@link #expressions}.
+ */
+ private Vector values;
+
+ /**
+ * Creates a new MyTableModel.
+ */
+ public MyTableModel(SwingGui debugGui) {
+ this.debugGui = debugGui;
+ expressions = new Vector();
+ values = new Vector();
+ expressions.addElement("");
+ values.addElement("");
+ }
+
+ /**
+ * Returns the number of columns in the table (2).
+ */
+ public int getColumnCount() {
+ return 2;
+ }
+
+ /**
+ * Returns the number of rows in the table.
+ */
+ public int getRowCount() {
+ return expressions.size();
+ }
+
+ /**
+ * Returns the name of the given column.
+ */
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ return "Expression";
+ case 1:
+ return "Value";
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the given cell is editable.
+ */
+ public boolean isCellEditable(int row, int column) {
+ return true;
+ }
+
+ /**
+ * Returns the value in the given cell.
+ */
+ public Object getValueAt(int row, int column) {
+ switch (column) {
+ case 0:
+ return expressions.elementAt(row);
+ case 1:
+ return values.elementAt(row);
+ }
+ return "";
+ }
+
+ /**
+ * Sets the value in the given cell.
+ */
+ public void setValueAt(Object value, int row, int column) {
+ switch (column) {
+ case 0:
+ String expr = value.toString();
+ expressions.setElementAt(expr, row);
+ String result = "";
+ if (expr.length() > 0) {
+ result = debugGui.dim.eval(expr);
+ if (result == null) result = "";
+ }
+ values.setElementAt(result, row);
+ updateModel();
+ if (row + 1 == expressions.size()) {
+ expressions.addElement("");
+ values.addElement("");
+ fireTableRowsInserted(row + 1, row + 1);
+ }
+ break;
+ case 1:
+ // just reset column 2; ignore edits
+ fireTableDataChanged();
+ }
+ }
+
+ /**
+ * Re-evaluates the expressions in the table.
+ */
+ void updateModel() {
+ for (int i = 0; i < expressions.size(); ++i) {
+ Object value = expressions.elementAt(i);
+ String expr = value.toString();
+ String result = "";
+ if (expr.length() > 0) {
+ result = debugGui.dim.eval(expr);
+ if (result == null) result = "";
+ } else {
+ result = "";
+ }
+ result = result.replace('\n', ' ');
+ values.setElementAt(result, i);
+ }
+ fireTableDataChanged();
+ }
+}
+
+/**
+ * A table for evaluated expressions.
+ */
+class Evaluator extends JTable {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 8133672432982594256L;
+
+ /**
+ * The {@link TableModel} for this table.
+ */
+ MyTableModel tableModel;
+
+ /**
+ * Creates a new Evaluator.
+ */
+ public Evaluator(SwingGui debugGui) {
+ super(new MyTableModel(debugGui));
+ tableModel = (MyTableModel)getModel();
+ }
+}
+
+/**
+ * Tree model for script object inspection.
+ */
+class VariableModel implements TreeTableModel {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final String[] cNames = { " Name", " Value" };
+
+ /**
+ * Tree column types.
+ */
+ private static final Class[] cTypes =
+ { TreeTableModel.class, String.class };
+
+ /**
+ * Empty {@link VariableNode} array.
+ */
+ private static final VariableNode[] CHILDLESS = new VariableNode[0];
+
+ /**
+ * The debugger.
+ */
+ private Dim debugger;
+
+ /**
+ * The root node.
+ */
+ private VariableNode root;
+
+ /**
+ * Creates a new VariableModel.
+ */
+ public VariableModel() {
+ }
+
+ /**
+ * Creates a new VariableModel.
+ */
+ public VariableModel(Dim debugger, Object scope) {
+ this.debugger = debugger;
+ this.root = new VariableNode(scope, "this");
+ }
+
+ // TreeTableModel
+
+ /**
+ * Returns the root node of the tree.
+ */
+ public Object getRoot() {
+ if (debugger == null) {
+ return null;
+ }
+ return root;
+ }
+
+ /**
+ * Returns the number of children of the given node.
+ */
+ public int getChildCount(Object nodeObj) {
+ if (debugger == null) {
+ return 0;
+ }
+ VariableNode node = (VariableNode) nodeObj;
+ return children(node).length;
+ }
+
+ /**
+ * Returns a child of the given node.
+ */
+ public Object getChild(Object nodeObj, int i) {
+ if (debugger == null) {
+ return null;
+ }
+ VariableNode node = (VariableNode) nodeObj;
+ return children(node)[i];
+ }
+
+ /**
+ * Returns whether the given node is a leaf node.
+ */
+ public boolean isLeaf(Object nodeObj) {
+ if (debugger == null) {
+ return true;
+ }
+ VariableNode node = (VariableNode) nodeObj;
+ return children(node).length == 0;
+ }
+
+ /**
+ * Returns the index of a node under its parent.
+ */
+ public int getIndexOfChild(Object parentObj, Object childObj) {
+ if (debugger == null) {
+ return -1;
+ }
+ VariableNode parent = (VariableNode) parentObj;
+ VariableNode child = (VariableNode) childObj;
+ VariableNode[] children = children(parent);
+ for (int i = 0; i != children.length; i++) {
+ if (children[i] == child) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns whether the given cell is editable.
+ */
+ public boolean isCellEditable(Object node, int column) {
+ return column == 0;
+ }
+
+ /**
+ * Sets the value at the given cell.
+ */
+ public void setValueAt(Object value, Object node, int column) { }
+
+ /**
+ * Adds a TreeModelListener to this tree.
+ */
+ public void addTreeModelListener(TreeModelListener l) { }
+
+ /**
+ * Removes a TreeModelListener from this tree.
+ */
+ public void removeTreeModelListener(TreeModelListener l) { }
+
+ public void valueForPathChanged(TreePath path, Object newValue) { }
+
+ // TreeTableNode
+
+ /**
+ * Returns the number of columns.
+ */
+ public int getColumnCount() {
+ return cNames.length;
+ }
+
+ /**
+ * Returns the name of the given column.
+ */
+ public String getColumnName(int column) {
+ return cNames[column];
+ }
+
+ /**
+ * Returns the type of value stored in the given column.
+ */
+ public Class getColumnClass(int column) {
+ return cTypes[column];
+ }
+
+ /**
+ * Returns the value at the given cell.
+ */
+ public Object getValueAt(Object nodeObj, int column) {
+ if (debugger == null) { return null; }
+ VariableNode node = (VariableNode)nodeObj;
+ switch (column) {
+ case 0: // Name
+ return node.toString();
+ case 1: // Value
+ String result;
+ try {
+ result = debugger.objectToString(getValue(node));
+ } catch (RuntimeException exc) {
+ result = exc.getMessage();
+ }
+ StringBuffer buf = new StringBuffer();
+ int len = result.length();
+ for (int i = 0; i < len; i++) {
+ char ch = result.charAt(i);
+ if (Character.isISOControl(ch)) {
+ ch = ' ';
+ }
+ buf.append(ch);
+ }
+ return buf.toString();
+ }
+ return null;
+ }
+
+ /**
+ * Returns an array of the children of the given node.
+ */
+ private VariableNode[] children(VariableNode node) {
+ if (node.children != null) {
+ return node.children;
+ }
+
+ VariableNode[] children;
+
+ Object value = getValue(node);
+ Object[] ids = debugger.getObjectIds(value);
+ if (ids == null || ids.length == 0) {
+ children = CHILDLESS;
+ } else {
+ Arrays.sort(ids, new Comparator() {
+ public int compare(Object l, Object r)
+ {
+ if (l instanceof String) {
+ if (r instanceof Integer) {
+ return -1;
+ }
+ return ((String)l).compareToIgnoreCase((String)r);
+ } else {
+ if (r instanceof String) {
+ return 1;
+ }
+ int lint = ((Integer)l).intValue();
+ int rint = ((Integer)r).intValue();
+ return lint - rint;
+ }
+ }
+ });
+ children = new VariableNode[ids.length];
+ for (int i = 0; i != ids.length; ++i) {
+ children[i] = new VariableNode(value, ids[i]);
+ }
+ }
+ node.children = children;
+ return children;
+ }
+
+ /**
+ * Returns the value of the given node.
+ */
+ public Object getValue(VariableNode node) {
+ try {
+ return debugger.getObjectProperty(node.object, node.id);
+ } catch (Exception exc) {
+ return "undefined";
+ }
+ }
+
+ /**
+ * A variable node in the tree.
+ */
+ private static class VariableNode {
+
+ /**
+ * The script object.
+ */
+ private Object object;
+
+ /**
+ * The object name. Either a String or an Integer.
+ */
+ private Object id;
+
+ /**
+ * Array of child nodes. This is filled with the properties of
+ * the object.
+ */
+ private VariableNode[] children;
+
+ /**
+ * Creates a new VariableNode.
+ */
+ public VariableNode(Object object, Object id) {
+ this.object = object;
+ this.id = id;
+ }
+
+ /**
+ * Returns a string representation of this node.
+ */
+ public String toString() {
+ return id instanceof String
+ ? (String) id : "[" + ((Integer) id).intValue() + "]";
+ }
+ }
+}
+
+/**
+ * A tree table for browsing script objects.
+ */
+class MyTreeTable extends JTreeTable {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 3457265548184453049L;
+
+ /**
+ * Creates a new MyTreeTable.
+ */
+ public MyTreeTable(VariableModel model) {
+ super(model);
+ }
+
+ /**
+ * Initializes a tree for this tree table.
+ */
+ public JTree resetTree(TreeTableModel treeTableModel) {
+ tree = new TreeTableCellRenderer(treeTableModel);
+
+ // Install a tableModel representing the visible rows in the tree.
+ super.setModel(new TreeTableModelAdapter(treeTableModel, tree));
+
+ // Force the JTable and JTree to share their row selection models.
+ ListToTreeSelectionModelWrapper selectionWrapper = new
+ ListToTreeSelectionModelWrapper();
+ tree.setSelectionModel(selectionWrapper);
+ setSelectionModel(selectionWrapper.getListSelectionModel());
+
+ // Make the tree and table row heights the same.
+ if (tree.getRowHeight() < 1) {
+ // Metal looks better like this.
+ setRowHeight(18);
+ }
+
+ // Install the tree editor renderer and editor.
+ setDefaultRenderer(TreeTableModel.class, tree);
+ setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
+ setShowGrid(true);
+ setIntercellSpacing(new Dimension(1,1));
+ tree.setRootVisible(false);
+ tree.setShowsRootHandles(true);
+ DefaultTreeCellRenderer r = (DefaultTreeCellRenderer)tree.getCellRenderer();
+ r.setOpenIcon(null);
+ r.setClosedIcon(null);
+ r.setLeafIcon(null);
+ return tree;
+ }
+
+ /**
+ * Returns whether the cell under the coordinates of the mouse
+ * in the {@link EventObject} is editable.
+ */
+ public boolean isCellEditable(EventObject e) {
+ if (e instanceof MouseEvent) {
+ MouseEvent me = (MouseEvent)e;
+ // If the modifiers are not 0 (or the left mouse button),
+ // tree may try and toggle the selection, and table
+ // will then try and toggle, resulting in the
+ // selection remaining the same. To avoid this, we
+ // only dispatch when the modifiers are 0 (or the left mouse
+ // button).
+ if (me.getModifiers() == 0 ||
+ ((me.getModifiers() & (InputEvent.BUTTON1_MASK|1024)) != 0 &&
+ (me.getModifiers() &
+ (InputEvent.SHIFT_MASK |
+ InputEvent.CTRL_MASK |
+ InputEvent.ALT_MASK |
+ InputEvent.BUTTON2_MASK |
+ InputEvent.BUTTON3_MASK |
+ 64 | //SHIFT_DOWN_MASK
+ 128 | //CTRL_DOWN_MASK
+ 512 | // ALT_DOWN_MASK
+ 2048 | //BUTTON2_DOWN_MASK
+ 4096 //BUTTON3_DOWN_MASK
+ )) == 0)) {
+ int row = rowAtPoint(me.getPoint());
+ for (int counter = getColumnCount() - 1; counter >= 0;
+ counter--) {
+ if (TreeTableModel.class == getColumnClass(counter)) {
+ MouseEvent newME = new MouseEvent
+ (MyTreeTable.this.tree, me.getID(),
+ me.getWhen(), me.getModifiers(),
+ me.getX() - getCellRect(row, counter, true).x,
+ me.getY(), me.getClickCount(),
+ me.isPopupTrigger());
+ MyTreeTable.this.tree.dispatchEvent(newME);
+ break;
+ }
+ }
+ }
+ if (me.getClickCount() >= 3) {
+ return true;
+ }
+ return false;
+ }
+ if (e == null) {
+ return true;
+ }
+ return false;
+ }
+}
+
+/**
+ * Panel that shows information about the context.
+ */
+class ContextWindow extends JPanel implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 2306040975490228051L;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The combo box that holds the stack frames.
+ */
+ JComboBox context;
+
+ /**
+ * Tool tips for the stack frames.
+ */
+ Vector toolTips;
+
+ /**
+ * Tabbed pane for "this" and "locals".
+ */
+ private JTabbedPane tabs;
+
+ /**
+ * Tabbed pane for "watch" and "evaluate".
+ */
+ private JTabbedPane tabs2;
+
+ /**
+ * The table showing the "this" object.
+ */
+ private MyTreeTable thisTable;
+
+ /**
+ * The table showing the stack local variables.
+ */
+ private MyTreeTable localsTable;
+
+ /**
+ * The {@link #evaluator}'s table model.
+ */
+ private MyTableModel tableModel;
+
+ /**
+ * The script evaluator table.
+ */
+ private Evaluator evaluator;
+
+ /**
+ * The script evaluation text area.
+ */
+ private EvalTextArea cmdLine;
+
+ /**
+ * The split pane.
+ */
+ JSplitPane split;
+
+ /**
+ * Whether the ContextWindow is enabled.
+ */
+ private boolean enabled;
+
+ /**
+ * Creates a new ContextWindow.
+ */
+ public ContextWindow(final SwingGui debugGui) {
+ this.debugGui = debugGui;
+ enabled = false;
+ JPanel left = new JPanel();
+ JToolBar t1 = new JToolBar();
+ t1.setName("Variables");
+ t1.setLayout(new GridLayout());
+ t1.add(left);
+ JPanel p1 = new JPanel();
+ p1.setLayout(new GridLayout());
+ JPanel p2 = new JPanel();
+ p2.setLayout(new GridLayout());
+ p1.add(t1);
+ JLabel label = new JLabel("Context:");
+ context = new JComboBox();
+ context.setLightWeightPopupEnabled(false);
+ toolTips = new java.util.Vector();
+ label.setBorder(context.getBorder());
+ context.addActionListener(this);
+ context.setActionCommand("ContextSwitch");
+ GridBagLayout layout = new GridBagLayout();
+ left.setLayout(layout);
+ GridBagConstraints lc = new GridBagConstraints();
+ lc.insets.left = 5;
+ lc.anchor = GridBagConstraints.WEST;
+ lc.ipadx = 5;
+ layout.setConstraints(label, lc);
+ left.add(label);
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.WEST;
+ layout.setConstraints(context, c);
+ left.add(context);
+ tabs = new JTabbedPane(SwingConstants.BOTTOM);
+ tabs.setPreferredSize(new Dimension(500,300));
+ thisTable = new MyTreeTable(new VariableModel());
+ JScrollPane jsp = new JScrollPane(thisTable);
+ jsp.getViewport().setViewSize(new Dimension(5,2));
+ tabs.add("this", jsp);
+ localsTable = new MyTreeTable(new VariableModel());
+ localsTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+ localsTable.setPreferredSize(null);
+ jsp = new JScrollPane(localsTable);
+ tabs.add("Locals", jsp);
+ c.weightx = c.weighty = 1;
+ c.gridheight = GridBagConstraints.REMAINDER;
+ c.fill = GridBagConstraints.BOTH;
+ c.anchor = GridBagConstraints.WEST;
+ layout.setConstraints(tabs, c);
+ left.add(tabs);
+ evaluator = new Evaluator(debugGui);
+ cmdLine = new EvalTextArea(debugGui);
+ //cmdLine.requestFocus();
+ tableModel = evaluator.tableModel;
+ jsp = new JScrollPane(evaluator);
+ JToolBar t2 = new JToolBar();
+ t2.setName("Evaluate");
+ tabs2 = new JTabbedPane(SwingConstants.BOTTOM);
+ tabs2.add("Watch", jsp);
+ tabs2.add("Evaluate", new JScrollPane(cmdLine));
+ tabs2.setPreferredSize(new Dimension(500,300));
+ t2.setLayout(new GridLayout());
+ t2.add(tabs2);
+ p2.add(t2);
+ evaluator.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+ split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
+ p1, p2);
+ split.setOneTouchExpandable(true);
+ SwingGui.setResizeWeight(split, 0.5);
+ setLayout(new BorderLayout());
+ add(split, BorderLayout.CENTER);
+
+ final JToolBar finalT1 = t1;
+ final JToolBar finalT2 = t2;
+ final JPanel finalP1 = p1;
+ final JPanel finalP2 = p2;
+ final JSplitPane finalSplit = split;
+ final JPanel finalThis = this;
+
+ ComponentListener clistener = new ComponentListener() {
+ boolean t2Docked = true;
+ void check(Component comp) {
+ Component thisParent = finalThis.getParent();
+ if (thisParent == null) {
+ return;
+ }
+ Component parent = finalT1.getParent();
+ boolean leftDocked = true;
+ boolean rightDocked = true;
+ boolean adjustVerticalSplit = false;
+ if (parent != null) {
+ if (parent != finalP1) {
+ while (!(parent instanceof JFrame)) {
+ parent = parent.getParent();
+ }
+ JFrame frame = (JFrame)parent;
+ debugGui.addTopLevel("Variables", frame);
+
+ // We need the following hacks because:
+ // - We want an undocked toolbar to be
+ // resizable.
+ // - We are using JToolbar as a container of a
+ // JComboBox. Without this JComboBox's popup
+ // can get left floating when the toolbar is
+ // re-docked.
+ //
+ // We make the frame resizable and then
+ // remove JToolbar's window listener
+ // and insert one of our own that first ensures
+ // the JComboBox's popup window is closed
+ // and then calls JToolbar's window listener.
+ if (!frame.isResizable()) {
+ frame.setResizable(true);
+ frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+ final EventListener[] l =
+ frame.getListeners(WindowListener.class);
+ frame.removeWindowListener((WindowListener)l[0]);
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ context.hidePopup();
+ ((WindowListener)l[0]).windowClosing(e);
+ }
+ });
+ //adjustVerticalSplit = true;
+ }
+ leftDocked = false;
+ } else {
+ leftDocked = true;
+ }
+ }
+ parent = finalT2.getParent();
+ if (parent != null) {
+ if (parent != finalP2) {
+ while (!(parent instanceof JFrame)) {
+ parent = parent.getParent();
+ }
+ JFrame frame = (JFrame)parent;
+ debugGui.addTopLevel("Evaluate", frame);
+ frame.setResizable(true);
+ rightDocked = false;
+ } else {
+ rightDocked = true;
+ }
+ }
+ if (leftDocked && t2Docked && rightDocked && t2Docked) {
+ // no change
+ return;
+ }
+ t2Docked = rightDocked;
+ JSplitPane split = (JSplitPane)thisParent;
+ if (leftDocked) {
+ if (rightDocked) {
+ finalSplit.setDividerLocation(0.5);
+ } else {
+ finalSplit.setDividerLocation(1.0);
+ }
+ if (adjustVerticalSplit) {
+ split.setDividerLocation(0.66);
+ }
+
+ } else if (rightDocked) {
+ finalSplit.setDividerLocation(0.0);
+ split.setDividerLocation(0.66);
+ } else {
+ // both undocked
+ split.setDividerLocation(1.0);
+ }
+ }
+ public void componentHidden(ComponentEvent e) {
+ check(e.getComponent());
+ }
+ public void componentMoved(ComponentEvent e) {
+ check(e.getComponent());
+ }
+ public void componentResized(ComponentEvent e) {
+ check(e.getComponent());
+ }
+ public void componentShown(ComponentEvent e) {
+ check(e.getComponent());
+ }
+ };
+ p1.addContainerListener(new ContainerListener() {
+ public void componentAdded(ContainerEvent e) {
+ Component thisParent = finalThis.getParent();
+ JSplitPane split = (JSplitPane)thisParent;
+ if (e.getChild() == finalT1) {
+ if (finalT2.getParent() == finalP2) {
+ // both docked
+ finalSplit.setDividerLocation(0.5);
+ } else {
+ // left docked only
+ finalSplit.setDividerLocation(1.0);
+ }
+ split.setDividerLocation(0.66);
+ }
+ }
+ public void componentRemoved(ContainerEvent e) {
+ Component thisParent = finalThis.getParent();
+ JSplitPane split = (JSplitPane)thisParent;
+ if (e.getChild() == finalT1) {
+ if (finalT2.getParent() == finalP2) {
+ // right docked only
+ finalSplit.setDividerLocation(0.0);
+ split.setDividerLocation(0.66);
+ } else {
+ // both undocked
+ split.setDividerLocation(1.0);
+ }
+ }
+ }
+ });
+ t1.addComponentListener(clistener);
+ t2.addComponentListener(clistener);
+ disable();
+ }
+
+ /**
+ * Disables the component.
+ */
+ public void disable() {
+ context.setEnabled(false);
+ thisTable.setEnabled(false);
+ localsTable.setEnabled(false);
+ evaluator.setEnabled(false);
+ cmdLine.setEnabled(false);
+ }
+
+ /**
+ * Enables the component.
+ */
+ public void enable() {
+ context.setEnabled(true);
+ thisTable.setEnabled(true);
+ localsTable.setEnabled(true);
+ evaluator.setEnabled(true);
+ cmdLine.setEnabled(true);
+ }
+
+ /**
+ * Disables updating of the component.
+ */
+ public void disableUpdate() {
+ enabled = false;
+ }
+
+ /**
+ * Enables updating of the component.
+ */
+ public void enableUpdate() {
+ enabled = true;
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ if (!enabled) return;
+ if (e.getActionCommand().equals("ContextSwitch")) {
+ Dim.ContextData contextData = debugGui.dim.currentContextData();
+ if (contextData == null) { return; }
+ int frameIndex = context.getSelectedIndex();
+ context.setToolTipText(toolTips.elementAt(frameIndex).toString());
+ int frameCount = contextData.frameCount();
+ if (frameIndex >= frameCount) {
+ return;
+ }
+ Dim.StackFrame frame = contextData.getFrame(frameIndex);
+ Object scope = frame.scope();
+ Object thisObj = frame.thisObj();
+ thisTable.resetTree(new VariableModel(debugGui.dim, thisObj));
+ VariableModel scopeModel;
+ if (scope != thisObj) {
+ scopeModel = new VariableModel(debugGui.dim, scope);
+ } else {
+ scopeModel = new VariableModel();
+ }
+ localsTable.resetTree(scopeModel);
+ debugGui.dim.contextSwitch(frameIndex);
+ debugGui.showStopLine(frame);
+ tableModel.updateModel();
+ }
+ }
+}
+
+/**
+ * The debugger frame menu bar.
+ */
+class Menubar extends JMenuBar implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 3217170497245911461L;
+
+ /**
+ * Items that are enabled only when interrupted.
+ */
+ private Vector interruptOnlyItems = new Vector();
+
+ /**
+ * Items that are enabled only when running.
+ */
+ private Vector runOnlyItems = new Vector();
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The menu listing the internal frames.
+ */
+ private JMenu windowMenu;
+
+ /**
+ * The "Break on exceptions" menu item.
+ */
+ private JCheckBoxMenuItem breakOnExceptions;
+
+ /**
+ * The "Break on enter" menu item.
+ */
+ private JCheckBoxMenuItem breakOnEnter;
+
+ /**
+ * The "Break on return" menu item.
+ */
+ private JCheckBoxMenuItem breakOnReturn;
+
+ /**
+ * Creates a new Menubar.
+ */
+ Menubar(SwingGui debugGui) {
+ super();
+ this.debugGui = debugGui;
+ String[] fileItems = {"Open...", "Run...", "", "Exit"};
+ String[] fileCmds = {"Open", "Load", "", "Exit"};
+ char[] fileShortCuts = {'0', 'N', 0, 'X'};
+ int[] fileAccelerators = {KeyEvent.VK_O,
+ KeyEvent.VK_N,
+ 0,
+ KeyEvent.VK_Q};
+ String[] editItems = {"Cut", "Copy", "Paste", "Go to function..."};
+ char[] editShortCuts = {'T', 'C', 'P', 'F'};
+ String[] debugItems = {"Break", "Go", "Step Into", "Step Over", "Step Out"};
+ char[] debugShortCuts = {'B', 'G', 'I', 'O', 'T'};
+ String[] plafItems = {"Metal", "Windows", "Motif"};
+ char [] plafShortCuts = {'M', 'W', 'F'};
+ int[] debugAccelerators = {KeyEvent.VK_PAUSE,
+ KeyEvent.VK_F5,
+ KeyEvent.VK_F11,
+ KeyEvent.VK_F7,
+ KeyEvent.VK_F8,
+ 0, 0};
+
+ JMenu fileMenu = new JMenu("File");
+ fileMenu.setMnemonic('F');
+ JMenu editMenu = new JMenu("Edit");
+ editMenu.setMnemonic('E');
+ JMenu plafMenu = new JMenu("Platform");
+ plafMenu.setMnemonic('P');
+ JMenu debugMenu = new JMenu("Debug");
+ debugMenu.setMnemonic('D');
+ windowMenu = new JMenu("Window");
+ windowMenu.setMnemonic('W');
+ for (int i = 0; i < fileItems.length; ++i) {
+ if (fileItems[i].length() == 0) {
+ fileMenu.addSeparator();
+ } else {
+ JMenuItem item = new JMenuItem(fileItems[i],
+ fileShortCuts[i]);
+ item.setActionCommand(fileCmds[i]);
+ item.addActionListener(this);
+ fileMenu.add(item);
+ if (fileAccelerators[i] != 0) {
+ KeyStroke k = KeyStroke.getKeyStroke(fileAccelerators[i], Event.CTRL_MASK);
+ item.setAccelerator(k);
+ }
+ }
+ }
+ for (int i = 0; i < editItems.length; ++i) {
+ JMenuItem item = new JMenuItem(editItems[i],
+ editShortCuts[i]);
+ item.addActionListener(this);
+ editMenu.add(item);
+ }
+ for (int i = 0; i < plafItems.length; ++i) {
+ JMenuItem item = new JMenuItem(plafItems[i],
+ plafShortCuts[i]);
+ item.addActionListener(this);
+ plafMenu.add(item);
+ }
+ for (int i = 0; i < debugItems.length; ++i) {
+ JMenuItem item = new JMenuItem(debugItems[i],
+ debugShortCuts[i]);
+ item.addActionListener(this);
+ if (debugAccelerators[i] != 0) {
+ KeyStroke k = KeyStroke.getKeyStroke(debugAccelerators[i], 0);
+ item.setAccelerator(k);
+ }
+ if (i != 0) {
+ interruptOnlyItems.add(item);
+ } else {
+ runOnlyItems.add(item);
+ }
+ debugMenu.add(item);
+ }
+ breakOnExceptions = new JCheckBoxMenuItem("Break on Exceptions");
+ breakOnExceptions.setMnemonic('X');
+ breakOnExceptions.addActionListener(this);
+ breakOnExceptions.setSelected(false);
+ debugMenu.add(breakOnExceptions);
+
+ breakOnEnter = new JCheckBoxMenuItem("Break on Function Enter");
+ breakOnEnter.setMnemonic('E');
+ breakOnEnter.addActionListener(this);
+ breakOnEnter.setSelected(false);
+ debugMenu.add(breakOnEnter);
+
+ breakOnReturn = new JCheckBoxMenuItem("Break on Function Return");
+ breakOnReturn.setMnemonic('R');
+ breakOnReturn.addActionListener(this);
+ breakOnReturn.setSelected(false);
+ debugMenu.add(breakOnReturn);
+
+ add(fileMenu);
+ add(editMenu);
+ //add(plafMenu);
+ add(debugMenu);
+ JMenuItem item;
+ windowMenu.add(item = new JMenuItem("Cascade", 'A'));
+ item.addActionListener(this);
+ windowMenu.add(item = new JMenuItem("Tile", 'T'));
+ item.addActionListener(this);
+ windowMenu.addSeparator();
+ windowMenu.add(item = new JMenuItem("Console", 'C'));
+ item.addActionListener(this);
+ add(windowMenu);
+
+ updateEnabled(false);
+ }
+
+ /**
+ * Returns the "Break on exceptions" menu item.
+ */
+ public JCheckBoxMenuItem getBreakOnExceptions() {
+ return breakOnExceptions;
+ }
+
+ /**
+ * Returns the "Break on enter" menu item.
+ */
+ public JCheckBoxMenuItem getBreakOnEnter() {
+ return breakOnEnter;
+ }
+
+ /**
+ * Returns the "Break on return" menu item.
+ */
+ public JCheckBoxMenuItem getBreakOnReturn() {
+ return breakOnReturn;
+ }
+
+ /**
+ * Returns the "Debug" menu.
+ */
+ public JMenu getDebugMenu() {
+ return getMenu(2);
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ String plaf_name = null;
+ if (cmd.equals("Metal")) {
+ plaf_name = "javax.swing.plaf.metal.MetalLookAndFeel";
+ } else if (cmd.equals("Windows")) {
+ plaf_name = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
+ } else if (cmd.equals("Motif")) {
+ plaf_name = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
+ } else {
+ Object source = e.getSource();
+ if (source == breakOnExceptions) {
+ debugGui.dim.setBreakOnExceptions(breakOnExceptions.isSelected());
+ } else if (source == breakOnEnter) {
+ debugGui.dim.setBreakOnEnter(breakOnEnter.isSelected());
+ } else if (source == breakOnReturn) {
+ debugGui.dim.setBreakOnReturn(breakOnReturn.isSelected());
+ } else {
+ debugGui.actionPerformed(e);
+ }
+ return;
+ }
+ try {
+ UIManager.setLookAndFeel(plaf_name);
+ SwingUtilities.updateComponentTreeUI(debugGui);
+ SwingUtilities.updateComponentTreeUI(debugGui.dlg);
+ } catch (Exception ignored) {
+ //ignored.printStackTrace();
+ }
+ }
+
+ /**
+ * Adds a file to the window menu.
+ */
+ public void addFile(String url) {
+ int count = windowMenu.getItemCount();
+ JMenuItem item;
+ if (count == 4) {
+ windowMenu.addSeparator();
+ count++;
+ }
+ JMenuItem lastItem = windowMenu.getItem(count -1);
+ boolean hasMoreWin = false;
+ int maxWin = 5;
+ if (lastItem != null &&
+ lastItem.getText().equals("More Windows...")) {
+ hasMoreWin = true;
+ maxWin++;
+ }
+ if (!hasMoreWin && count - 4 == 5) {
+ windowMenu.add(item = new JMenuItem("More Windows...", 'M'));
+ item.setActionCommand("More Windows...");
+ item.addActionListener(this);
+ return;
+ } else if (count - 4 <= maxWin) {
+ if (hasMoreWin) {
+ count--;
+ windowMenu.remove(lastItem);
+ }
+ String shortName = SwingGui.getShortName(url);
+
+ windowMenu.add(item = new JMenuItem((char)('0' + (count-4)) + " " + shortName, '0' + (count - 4)));
+ if (hasMoreWin) {
+ windowMenu.add(lastItem);
+ }
+ } else {
+ return;
+ }
+ item.setActionCommand(url);
+ item.addActionListener(this);
+ }
+
+ /**
+ * Updates the enabledness of menu items.
+ */
+ public void updateEnabled(boolean interrupted) {
+ for (int i = 0; i != interruptOnlyItems.size(); ++i) {
+ JMenuItem item = (JMenuItem)interruptOnlyItems.elementAt(i);
+ item.setEnabled(interrupted);
+ }
+
+ for (int i = 0; i != runOnlyItems.size(); ++i) {
+ JMenuItem item = (JMenuItem)runOnlyItems.elementAt(i);
+ item.setEnabled(!interrupted);
+ }
+ }
+}
+
+/**
+ * Class to consolidate all cases that require to implement Runnable
+ * to avoid class generation bloat.
+ */
+class RunProxy implements Runnable {
+
+ // Constants for 'type'.
+ static final int OPEN_FILE = 1;
+ static final int LOAD_FILE = 2;
+ static final int UPDATE_SOURCE_TEXT = 3;
+ static final int ENTER_INTERRUPT = 4;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The type of Runnable this object is. Takes one of the constants
+ * defined in this class.
+ */
+ private int type;
+
+ /**
+ * The name of the file to open or load.
+ */
+ String fileName;
+
+ /**
+ * The source text to update.
+ */
+ String text;
+
+ /**
+ * The source for which to update the text.
+ */
+ Dim.SourceInfo sourceInfo;
+
+ /**
+ * The frame to interrupt in.
+ */
+ Dim.StackFrame lastFrame;
+
+ /**
+ * The name of the interrupted thread.
+ */
+ String threadTitle;
+
+ /**
+ * The message of the exception thrown that caused the thread
+ * interruption, if any.
+ */
+ String alertMessage;
+
+ /**
+ * Creates a new RunProxy.
+ */
+ public RunProxy(SwingGui debugGui, int type) {
+ this.debugGui = debugGui;
+ this.type = type;
+ }
+
+ /**
+ * Runs this Runnable.
+ */
+ public void run() {
+ switch (type) {
+ case OPEN_FILE:
+ try {
+ debugGui.dim.compileScript(fileName, text);
+ } catch (RuntimeException ex) {
+ MessageDialogWrapper.showMessageDialog(
+ debugGui, ex.getMessage(), "Error Compiling "+fileName,
+ JOptionPane.ERROR_MESSAGE);
+ }
+ break;
+
+ case LOAD_FILE:
+ try {
+ debugGui.dim.evalScript(fileName, text);
+ } catch (RuntimeException ex) {
+ MessageDialogWrapper.showMessageDialog(
+ debugGui, ex.getMessage(), "Run error for "+fileName,
+ JOptionPane.ERROR_MESSAGE);
+ }
+ break;
+
+ case UPDATE_SOURCE_TEXT:
+ {
+ String fileName = sourceInfo.url();
+ if (!debugGui.updateFileWindow(sourceInfo) &&
+ !fileName.equals("<stdin>")) {
+ debugGui.createFileWindow(sourceInfo, -1);
+ }
+ }
+ break;
+
+ case ENTER_INTERRUPT:
+ debugGui.enterInterruptImpl(lastFrame, threadTitle, alertMessage);
+ break;
+
+ default:
+ throw new IllegalArgumentException(String.valueOf(type));
+
+ }
+ }
+}