/*FreeMind - A Program for creating and viewing Mindmaps
*Copyright (C) 2000-2007 Joerg Mueller, Daniel Polansky, Dimitri Polivaev, Christian Foltin and others.
*
*See COPYING for Details
*
*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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Created on 10.01.2007
*/
/*$Id: ScriptEditorPanel.java,v 1.1.2.18 2008/07/05 20:40:10 christianfoltin Exp $*/
package plugins.script;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.DefaultListModel;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import plugins.script.ScriptingEngine.ErrorHandler;
import freemind.controller.BlindIcon;
import freemind.controller.StructuredMenuHolder;
import freemind.controller.actions.generated.instance.ScriptEditorWindowConfigurationStorage;
import freemind.main.FreeMindMain;
import freemind.main.Resources;
import freemind.main.Tools;
/**
* @author foltin TODO:
* <ul>
* <li></li>
* <li>new script/delete script buttons</li>
* <li>rename script button</li>
* <li>undo feature?</li>
* <li>show line/column numbers in status bar</li>
* </ul>
*/
public class ScriptEditorPanel extends JDialog {
/**
*
*/
private static final long serialVersionUID = 3221975191441136520L;
private static final String WINDOW_PREFERENCE_STORAGE_PROPERTY = "plugins.script.ScriptEditorPanel/window_positions";
private final FreeMindMain mFrame;
private final ScriptModel mScriptModel;
private JList mScriptList;
private JTextArea mScriptTextField;
private DefaultListModel mListModel;
private Integer mLastSelected = null;
private JTextArea mScriptResultField;
private JSplitPane mCentralUpperPanel;
private JSplitPane mCentralPanel;
private Logger logger;
private JLabel mStatus;
private AbstractAction mRunAction;
private SignAction mSignAction;
private final class ResultFieldStream extends OutputStream {
public void write(int pByte) throws IOException {
mScriptResultField.append(new String(new byte[] { (byte) pByte }));
}
public void write(byte[] pB) throws IOException {
mScriptResultField.append(new String(pB));
}
}
private final class RunAction extends AbstractAction {
private RunAction(String pArg0) {
super(pArg0);
}
public void actionPerformed(ActionEvent arg0) {
storeCurrent();
if (!mScriptList.isSelectionEmpty()) {
mScriptResultField.setText("");
mScriptModel.executeScript(mScriptList.getSelectedIndex(),
getPrintStream(), getErrorHandler());
}
}
}
private final class SignAction extends AbstractAction {
private SignAction(String pArg0) {
super(pArg0);
}
public void actionPerformed(ActionEvent arg0) {
storeCurrent();
if (!mScriptList.isSelectionEmpty()) {
int selectedIndex = mScriptList.getSelectedIndex();
ScriptHolder script = mScriptModel.getScript(selectedIndex);
String signedScript = new SignedScriptHandler().signScript(
script.mScript, Resources.getInstance(), mFrame);
script.setScript(signedScript);
mScriptModel.setScript(selectedIndex, script);
mScriptTextField.setText(signedScript);
}
}
}
private final class CancelAction extends AbstractAction {
private CancelAction(String pArg0) {
super(pArg0);
}
public void actionPerformed(ActionEvent arg0) {
disposeDialog(true);
}
}
private final class ExitAction extends AbstractAction {
private ExitAction(String pArg0) {
super(pArg0);
}
public void actionPerformed(ActionEvent arg0) {
storeCurrent();
disposeDialog(false);
}
}
private final class NewScriptAction extends AbstractAction {
private NewScriptAction(String pArg0) {
super(pArg0);
}
public void actionPerformed(ActionEvent arg0) {
storeCurrent();
mLastSelected = null;
int scriptIndex = mScriptModel.addNewScript();
updateFields();
select(scriptIndex);
}
}
public static class ScriptHolder {
String mScript;
String mScriptName;
/**
* @param pScriptName
* script name (starting with "script"
* (ScriptingEngine.SCRIPT_PREFIX))
* @param pScript
* script content
*/
public ScriptHolder(String pScriptName, String pScript) {
super();
mScript = pScript;
mScriptName = pScriptName;
}
public String getScript() {
return mScript;
}
public String getScriptName() {
return mScriptName;
}
public ScriptHolder setScript(String pScript) {
mScript = pScript;
return this;
}
public ScriptHolder setScriptName(String pScriptName) {
mScriptName = pScriptName;
return this;
}
}
public interface ScriptModel {
int getAmountOfScripts();
/**
* @param pIndex
* zero-based
* @return a script
*/
ScriptHolder getScript(int pIndex);
void setScript(int pIndex, ScriptHolder pScript);
boolean executeScript(int pIndex, PrintStream outStream,
ErrorHandler pErrorHandler);
void storeDialogPositions(ScriptEditorPanel pPanel,
ScriptEditorWindowConfigurationStorage pStorage,
String pWindow_preference_storage_property);
ScriptEditorWindowConfigurationStorage decorateDialog(
ScriptEditorPanel pPanel,
String pWindow_preference_storage_property);
void endDialog(boolean pIsCanceled);
boolean isDirty();
/**
*
* @return the index of the new script.
*/
int addNewScript();
}
public ScriptEditorPanel(ScriptModel pScriptModel, FreeMindMain pFrame,
boolean pHasNewScriptFunctionality) {
super(pFrame.getJFrame(), true /* modal */);
logger = pFrame.getLogger(this.getClass().getName());
mScriptModel = pScriptModel;
mFrame = pFrame;
// build the panel:
this.setTitle(pFrame
.getResourceString("plugins/ScriptEditor/window.title"));
this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
disposeDialog(true);
}
});
Tools.addEscapeActionToDialog(this, new AbstractAction() {
public void actionPerformed(ActionEvent arg0) {
disposeDialog(true);
}
});
Container contentPane = this.getContentPane();
contentPane.setLayout(new BorderLayout());
mListModel = new DefaultListModel();
mScriptList = new JList(mListModel);
mScriptList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mScriptList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent pEvent) {
if (pEvent.getValueIsAdjusting())
return;
// System.out.println("List selection:" + pEvent);
select(mScriptList.getSelectedIndex());
}
});
// add(mScriptList, BorderLayout.WEST);
mScriptTextField = new JTextArea();
mScriptTextField.setFont(new Font("Monospaced", Font.PLAIN, 12));
mScriptTextField.setEnabled(false);
mScriptTextField.setTabSize(2);
mCentralUpperPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
mScriptList, new JScrollPane(mScriptTextField));
mCentralUpperPanel.setContinuousLayout(true);
mScriptResultField = new JTextArea();
mScriptResultField.setEditable(false);
mScriptResultField.setWrapStyleWord(true);
mCentralPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
mCentralUpperPanel, new JScrollPane(mScriptResultField));
mCentralPanel.setDividerLocation(0.8);
mCentralPanel.setContinuousLayout(true);
contentPane.add(mCentralPanel, BorderLayout.CENTER);
mStatus = new JLabel();
contentPane.add(mStatus, BorderLayout.SOUTH);
mScriptTextField.addCaretListener(new CaretListener() {
public void caretUpdate(CaretEvent arg0) {
int caretPosition = mScriptTextField.getCaretPosition();
try {
int lineOfOffset = mScriptTextField
.getLineOfOffset(caretPosition);
mStatus.setText("Line: "
+ (lineOfOffset + 1)
+ ", Column: "
+ (caretPosition
- mScriptTextField
.getLineStartOffset(lineOfOffset) + 1));
} catch (BadLocationException e) {
freemind.main.Resources.getInstance().logException(e);
}
}
});
updateFields();
mScriptTextField.repaint();
// menu:
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu();
Tools.setLabelAndMnemonic(menu,
pFrame.getResourceString("plugins/ScriptEditor.menu_actions"));
if (pHasNewScriptFunctionality) {
addAction(
menu,
new NewScriptAction(
pFrame.getResourceString("plugins/ScriptEditor.new_script")));
}
mRunAction = new RunAction(
pFrame.getResourceString("plugins/ScriptEditor.run"));
mRunAction.setEnabled(false);
addAction(menu, mRunAction);
mSignAction = new SignAction(
pFrame.getResourceString("plugins/ScriptEditor.sign"));
mSignAction.setEnabled(false);
addAction(menu, mSignAction);
AbstractAction cancelAction = new CancelAction(
pFrame.getResourceString("plugins/ScriptEditor.cancel"));
addAction(menu, cancelAction);
AbstractAction exitAction = new ExitAction(
pFrame.getResourceString("plugins/ScriptEditor.exit"));
addAction(menu, exitAction);
menuBar.add(menu);
this.setJMenuBar(menuBar);
// Retrieve window size and column positions.
ScriptEditorWindowConfigurationStorage storage = mScriptModel
.decorateDialog(this, WINDOW_PREFERENCE_STORAGE_PROPERTY);
if (storage != null) {
mCentralUpperPanel.setDividerLocation(storage.getLeftRatio());
mCentralPanel.setDividerLocation(storage.getTopRatio());
} else {
// bug fix: for new users, this is set to some reasonable values.
mCentralUpperPanel.setDividerLocation(100);
mCentralPanel.setDividerLocation(240);
}
}
private void addAction(JMenu menu, AbstractAction action) {
JMenuItem item = menu.add(action);
Tools.setLabelAndMnemonic(item,
(String) action.getValue(AbstractAction.NAME));
item.setIcon(new BlindIcon(StructuredMenuHolder.ICON_SIZE));
}
private void updateFields() {
mListModel.clear();
for (int i = 0; i < mScriptModel.getAmountOfScripts(); ++i) {
ScriptHolder script = mScriptModel.getScript(i);
mListModel.addElement(script.getScriptName());
}
}
private void select(int pIndex) {
mScriptTextField.setEnabled(pIndex >= 0);
mRunAction.setEnabled(pIndex >= 0);
mSignAction.setEnabled(pIndex >= 0);
if (pIndex < 0) {
mScriptTextField.setText("");
return;
}
storeCurrent();
// set new script
mScriptTextField.setText(mScriptModel.getScript(pIndex).getScript());
// set last one:
mLastSelected = new Integer(pIndex);
if (pIndex >= 0 && mScriptList.getSelectedIndex() != pIndex) {
mScriptList.setSelectedIndex(pIndex);
}
}
private void storeCurrent() {
if (mLastSelected != null) {
// store old value:
int oldIndex = mLastSelected.intValue();
mScriptModel.setScript(oldIndex, mScriptModel.getScript(oldIndex)
.setScript(mScriptTextField.getText()));
}
}
/**
* @param pIsCanceled
* TODO
*
*/
private void disposeDialog(boolean pIsCanceled) {
// store current script:
if (!mScriptList.isSelectionEmpty()) {
select(mScriptList.getSelectedIndex());
}
if (pIsCanceled && mScriptModel.isDirty()) {
// ask if really cancel:
int action = JOptionPane.showConfirmDialog(this, mFrame
.getResourceString("ScriptEditorPanel.changed_save"),
"FreeMind", JOptionPane.YES_NO_CANCEL_OPTION);
if (action == JOptionPane.CANCEL_OPTION)
return;
if(action == JOptionPane.YES_OPTION) {
pIsCanceled = false;
}
}
// store window positions:
ScriptEditorWindowConfigurationStorage storage = new ScriptEditorWindowConfigurationStorage();
storage.setLeftRatio(mCentralUpperPanel.getDividerLocation());
storage.setTopRatio(mCentralPanel.getDividerLocation());
mScriptModel.storeDialogPositions(this, storage,
WINDOW_PREFERENCE_STORAGE_PROPERTY);
this.setVisible(false);
this.dispose();
mScriptModel.endDialog(pIsCanceled);
}
PrintStream getPrintStream() {
return new PrintStream(new ResultFieldStream());
}
ErrorHandler getErrorHandler() {
return new ErrorHandler() {
public void gotoLine(int pLineNumber) {
logger.info("Line number: " + pLineNumber);
if (pLineNumber > 0
&& pLineNumber <= mScriptTextField.getLineCount()) {
Element element3 = mScriptTextField.getDocument()
.getDefaultRootElement();
Element element4 = element3.getElement(pLineNumber - 1);
if (element4 != null) {
mScriptTextField.select(
((int) element4.getStartOffset()),
element4.getEndOffset());
}
}
}
};
}
}