/* * Freeplane - mind map editor * Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev * * This file author is Christian Foltin * It is modified by Dimitry Polivaev in 2008. * * 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, see <http://www.gnu.org/licenses/>. */ package org.freeplane.plugin.script; import java.awt.BorderLayout; import java.awt.Container; 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.io.UnsupportedEncodingException; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.DefaultListModel; import javax.swing.JDialog; import javax.swing.JEditorPane; 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.JTextComponent; import org.freeplane.core.ui.MenuBuilder; import org.freeplane.core.ui.UIBuilder; import org.freeplane.core.ui.components.BlindIcon; import org.freeplane.core.ui.components.UITools; import org.freeplane.core.util.LogUtils; import org.freeplane.core.util.TextUtils; import org.freeplane.features.mode.Controller; import org.freeplane.plugin.script.ScriptingEngine.IErrorHandler; /** */ class ScriptEditorPanel extends JDialog { private static final String internalCharset = "UTF-16BE"; final private class CancelAction extends AbstractAction { /** * */ private static final long serialVersionUID = 1L; private CancelAction(final String pArg0) { super(pArg0); } public void actionPerformed(final ActionEvent arg0) { disposeDialog(true); } } final private class ExitAction extends AbstractAction { /** * */ private static final long serialVersionUID = 1L; private ExitAction(final String pArg0) { super(pArg0); } public void actionPerformed(final ActionEvent arg0) { storeCurrent(); disposeDialog(false); } } public interface IScriptModel { /** * @return the index of the new script. */ int addNewScript(); ScriptEditorWindowConfigurationStorage decorateDialog(ScriptEditorPanel pPanel, String pWindow_preference_storage_property); void endDialog(boolean pIsCanceled); Object executeScript(int pIndex, PrintStream outStream, IErrorHandler pErrorHandler); int getAmountOfScripts(); /** * @param pIndex * zero-based * @return a script */ ScriptHolder getScript(int pIndex); boolean isDirty(); void setScript(int pIndex, ScriptHolder pScript); void storeDialogPositions(ScriptEditorPanel pPanel, ScriptEditorWindowConfigurationStorage pStorage, String pWindow_preference_storage_property); } final private class NewScriptAction extends AbstractAction { /** * */ private static final long serialVersionUID = 1L; private NewScriptAction(final String pArg0) { super(pArg0); } public void actionPerformed(final ActionEvent arg0) { storeCurrent(); mLastSelected = null; final int scriptIndex = mScriptModel.addNewScript(); updateFields(); select(scriptIndex); } } final private class ResultFieldStream extends OutputStream { private final byte[] buf = new byte[2]; private int i = 0; @Override public void write(final int pByte) throws IOException { buf[i++] = (byte) pByte; if (i == 2) { mScriptResultField.append(new String(buf, internalCharset)); i = 0; } } @Override public void write(final byte b[], int off, int len) throws IOException { if (i == 1) { write(b[off++]); len--; } if (len <= 0) { return; } final int len2 = len & ~1; mScriptResultField.append(new String(b, off, len2, internalCharset)); if (len2 != len) { write(b[len2]); } } } final private class RunAction extends AbstractAction { /** * */ private static final long serialVersionUID = 1L; private RunAction(final String pArg0) { super(pArg0); } public void actionPerformed(final ActionEvent arg0) { storeCurrent(); if (!mScriptList.isSelectionEmpty()) { mScriptResultField.setText(""); Object result = null; try { result = mScriptModel.executeScript(mScriptList.getSelectedIndex(), getPrintStream(), getErrorHandler()); } catch (Throwable e2) { Throwable cause = e2.getCause(); String causeMessage = ""; if(cause != null && cause.getMessage()!= null) causeMessage = cause.getMessage(); final String message = e2.getMessage() != null ? e2.getMessage() : ""; UITools.errorMessage(e2.getClass().getName() + ": " + causeMessage + ((causeMessage.length() != 0 && message.length() != 0) ? ", " : "") + message); result = message; } getPrintStream().print(TextUtils.getText("plugins/ScriptEditor/window.Result") + result); } } } public static class ScriptHolder { String mScript; String mScriptName; /** * @param pScriptName * script name (starting with "script" * (ScriptingEngine.SCRIPT_PREFIX)) * @param pScript * script content */ public ScriptHolder(final String pScriptName, final String pScript) { super(); mScript = pScript; mScriptName = pScriptName; } public String getScript() { return mScript; } public String getScriptName() { return mScriptName; } public ScriptHolder setScript(final String pScript) { mScript = pScript; return this; } public ScriptHolder setScriptName(final String pScriptName) { mScriptName = pScriptName; return this; } } final private class SignAction extends AbstractAction { /** * */ private static final long serialVersionUID = 1L; // // final private Controller controller; private SignAction( final String pArg0) { super(pArg0); // this.controller = controller; } public void actionPerformed(final ActionEvent arg0) { storeCurrent(); if (!mScriptList.isSelectionEmpty()) { final int selectedIndex = mScriptList.getSelectedIndex(); final ScriptHolder script = mScriptModel.getScript(selectedIndex); final String signedScript = new SignedScriptHandler().signScript(script.mScript); script.setScript(signedScript); mScriptModel.setScript(selectedIndex, script); mScriptTextField.setText(signedScript); } } } /** * */ private static final long serialVersionUID = 1L; /** * */ private static final String WINDOW_PREFERENCE_STORAGE_PROPERTY = "plugins.script.ScriptEditorPanel/window_positions"; final private JSplitPane mCentralPanel; final private JSplitPane mCentralUpperPanel; private Integer mLastSelected = null; final private DefaultListModel mListModel; final private AbstractAction mRunAction; final private JList mScriptList; final private IScriptModel mScriptModel; final private JTextArea mScriptResultField; final private JTextComponent mScriptTextField; final private SignAction mSignAction; final private JLabel mStatus; public ScriptEditorPanel( final IScriptModel pScriptModel, final boolean pHasNewScriptFunctionality) { super(Controller.getCurrentController().getViewController().getJFrame(), true /* modal */); mScriptModel = pScriptModel; this.setTitle(TextUtils.getText("plugins/ScriptEditor/window.title")); this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(final WindowEvent event) { disposeDialog(true); } }); UITools.addEscapeActionToDialog(this, new AbstractAction() { /** * */ private static final long serialVersionUID = 1L; public void actionPerformed(final ActionEvent arg0) { disposeDialog(true); } }); final 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(final ListSelectionEvent pEvent) { if (pEvent.getValueIsAdjusting()) { return; } select(mScriptList.getSelectedIndex()); } }); final JEditorPane editorPane = new JEditorPane(); editorPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true); mScriptTextField = editorPane; mScriptTextField.setEnabled(false); mCentralUpperPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mScriptList, new JScrollPane(mScriptTextField)); try { editorPane.setContentType("text/groovy"); } catch (Exception e) { LogUtils.warn(e); editorPane.setContentType("text/plain"); } 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(final CaretEvent arg0) { final int caretPosition = mScriptTextField.getCaretPosition(); final int lineOfOffset = JSyntaxPaneProxy.getLineOfOffset(mScriptTextField, caretPosition); mStatus.setText("Line: " + (lineOfOffset + 1) + ", Column: " + (caretPosition - JSyntaxPaneProxy.getLineOfOffset(mScriptTextField, lineOfOffset) + 1)); } }); updateFields(); mScriptTextField.repaint(); final JMenuBar menuBar = new JMenuBar(); final JMenu menu = new JMenu(); MenuBuilder.setLabelAndMnemonic(menu, TextUtils.getRawText("plugins/ScriptEditor.menu_actions")); if (pHasNewScriptFunctionality) { addAction(menu, new NewScriptAction(TextUtils.getRawText("plugins/ScriptEditor.new_script"))); } mRunAction = new RunAction(TextUtils.getRawText("plugins/ScriptEditor.run")); mRunAction.setEnabled(false); addAction(menu, mRunAction); mSignAction = new SignAction(TextUtils.getRawText("plugins/ScriptEditor.sign")); mSignAction.setEnabled(false); addAction(menu, mSignAction); final AbstractAction cancelAction = new CancelAction(TextUtils.getRawText("plugins/ScriptEditor.cancel")); addAction(menu, cancelAction); final AbstractAction exitAction = new ExitAction(TextUtils.getRawText("plugins/ScriptEditor.exit")); addAction(menu, exitAction); menuBar.add(menu); this.setJMenuBar(menuBar); final ScriptEditorWindowConfigurationStorage storage = mScriptModel.decorateDialog(this, ScriptEditorPanel.WINDOW_PREFERENCE_STORAGE_PROPERTY); if (storage != null) { mCentralUpperPanel.setDividerLocation(storage.getLeftRatio()); mCentralPanel.setDividerLocation(storage.getTopRatio()); } else { mCentralUpperPanel.setDividerLocation(100); mCentralPanel.setDividerLocation(240); } } private void addAction(final JMenu menu, final AbstractAction action) { final JMenuItem item = menu.add(action); MenuBuilder.setLabelAndMnemonic(item, (String) action.getValue(Action.NAME)); item.setIcon(new BlindIcon(UIBuilder.ICON_SIZE)); } /** * @param pIsCanceled */ private void disposeDialog(final boolean pIsCanceled) { if (!mScriptList.isSelectionEmpty()) { select(mScriptList.getSelectedIndex()); } if (pIsCanceled && mScriptModel.isDirty()) { final int action = JOptionPane.showConfirmDialog(this, TextUtils .getText("ScriptEditorPanel.changed_cancel"), "Freeplane", JOptionPane.OK_CANCEL_OPTION); if (action == JOptionPane.CANCEL_OPTION) { return; } } final ScriptEditorWindowConfigurationStorage storage = new ScriptEditorWindowConfigurationStorage(); storage.setLeftRatio(mCentralUpperPanel.getDividerLocation()); storage.setTopRatio(mCentralPanel.getDividerLocation()); mScriptModel.storeDialogPositions(this, storage, ScriptEditorPanel.WINDOW_PREFERENCE_STORAGE_PROPERTY); this.setVisible(false); this.dispose(); mScriptModel.endDialog(pIsCanceled); } IErrorHandler getErrorHandler() { return new IErrorHandler() { public void gotoLine(final int pLineNumber) { JSyntaxPaneProxy.gotoPosition(mScriptTextField, pLineNumber, 1); } }; } PrintStream getPrintStream() { try { return new PrintStream(new ResultFieldStream(), false, internalCharset); } catch (final UnsupportedEncodingException e) { return null; } } private void select(final int pIndex) { mScriptTextField.setEnabled(pIndex >= 0); mRunAction.setEnabled(pIndex >= 0); mSignAction.setEnabled(pIndex >= 0); if (pIndex < 0) { mScriptTextField.setText(""); return; } storeCurrent(); mScriptTextField.setText(mScriptModel.getScript(pIndex).getScript()); mLastSelected = new Integer(pIndex); if (pIndex >= 0 && mScriptList.getSelectedIndex() != pIndex) { mScriptList.setSelectedIndex(pIndex); } } private void storeCurrent() { if (mLastSelected != null) { final int oldIndex = mLastSelected.intValue(); mScriptModel.setScript(oldIndex, mScriptModel.getScript(oldIndex).setScript(mScriptTextField.getText())); } } private void updateFields() { mListModel.clear(); for (int i = 0; i < mScriptModel.getAmountOfScripts(); ++i) { final ScriptHolder script = mScriptModel.getScript(i); mListModel.addElement(script.getScriptName()); } } }