gt:             if (historyIndex >= 0) {
context: /* -*- 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--;
  