mirror of
https://github.com/mitb-archive/filebot
synced 2024-11-14 05:15:03 -05:00
319 lines
8.8 KiB
Java
319 lines
8.8 KiB
Java
package net.filebot.cli;
|
|
|
|
import static net.filebot.Logging.*;
|
|
import static net.filebot.util.ui.SwingUI.*;
|
|
|
|
import java.awt.BorderLayout;
|
|
import java.awt.Color;
|
|
import java.awt.Dialog.ModalExclusionType;
|
|
import java.awt.event.ActionEvent;
|
|
import java.awt.event.KeyEvent;
|
|
import java.awt.event.WindowAdapter;
|
|
import java.awt.event.WindowEvent;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.PrintStream;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.util.HashMap;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.logging.Level;
|
|
|
|
import javax.script.Bindings;
|
|
import javax.script.ScriptException;
|
|
import javax.script.SimpleBindings;
|
|
import javax.swing.Action;
|
|
import javax.swing.JComponent;
|
|
import javax.swing.JFrame;
|
|
import javax.swing.JSplitPane;
|
|
import javax.swing.JToolBar;
|
|
import javax.swing.KeyStroke;
|
|
import javax.swing.SwingUtilities;
|
|
import javax.swing.SwingWorker;
|
|
import javax.swing.text.JTextComponent;
|
|
|
|
import org.fife.ui.rsyntaxtextarea.FileLocation;
|
|
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
|
import org.fife.ui.rsyntaxtextarea.TextEditorPane;
|
|
import org.fife.ui.rtextarea.RTextScrollPane;
|
|
|
|
import net.filebot.ApplicationFolder;
|
|
import net.filebot.ResourceManager;
|
|
import net.filebot.Settings;
|
|
import net.filebot.util.TeePrintStream;
|
|
|
|
public class GroovyPad extends JFrame {
|
|
|
|
public static final String DEFAULT_SCRIPT = "runScript 'sysinfo'";
|
|
|
|
public GroovyPad() throws IOException {
|
|
super("Groovy Pad");
|
|
|
|
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, createEditor(), createOutputLog());
|
|
splitPane.setResizeWeight(0.7);
|
|
|
|
JComponent c = (JComponent) getContentPane();
|
|
c.setLayout(new BorderLayout(0, 0));
|
|
c.add(splitPane, BorderLayout.CENTER);
|
|
|
|
JToolBar tools = new JToolBar("Run", JToolBar.HORIZONTAL);
|
|
tools.setFloatable(true);
|
|
tools.add(run);
|
|
tools.add(cancel);
|
|
c.add(tools, BorderLayout.NORTH);
|
|
|
|
run.setEnabled(true);
|
|
cancel.setEnabled(false);
|
|
|
|
installAction(c, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), run);
|
|
installAction(c, KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK), run);
|
|
|
|
addWindowListener(new WindowAdapter() {
|
|
|
|
@Override
|
|
public void windowClosed(WindowEvent evt) {
|
|
cancel.actionPerformed(null);
|
|
console.unhook();
|
|
|
|
try {
|
|
editor.save();
|
|
} catch (IOException e) {
|
|
// ignore
|
|
}
|
|
}
|
|
});
|
|
|
|
console = new MessageConsole(output);
|
|
console.hook();
|
|
|
|
shell = createScriptShell();
|
|
editor.requestFocusInWindow();
|
|
|
|
setModalExclusionType(ModalExclusionType.TOOLKIT_EXCLUDE);
|
|
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
|
setLocationByPlatform(true);
|
|
setSize(800, 600);
|
|
|
|
}
|
|
|
|
protected MessageConsole console;
|
|
protected TextEditorPane editor;
|
|
protected TextEditorPane output;
|
|
|
|
protected JComponent createEditor() throws IOException {
|
|
editor = new TextEditorPane(TextEditorPane.INSERT_MODE, false, getFileLocation("pad.groovy"), "UTF-8");
|
|
editor.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_GROOVY);
|
|
editor.setAutoscrolls(false);
|
|
editor.setAnimateBracketMatching(false);
|
|
editor.setAntiAliasingEnabled(true);
|
|
editor.setAutoIndentEnabled(true);
|
|
editor.setBracketMatchingEnabled(true);
|
|
editor.setCloseCurlyBraces(true);
|
|
editor.setClearWhitespaceLinesEnabled(true);
|
|
editor.setCodeFoldingEnabled(true);
|
|
editor.setHighlightSecondaryLanguages(false);
|
|
editor.setRoundedSelectionEdges(false);
|
|
editor.setTabsEmulated(false);
|
|
|
|
// restore on open
|
|
editor.reload();
|
|
|
|
return new RTextScrollPane(editor, true);
|
|
}
|
|
|
|
protected JComponent createOutputLog() throws IOException {
|
|
output = new TextEditorPane(TextEditorPane.INSERT_MODE, false);
|
|
output.setEditable(false);
|
|
output.setReadOnly(true);
|
|
output.setAutoscrolls(true);
|
|
output.setBackground(new Color(255, 255, 218));
|
|
|
|
output.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_NONE);
|
|
output.setAnimateBracketMatching(false);
|
|
output.setAntiAliasingEnabled(true);
|
|
output.setAutoIndentEnabled(false);
|
|
output.setBracketMatchingEnabled(false);
|
|
output.setCloseCurlyBraces(false);
|
|
output.setClearWhitespaceLinesEnabled(false);
|
|
output.setCodeFoldingEnabled(false);
|
|
output.setHighlightCurrentLine(false);
|
|
output.setHighlightSecondaryLanguages(false);
|
|
output.setRoundedSelectionEdges(false);
|
|
output.setTabsEmulated(false);
|
|
|
|
return new RTextScrollPane(output, true);
|
|
}
|
|
|
|
protected FileLocation getFileLocation(String name) throws IOException {
|
|
File pad = ApplicationFolder.AppData.resolve(name);
|
|
if (!pad.exists()) {
|
|
// use this default value so people can easily submit bug reports with fn:sysinfo logs
|
|
ScriptShellMethods.saveAs(DEFAULT_SCRIPT, pad);
|
|
}
|
|
return FileLocation.create(pad);
|
|
}
|
|
|
|
protected final ScriptShell shell;
|
|
|
|
protected ScriptShell createScriptShell() {
|
|
try {
|
|
return new ScriptShell(s -> ScriptSource.GITHUB_STABLE.getScriptProvider(s).getScript(s), new HashMap<String, Object>());
|
|
} catch (Exception e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
protected final Action run = newAction("Run", ResourceManager.getIcon("script.go"), this::runScript);
|
|
protected final Action cancel = newAction("Cancel", ResourceManager.getIcon("script.cancel"), this::cancelScript);
|
|
|
|
private Runner currentRunner = null;
|
|
|
|
public void runScript(ActionEvent evt) {
|
|
// persist script file and clear output
|
|
try {
|
|
editor.save();
|
|
} catch (IOException e) {
|
|
debug.log(Level.WARNING, e, e::getMessage);
|
|
}
|
|
|
|
output.setText("");
|
|
runScript(editor.getText());
|
|
}
|
|
|
|
public void runScript(String script) {
|
|
if (currentRunner == null || currentRunner.isDone()) {
|
|
currentRunner = new Runner(script) {
|
|
|
|
@Override
|
|
protected void done() {
|
|
run.setEnabled(true);
|
|
cancel.setEnabled(false);
|
|
}
|
|
};
|
|
|
|
run.setEnabled(false);
|
|
cancel.setEnabled(true);
|
|
currentRunner.execute();
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
protected void cancelScript(ActionEvent evt) {
|
|
if (currentRunner != null && !currentRunner.isDone()) {
|
|
currentRunner.cancel(true);
|
|
currentRunner.getExecutionThread().stop();
|
|
|
|
try {
|
|
currentRunner.get(2, TimeUnit.SECONDS);
|
|
} catch (Exception e) {
|
|
debug.log(Level.WARNING, e, e::getMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected class Runner extends SwingWorker<Object, Object> {
|
|
|
|
private Thread executionThread;
|
|
private Object result;
|
|
|
|
public Runner(final String script) {
|
|
executionThread = new Thread("GroovyPadRunner") {
|
|
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
Bindings bindings = new SimpleBindings();
|
|
bindings.put(ScriptShell.SHELL_ARGV_BINDING_NAME, Settings.getApplicationArguments().getArgumentArray());
|
|
bindings.put(ScriptShell.ARGV_BINDING_NAME, Settings.getApplicationArguments().getFiles(false));
|
|
|
|
result = shell.evaluate(script, bindings);
|
|
|
|
// print result and make sure to flush Groovy output
|
|
SimpleBindings resultBindings = new SimpleBindings();
|
|
resultBindings.put("result", result);
|
|
if (result != null) {
|
|
shell.evaluate("print('Result: '); println(result);", resultBindings);
|
|
} else {
|
|
shell.evaluate("println();", resultBindings);
|
|
}
|
|
} catch (ScriptException e) {
|
|
while (e.getCause() instanceof ScriptException) {
|
|
e = (ScriptException) e.getCause();
|
|
}
|
|
e.printStackTrace();
|
|
} catch (Throwable e) {
|
|
e.printStackTrace();
|
|
}
|
|
};
|
|
};
|
|
|
|
executionThread.setDaemon(false);
|
|
executionThread.setPriority(Thread.MIN_PRIORITY);
|
|
}
|
|
|
|
@Override
|
|
protected Object doInBackground() throws Exception {
|
|
executionThread.start();
|
|
executionThread.join();
|
|
return result;
|
|
}
|
|
|
|
public Thread getExecutionThread() {
|
|
return executionThread;
|
|
}
|
|
};
|
|
|
|
public static class MessageConsole {
|
|
|
|
private final PrintStream system_out = System.out;
|
|
private final PrintStream system_err = System.err;
|
|
|
|
private JTextComponent textComponent;
|
|
|
|
public MessageConsole(JTextComponent textComponent) {
|
|
this.textComponent = textComponent;
|
|
}
|
|
|
|
public void hook() {
|
|
try {
|
|
System.setOut(new TeePrintStream(new ConsoleOutputStream(), true, "UTF-8", system_out));
|
|
System.setErr(new TeePrintStream(new ConsoleOutputStream(), true, "UTF-8", system_err));
|
|
} catch (UnsupportedEncodingException e) {
|
|
debug.log(Level.WARNING, e, e::getMessage);
|
|
}
|
|
}
|
|
|
|
public void unhook() {
|
|
System.setOut(system_out);
|
|
System.setErr(system_err);
|
|
}
|
|
|
|
private class ConsoleOutputStream extends ByteArrayOutputStream {
|
|
|
|
@Override
|
|
public void flush() {
|
|
try {
|
|
String message = this.toString("UTF-8");
|
|
reset();
|
|
commit(message);
|
|
} catch (Exception e) {
|
|
// can't happen
|
|
}
|
|
}
|
|
|
|
private void commit(final String line) {
|
|
SwingUtilities.invokeLater(() -> {
|
|
try {
|
|
int offset = textComponent.getDocument().getLength();
|
|
textComponent.getDocument().insertString(offset, line, null);
|
|
textComponent.setCaretPosition(textComponent.getDocument().getLength());
|
|
} catch (Exception e) {
|
|
// ignore
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|