content: /*
   *    This program is free software; you can redistribute it and/or modify
   *    it under the terms of the GNU General Public License as published by
   *    the Free Software Foundation; either version 2 of the License, or
   *    (at your option) any later version.
   *
   *    This program is distributed in the hope that it will be useful,
   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *    GNU General Public License for more details.
   *
   *    You should have received a copy of the GNU General Public License
   *    along with this program; if not, write to the Free Software
   *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  /*
   * FileScriptingPanel.java
   * Copyright (C) 2009 University of Waikato, Hamilton, New Zealand
   * Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  
   */
  
  package weka.gui.scripting;
  
  import weka.core.Utils;
  import weka.gui.ComponentHelper;
  import weka.gui.DocumentPrinting;
  import weka.gui.ExtensionFileFilter;
  import weka.gui.PropertyDialog;
  import weka.gui.scripting.event.ScriptExecutionEvent;
  import weka.gui.scripting.event.ScriptExecutionListener;
  import weka.gui.scripting.event.TitleUpdatedEvent;
  import weka.gui.scripting.event.ScriptExecutionEvent.Type;
  
  import java.awt.BorderLayout;
  import java.awt.Dialog;
  import java.awt.Dimension;
  import java.awt.Font;
  import java.awt.Frame;
  import java.awt.event.ActionEvent;
  import java.awt.event.WindowEvent;
  import java.awt.event.WindowListener;
  import java.io.File;
  import java.util.HashMap;
  
  import javax.swing.AbstractAction;
  import javax.swing.Action;
  import javax.swing.BorderFactory;
  import javax.swing.JDialog;
  import javax.swing.JFileChooser;
  import javax.swing.JFrame;
  import javax.swing.JInternalFrame;
  import javax.swing.JLabel;
  import javax.swing.JMenu;
  import javax.swing.JMenuBar;
  import javax.swing.JMenuItem;
  import javax.swing.JOptionPane;
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
  import javax.swing.JTextArea;
  import javax.swing.JTextPane;
  import javax.swing.KeyStroke;
  import javax.swing.event.DocumentEvent;
  import javax.swing.event.DocumentListener;
  import javax.swing.event.UndoableEditEvent;
  import javax.swing.event.UndoableEditListener;
  import javax.swing.text.DefaultEditorKit;
  import javax.swing.text.Document;
  import javax.swing.text.JTextComponent;
  import javax.swing.undo.CannotRedoException;
  import javax.swing.undo.CannotUndoException;
  import javax.swing.undo.UndoManager;
  
  /**
   * Supports loading/saving of files.
   * 
   * @author  fracpete (fracpete at waikato dot ac dot nz)
   * @author  Sun Microsystems Inc (see <a href="http://java.sun.com/docs/books/tutorial/uiswing/examples/components/TextComponentDemoProject/src/components/TextComponentDemo.java">TextComponentDemo.java</a>)
   * @version $Revision: 5144 $
   */
  public abstract class FileScriptingPanel
    extends ScriptingPanel 
    implements ScriptExecutionListener {
  
    /** for serialization. */
    private static final long serialVersionUID = 1583670545010241816L;
  
    /**
     * A slightly extended action class.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    public abstract class BasicAction
      extends AbstractAction {
      
      /** for serialization. */
      private static final long serialVersionUID = 2821117985661550385L;
  
      /**
       * Constructor for setting up an action.
       * 
       * @param name	the name of the action (to be displayed in menu/button)
       * @param icon	the icon name (no path required if in weka/gui/images), can be null
       * @param accel	the accelerator command, e.g., "ctrl N", can be null
       * @param mnemonic	the mnemonic character
       */
      public BasicAction(String name, String icon, String accel, Character mnemonic) {
        super(name);
        
        if ((icon != null) && (icon.length() > 0))
  	putValue(Action.SMALL_ICON, ComponentHelper.getImageIcon(icon));
        if ((accel != null) && (accel.length() > 0))
  	putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(accel));
        if (mnemonic != null)
  	putValue(Action.MNEMONIC_KEY, new Integer(mnemonic.charValue()));
      }
    }
    
    /**
     * The New action.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class NewAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = -8665722554539726090L;
  
      /**
       * Initializes the action.
       */
      public NewAction() {
        super("New", "new.gif", "ctrl N", 'N');
        setEnabled(true);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        m_Script.empty();
        notifyTitleUpdatedListeners(new TitleUpdatedEvent(FileScriptingPanel.this));
      }
    }
  
    /**
     * The Open action.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class OpenAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = -4496148485267789162L;
  
      /**
       * Initializes the action.
       */
      public OpenAction() {
        super("Open...", "open.gif", "ctrl O", 'O');
        setEnabled(true);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        boolean	ok;
        int	retVal;
        
        if (!checkModified())
  	return;
        
        retVal = m_FileChooser.showOpenDialog(FileScriptingPanel.this);
        if (retVal != JFileChooser.APPROVE_OPTION)
          return;
        
        ok = m_Script.open(m_FileChooser.getSelectedFile());
        m_TextCode.setCaretPosition(0);
        if (!ok)
  	JOptionPane.showMessageDialog(
  	    FileScriptingPanel.this, 
  	    "Couldn't open file '" + m_FileChooser.getSelectedFile() + "'!");
        
        notifyTitleUpdatedListeners(new TitleUpdatedEvent(FileScriptingPanel.this));
      }
    }
  
    /**
     * The Save action.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class SaveAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = -74651145892063975L;
      
      /** whether to bring up the save dialog all the time. */
      protected boolean m_ShowDialog;
      
      /**
       * Initializes the action.
       * 
       * @param name		the name of the action
       * @param showDialog	whether to always show the dialog
       */
      public SaveAction(String name, boolean showDialog) {
        super(name, (showDialog ? "" : "save.gif"), (showDialog ? "ctrl shift S" : "ctrl S"), (showDialog ? 'a' : 'S'));
        m_ShowDialog = showDialog;
        setEnabled(true);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        boolean	ok;
        int	retVal;
        
        if (m_ShowDialog || (m_Script.getFilename() == null)) {
  	retVal = m_FileChooser.showSaveDialog(FileScriptingPanel.this);
  	if (retVal != JFileChooser.APPROVE_OPTION)
  	  return;
  	ok = m_Script.saveAs(m_FileChooser.getSelectedFile());
        }
        else {
  	ok = m_Script.save();
        }
        
        if (!ok) {
  	if (m_Script.getFilename() != null)
  	  JOptionPane.showMessageDialog(
  	      FileScriptingPanel.this, 
  	      "Failed to save file '" + m_FileChooser.getSelectedFile() + "'!");
  	else
  	  JOptionPane.showMessageDialog(
  	      FileScriptingPanel.this, 
  	      "Failed to save file!");
        }
        else {
  	m_SaveAction.setEnabled(false);
        }
  
        notifyTitleUpdatedListeners(new TitleUpdatedEvent(FileScriptingPanel.this));
      }
    }
  
    /**
     * The Print action.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class PrintAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = -6246539539545724632L;
  
      /**
       * Initializes the action.
       */
      public PrintAction() {
        super("Print...", "print.gif", "ctrl P", 'P');
        setEnabled(true);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        JTextPane		pane;
        DocumentPrinting 	doc;
        
        pane = newCodePane();
        pane.setText(m_TextCode.getText());
        doc = new DocumentPrinting();
        doc.print(pane);
      }
    }
  
    /**
     * The Clear output action.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class ClearOutputAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = 47986890456997211L;
  
      /**
       * Initializes the action.
       */
      public ClearOutputAction() {
        super("Clear output", "", "F2", 'C');
        setEnabled(true);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        m_TextOutput.setText("");
      }
    }
  
    /**
     * The Exit action. Sends out a WindowEvent/WINDOW_CLOSED to all 
     * WindowListener objects of a jframe.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class ExitAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = -5884709836238884180L;
  
      /**
       * Initializes the action.
       */
      public ExitAction() {
        super("Exit", "", "", 'x');
        setEnabled(true);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        Dialog		dialog;
        Frame		frame;
        JFrame		jframe;
        JInternalFrame	jintframe;
        int		i;
        WindowListener[] 	listeners;
        WindowEvent	event;
        
        if (!checkModified())
  	return;
        
        if (PropertyDialog.getParentDialog(FileScriptingPanel.this) != null) {
  	dialog = PropertyDialog.getParentDialog(FileScriptingPanel.this);
  	dialog.setVisible(false);
        }
        else if (PropertyDialog.getParentFrame(FileScriptingPanel.this) != null) {
  	jintframe = PropertyDialog.getParentInternalFrame(FileScriptingPanel.this);
  	if (jintframe != null) {
  	  jintframe.doDefaultCloseAction();
  	}
  	else {
  	  frame = PropertyDialog.getParentFrame(FileScriptingPanel.this);
  	  if (frame instanceof JFrame) {
  	    jframe = (JFrame) frame;
  	    if (jframe.getDefaultCloseOperation() == JFrame.HIDE_ON_CLOSE)
  	      jframe.setVisible(false);
  	    else if (jframe.getDefaultCloseOperation() == JFrame.DISPOSE_ON_CLOSE)
  	      jframe.dispose();
  	    else if (jframe.getDefaultCloseOperation() == JFrame.EXIT_ON_CLOSE)
  	      System.exit(0);
  
  	    // notify listeners
  	    listeners = jframe.getWindowListeners();
  	    event     = new WindowEvent(jframe, WindowEvent.WINDOW_CLOSED);
  	    for (i = 0; i < listeners.length; i++)
  	      listeners[i].windowClosed(event);
  	  }
  	  else {
  	    frame.dispose();
  	}
  	}
        }
      }
    }
  
    /**
     * The Undo action.
     * 
     * @author  Sun Microsystems Inc (see <a href="http://java.sun.com/docs/books/tutorial/uiswing/examples/components/TextComponentDemoProject/src/components/TextComponentDemo.java">TextComponentDemo.java</a>)
     * @version $Revision: 5144 $
     */
    protected class UndoAction 
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = 4298096648424808522L;
  
      /**
       * Initializes the action.
       */
      public UndoAction() {
        super("Undo", "undo.gif", "ctrl Z", 'U');
        setEnabled(false);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        try {
  	m_Undo.undo();
        }
        catch (CannotUndoException ex) {
  	System.out.println("Unable to undo: " + ex);
  	ex.printStackTrace();
        }
        updateUndoState();
        m_RedoAction.updateRedoState();
      }
  
      /**
       * Updates the redo state.
       */
      protected void updateUndoState() {
        if (m_Undo.canUndo()) {
  	setEnabled(true);
  	putValue(Action.NAME, m_Undo.getUndoPresentationName());
        }
        else {
  	setEnabled(false);
  	putValue(Action.NAME, "Undo");
        }
      }
    }
  
    /**
     * The Redo action.
     * 
     * @author  Sun Microsystems Inc (see <a href="http://java.sun.com/docs/books/tutorial/uiswing/examples/components/TextComponentDemoProject/src/components/TextComponentDemo.java">TextComponentDemo.java</a>)
     * @version $Revision: 5144 $
     */
    protected class RedoAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = 4533966901523279350L;
  
      /**
       * Initializes the action.
       */
      public RedoAction() {
        super("Redo", "redo.gif", "ctrl Y", 'R');
        setEnabled(false);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        try {
  	m_Undo.redo();
        }
        catch (CannotRedoException ex) {
  	System.out.println("Unable to redo: " + ex);
  	ex.printStackTrace();
        }
        updateRedoState();
        m_UndoAction.updateUndoState();
      }
  
      /**
       * Updates the redo state.
       */
      protected void updateRedoState() {
        if (m_Undo.canRedo()) {
  	setEnabled(true);
  	putValue(Action.NAME, m_Undo.getRedoPresentationName());
        }
        else {
  	setEnabled(false);
  	putValue(Action.NAME, "Redo");
        }
      }
    }
  
    /**
     * The Commandline args action.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class CommandlineArgsAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = -3183470039010826204L;
  
      /**
       * Initializes the action.
       */
      public CommandlineArgsAction() {
        super("Arguments...", "properties.gif", "", 'g');
        setEnabled(true);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        String	retVal;
        
        retVal = JOptionPane.showInputDialog(
  	  FileScriptingPanel.this, 
  	  "Please enter the command-line arguments", 
  	  Utils.joinOptions(m_Args));
        if (retVal == null)
  	return;
        
        try {
  	m_Args = Utils.splitOptions(retVal);
        }
        catch (Exception ex) {
  	m_Args = new String[0];
  	ex.printStackTrace();
  	JOptionPane.showMessageDialog(
  	    FileScriptingPanel.this, 
  	    "Error setting command-line arguments:\n" + ex, 
  	    "Error", 
  	    JOptionPane.ERROR_MESSAGE);
        }
      }
    }
  
    /**
     * The Start action.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class StartAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = -7936456072955996220L;
  
      /**
       * Initializes the action.
       */
      public StartAction() {
        super((m_Script.canExecuteScripts() ? "Start" : "Start (missing classes?)"), "run.gif", "ctrl R", 'S');
        setEnabled(false);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        if (!checkModified())
  	return;
        
        if (m_Script.getFilename() == null)
  	return;
        
        try {
  	m_Script.start(m_Args);
        }
        catch (Exception ex) {
  	ex.printStackTrace();
  	JOptionPane.showMessageDialog(
  	    FileScriptingPanel.this, 
  	    "Error running script:\n" + ex, 
  	    "Error", 
  	    JOptionPane.ERROR_MESSAGE);
        }
      }
    }
  
    /**
     * The Stop action.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class StopAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = 8764023289575718872L;
  
      /**
       * Initializes the action.
       */
      public StopAction() {
        super("Stop", "stop.gif", "ctrl shift R", 'o');
        setEnabled(false);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        try {
  	m_Script.stop();
        }
        catch (Exception ex) {
  	// ignored
        }
      }
    }
  
    /**
     * The About action.
     * 
     * @author  fracpete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 5144 $
     */
    protected class AboutAction
      extends BasicAction {
      
      /** for serialization. */
      private static final long serialVersionUID = -6420463480569171227L;
  
      /**
       * Initializes the action.
       */
      public AboutAction() {
        super("About...", "", "F1", 'A');
        setEnabled(true);
      }
  
      /**
       * Fired when action got executed.
       * 
       * @param e		the event
       */
      public void actionPerformed(ActionEvent e) {
        JDialog	dialog;
        
        if (PropertyDialog.getParentDialog(FileScriptingPanel.this) != null)
  	dialog = new JDialog(PropertyDialog.getParentDialog(FileScriptingPanel.this), getName());
        else
  	dialog = new JDialog(PropertyDialog.getParentFrame(FileScriptingPanel.this), getName());
        dialog.setTitle((String) getValue(Action.NAME));
        dialog.getContentPane().setLayout(new BorderLayout());
        dialog.getContentPane().add(getAboutPanel());
        dialog.pack();
        dialog.setLocationRelativeTo(FileScriptingPanel.this);
        dialog.setVisible(true);
      }
    }
    
    /**
     * This listener class listens for edits that can be undone.
     * 
     * @author  Sun Microsystems Inc (see <a href="http://java.sun.com/docs/books/tutorial/uiswing/examples/components/TextComponentDemoProject/src/components/TextComponentDemo.java">TextComponentDemo.java</a>)
     * @version $Revision: 5144 $
     */
    protected class ScriptUndoableEditListener
      implements UndoableEditListener {
      
      /**
       * Gets called when an undoable event gets triggered.
       * 
       * @param e		the event
       */
      public void undoableEditHappened(UndoableEditEvent e) {
        //Remember the edit and update the menus.
        m_Undo.addEdit(e.getEdit());
        m_UndoAction.updateUndoState();
        m_RedoAction.updateRedoState();
      }
    }
    
    /** the directory with the scripting-specific images. */
    public final static String IMAGES_DIR = "weka/gui/scripting/images";
    
    /** for loading/saving file. */
    protected JFileChooser m_FileChooser;
    
    /** the script. */
    protected Script m_Script;
  
    /** the script area. */
    protected JTextArea m_ScriptArea;
  
    /** the output area. */
    protected JTextArea m_OutputArea;
    
    /** for informing the user. */
    protected JLabel m_LabelInfo;
    
    /** for storing the actions under their name. */
    protected HashMap<Object, Action> m_Actions;
  
    /** the new action. */
    protected NewAction m_NewAction;
    /** the open action. */
    protected OpenAction m_OpenAction;
    /** the Save action. */
    protected SaveAction m_SaveAction;
    /** the Save as action. */
    protected SaveAction m_SaveAsAction;
    /** the Print action. */
    protected PrintAction m_PrintAction;
    /** the clear output action. */
    protected ClearOutputAction m_ClearOutputAction;
    /** the exit action. */
    protected ExitAction m_ExitAction;
    /** the undo action. */
    protected UndoAction m_UndoAction;
    /** the redo action. */
    protected RedoAction m_RedoAction;
    /** the cut action. */
    protected Action m_CutAction;
    /** the copy action. */
    protected Action m_CopyAction;
    /** the paste action. */
    protected Action m_PasteAction;
    /** the start action. */
    protected StartAction m_StartAction;
    /** the stop action. */
    protected StopAction m_StopAction;
    /** the arguments action. */
    protected CommandlineArgsAction m_ArgsAction;
    /** the about action. */
    protected AboutAction m_AboutAction;
  
    /** the undo manager. */
    protected UndoManager m_Undo;
    
    /** the text pane with the code. */
    protected JTextPane m_TextCode;
    
    /** the text pane for the output. */
    protected JTextPane m_TextOutput;
    
    /** the commandline arguments to use. */
    protected String[] m_Args;
    
    /**
     * For initializing member variables.
     */
    protected void initialize() {
      super.initialize();
      
      m_FileChooser = new JFileChooser();
      m_FileChooser.setAcceptAllFileFilterUsed(true);
      m_FileChooser.setMultiSelectionEnabled(false);
      
      m_Undo = new UndoManager();
      m_Args = new String[0];
    }
    
    /**
     * Sets up the GUI after initializing the members.
     */
    protected void initGUI() {
      JPanel	panel;
      
      super.initGUI();
  
      setLayout(new BorderLayout(0, 5));
      
      m_TextCode = newCodePane();
      m_TextCode.setFont(new Font("monospaced", Font.PLAIN, 12));
      m_TextCode.getDocument().addUndoableEditListener(new ScriptUndoableEditListener());
      m_TextCode.getDocument().addDocumentListener(new DocumentListener() {
        public void changedUpdate(DocumentEvent e) {
  	update();
        }
        public void insertUpdate(DocumentEvent e) {
  	update();
        }
        public void removeUpdate(DocumentEvent e) {
  	update();
        }
        protected void update() {
  	Document doc = m_TextCode.getDocument();
  	m_StartAction.setEnabled((doc.getLength() > 0) && m_Script.canExecuteScripts());
  	m_SaveAction.setEnabled(true);
  	notifyTitleUpdatedListeners(new TitleUpdatedEvent(FileScriptingPanel.this));
        }
      });
      add(new JScrollPane(m_TextCode), BorderLayout.CENTER);
      
      panel = new JPanel(new BorderLayout(0, 5));
      panel.setPreferredSize(new Dimension(50, 200));
      add(panel, BorderLayout.SOUTH);
      
      m_TextOutput = new JTextPane();
      panel.add(new JScrollPane(m_TextOutput), BorderLayout.CENTER);
      
      m_LabelInfo = new JLabel(" ");
      m_LabelInfo.setBorder(BorderFactory.createLoweredBevelBorder());
      panel.add(m_LabelInfo, BorderLayout.SOUTH);
    }
    
    /**
     * Finishes up after initializing members and setting up the GUI.
     */
    protected void initFinish() {
      ExtensionFileFilter[]	filters;
      int				i;
      
      super.initFinish();
      
      m_Script = newScript(m_TextCode.getDocument());
      m_Script.addScriptFinishedListener(this);
      filters  = m_Script.getFilters();
      for (i = filters.length - 1; i >= 0; i--)
        m_FileChooser.addChoosableFileFilter(filters[i]);
      
      m_Actions = createActionTable(m_TextCode);
  
      // file
      m_NewAction         = new NewAction();
      m_OpenAction        = new OpenAction();
      m_SaveAction        = new SaveAction("Save", false);
      m_SaveAsAction      = new SaveAction("Save As...", true);
      m_PrintAction       = new PrintAction();
      m_ClearOutputAction = new ClearOutputAction();
      m_ExitAction        = new ExitAction();
      
      // edit
      m_UndoAction        = new UndoAction();
      m_RedoAction        = new RedoAction();
      m_CutAction         = updateAction(m_Actions.get(DefaultEditorKit.cutAction), "Cut", "cut.gif", "ctrl X", 'C');
      m_CopyAction        = updateAction(m_Actions.get(DefaultEditorKit.copyAction), "Copy", "copy.gif", "ctrl C", 'o');
      m_PasteAction       = updateAction(m_Actions.get(DefaultEditorKit.pasteAction), "Paste", "paste.gif", "ctrl V", 'P');
      
      // script
      m_StartAction       = new StartAction();
      m_StopAction        = new StopAction();
      m_ArgsAction        = new CommandlineArgsAction();
      
      // help
      m_AboutAction       = new AboutAction();
    }
    
    /**
     * Updates the action and returns it.
     * 
     * @param action	the action to update
     * @param name	the name to be used as display, can be null
     * @param icon	the icon to use (if located in weka/gui/images, not path required), can be null
     * @param accel	the accelerator command to use (e.g., "ctrl N"), can be null
     * @param mnemonic	the mnemonic character to use, can be null
     * @return		the updated action
     */
    protected Action updateAction(Action action, String name, String icon, String accel, Character mnemonic) {
      Action	result;
      
      // did we already update that action for another component?
      if (action == null) {
        result = m_Actions.get(name);
        return result;
      }
      
      result = action;
      
      if ((name != null) && (name.length() > 0))
        result.putValue(Action.NAME, name);
      if ((icon != null) && (icon.length() > 0))
        result.putValue(Action.SMALL_ICON, ComponentHelper.getImageIcon(icon));
      if ((accel != null) && (accel.length() > 0))
        result.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(accel));
      if (mnemonic != null)
        result.putValue(Action.MNEMONIC_KEY, new Integer(mnemonic.charValue()));
      
      return result;
    }
    
    /**
     * Creates a new JTextPane for the code.
     * 
     * @return		the text pane
     */
    protected abstract JTextPane newCodePane();
  
    /**
     * Returns an initialized script object.
     * 
     * @param doc		the document to use as basis
     * @return		the initialized script
     */
    protected abstract Script newScript(Document doc);
  
    /**
     * Gets sent when a script finishes execution.
     * 
     * @param e		the event
     */
    public void scriptFinished(ScriptExecutionEvent e) {
      if (e.getType() == Type.FINISHED)
        showInfo("Script execution finished");
      else if (e.getType() == Type.STOPPED)
        showInfo("Script execution stopped by user");
      else if (e.getType() == Type.ERROR)
        showInfo("Script execution failed" + (e.hasAdditional() ? (": " + e.getAdditional()) : ""));
      
      if (e.getType() != Type.STARTED) {
        m_NewAction.setEnabled(true);
        m_OpenAction.setEnabled(true);
        m_SaveAction.setEnabled(true);
        m_SaveAsAction.setEnabled(true);
        m_CutAction.setEnabled(true);
        m_CopyAction.setEnabled(true);
        m_PasteAction.setEnabled(true);
        m_StartAction.setEnabled(true);
        m_StopAction.setEnabled(false);
      }
      else {
        m_NewAction.setEnabled(false);
        m_OpenAction.setEnabled(false);
        m_SaveAction.setEnabled(false);
        m_SaveAsAction.setEnabled(false);
        m_CutAction.setEnabled(false);
        m_CopyAction.setEnabled(false);
        m_PasteAction.setEnabled(false);
        m_StartAction.setEnabled(false);
        m_StopAction.setEnabled(true);
      }
    }
    
    /**
     * The following two methods allow us to find an
     * action provided by the editor kit by its name.
     * 
     * @param comp	the component to get the actions from
     * @return		the relation
     */
    protected HashMap<Object, Action> createActionTable(JTextComponent comp) {
      HashMap<Object, Action> 	result;
      int				i;
      Action[] 			actions;
      Action 			action;
      
      result  = new HashMap<Object, Action>();
      actions = comp.getActions();
      for (i = 0; i < actions.length; i++) {
        action = actions[i];
        result.put(action.getValue(Action.NAME), action);
      }
      
      return result;
    }
    
    /**
     * Returns a panel to be displayed with the AboutAction.
     * 
     * @return		the panel with some information on the scripting panel
     */
    protected abstract JPanel getAboutPanel();
    
    /**
     * Returns the title (without the filename).
     * 
     * @return		the plain title
     */
    public abstract String getPlainTitle();
    
    /**
     * Returns the current title for the frame/dialog.
     * 
     * @return		the title
     */
    public String getTitle() {
      String	result;
      
      result = getPlainTitle();
      
      if (m_Script.isModified())
        result = "*" + result;
      
      if (m_Script.getFilename() != null)
        result += " [" + m_Script.getFilename() + "]";
      
      return result;
    }
    
    /**
     * Returns the text area that is used for displaying output on stdout
     * and stderr.
     * 
     * @return		the JTextArea
     */
    public JTextPane getOutput() {
      return m_TextOutput;
    }
    
    /**
     * Returns the menu bar to to be displayed in the frame.
     * 
     * @return		the menu bar, null if not applicable
     */
    public JMenuBar getMenuBar() {
      JMenuBar	result;
      JMenu	menu;
      JMenuItem	menuitem;
      
      result = new JMenuBar();
      
      // File
      menu = new JMenu("File");
      menu.setMnemonic('F');
      result.add(menu);
      
      // File/New
      menuitem = new JMenuItem(m_NewAction);
      menu.add(menuitem);
      
      // File/Open
      menuitem = new JMenuItem(m_OpenAction);
      menu.addSeparator();
      menu.add(menuitem);
      
      // File/Save
      menuitem = new JMenuItem(m_SaveAction);
      menu.add(menuitem);
      
      // File/SaveAs
      menuitem = new JMenuItem(m_SaveAsAction);
      menu.add(menuitem);
      
      // File/Print
      menuitem = new JMenuItem(m_PrintAction);
      menu.addSeparator();
      menu.add(menuitem);
      
      // File/Clear output
      menuitem = new JMenuItem(m_ClearOutputAction);
      menu.addSeparator();
      menu.add(menuitem);
      
      // File/Exit
      menuitem = new JMenuItem(m_ExitAction);
      menu.addSeparator();
      menu.add(menuitem);
      
      // Edit
      menu = new JMenu("Edit");
      menu.setMnemonic('E');
      result.add(menu);
      
      // Edit/Undo
      menuitem = new JMenuItem(m_UndoAction);
      menu.add(menuitem);
      
      // Edit/Redo
      menuitem = new JMenuItem(m_RedoAction);
      menu.add(menuitem);
      
      // Edit/Cut
      menuitem = new JMenuItem(m_CutAction);
      menu.addSeparator();
      menu.add(menuitem);
      
      // Edit/Copy
      menuitem = new JMenuItem(m_CopyAction);
      menu.add(menuitem);
      
      // Edit/Paste
      menuitem = new JMenuItem(m_PasteAction);
      menu.add(menuitem);
      
      // Script
      menu = new JMenu("Script");
      menu.setMnemonic('S');
      result.add(menu);
      
      // Script/Start
      menuitem = new JMenuItem(m_StartAction);
      menu.add(menuitem);
      
      // Script/Stop
      menuitem = new JMenuItem(m_StopAction);
      menu.add(menuitem);
      
      // Script/Arguments
      menuitem = new JMenuItem(m_ArgsAction);
      menu.add(menuitem);
      
      // Help
      menu = new JMenu("Help");
      menu.setMnemonic('H');
      result.add(menu);
      
      // Help/About
      menuitem = new JMenuItem(m_AboutAction);
      menu.add(menuitem);
      
      return result;
    }
    
    /**
     * Updates the info shown in the bottom panel.
     * 
     * @param msg		the message to display
     */
    protected void showInfo(String msg) {
      if (msg == null)
        msg = " ";
      m_LabelInfo.setText(msg);
    }
    
    /**
     * Opens the specified file.
     * 
     * @param file	the file to open
     */
    public void open(File file) {
      m_Script.open(file);
    }
    
    /**
     * Checks whether the script is modified and asks the user to save it or not.
     * If everything is fine and one can ignore the modified state, true is 
     * returned.
     * 
     * @return		true if one can proceed
     */
    protected boolean checkModified() {
      boolean	result;
      int		retVal;
      
      result = true;
      
      if (m_Script.isModified()) {
        retVal = JOptionPane.showConfirmDialog(
  	  FileScriptingPanel.this, 
  	  "Script not saved - save it now?", 
  	  "Confirm", 
  	  JOptionPane.YES_NO_CANCEL_OPTION);
        
        if (retVal == JOptionPane.YES_OPTION) {
  	if (m_Script.getFilename() != null)
  	  m_Script.save();
  	else
  	  m_SaveAsAction.actionPerformed(null);
  	result = !m_Script.isModified();
        }
        else if (retVal == JOptionPane.CANCEL_OPTION) {
  	result = false;
        }
      }
      
      return result;
    }
  }
  