+ use RSyntaxTextArea as Groovy editor so we get neat highlighting and bracket matching :)

This commit is contained in:
Reinhard Pointner 2013-10-03 19:44:33 +00:00
parent 0408a17ddb
commit 07173fabf0
5 changed files with 173 additions and 280 deletions

View File

@ -101,6 +101,10 @@
<include name="net/miginfocom/**" /> <include name="net/miginfocom/**" />
</zipfileset> </zipfileset>
<zipfileset src="${dir.lib}/rsyntaxtextarea.jar">
<include name="org/fife/**" />
</zipfileset>
<zipfileset src="${dir.lib}/xmlrpc.jar"> <zipfileset src="${dir.lib}/xmlrpc.jar">
<include name="redstone/xmlrpc/**" /> <include name="redstone/xmlrpc/**" />
</zipfileset> </zipfileset>

View File

@ -60,6 +60,7 @@
<jar href="xercesImpl.jar" download="lazy" part="scraper" /> <jar href="xercesImpl.jar" download="lazy" part="scraper" />
<jar href="mediainfo.jar" download="lazy" part="native" /> <jar href="mediainfo.jar" download="lazy" part="native" />
<jar href="sevenzipjbinding.jar" download="lazy" part="native" /> <jar href="sevenzipjbinding.jar" download="lazy" part="native" />
<jar href="rsyntaxtextarea.jar" download="eager" />
</resources> </resources>
<resources os="Windows" arch="x86"> <resources os="Windows" arch="x86">

BIN
lib/rsyntaxtextarea.jar Normal file

Binary file not shown.

View File

@ -1,94 +0,0 @@
package net.sourceforge.filebot.ui.rename;
import javax.swing.event.DocumentEvent;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
class ExpressionFormatDocument extends PlainDocument {
private Completion lastCompletion;
@Override
public void insertString(int offset, String text, AttributeSet attributes) throws BadLocationException {
if (text == null || text.isEmpty()) {
return;
}
// ignore user input that matches the last auto-completion
if (lastCompletion != null && lastCompletion.didComplete(this, offset, text)) {
lastCompletion = null;
// behave as if something was inserted (e.g. update caret position)
fireInsertUpdate(new DefaultDocumentEvent(offset, text.length(), DocumentEvent.EventType.INSERT));
return;
}
// try to auto-complete input
lastCompletion = Completion.getCompletion(this, offset, text);
if (lastCompletion != null) {
text = lastCompletion.complete(this, offset, text);
}
super.insertString(offset, text, attributes);
}
public Completion getLastCompletion() {
return lastCompletion;
}
public enum Completion {
RoundBrackets("()"),
SquareBrackets("[]"),
CurlyBrackets("{}"),
SingleQuoteStringLiteral("''"),
DoubleQuoteStringLiteral("\"\"");
public final String pattern;
private Completion(String pattern) {
this.pattern = pattern;
}
public boolean canComplete(Document document, int offset, String input) {
return pattern.startsWith(input);
}
public boolean didComplete(Document document, int offset, String input) {
try {
return document.getText(0, offset).concat(input).endsWith(pattern);
} catch (BadLocationException e) {
return false;
}
}
public String complete(Document document, int offset, String input) {
return pattern;
}
public static Completion getCompletion(Document document, int offset, String input) {
for (Completion completion : values()) {
if (completion.canComplete(document, offset, input)) {
return completion;
}
}
return null;
}
}
}

View File

@ -1,7 +1,5 @@
package net.sourceforge.filebot.ui.rename; package net.sourceforge.filebot.ui.rename;
import static java.awt.Font.*; import static java.awt.Font.*;
import static javax.swing.BorderFactory.*; import static javax.swing.BorderFactory.*;
import static net.sourceforge.filebot.ui.NotificationLogging.*; import static net.sourceforge.filebot.ui.NotificationLogging.*;
@ -52,9 +50,13 @@ import javax.swing.JTextField;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.Timer; import javax.swing.Timer;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener; import javax.swing.event.PopupMenuListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
@ -78,232 +80,226 @@ import net.sourceforge.tuned.ui.TunedUtilities;
import net.sourceforge.tuned.ui.notification.SeparatorBorder; import net.sourceforge.tuned.ui.notification.SeparatorBorder;
import net.sourceforge.tuned.ui.notification.SeparatorBorder.Position; import net.sourceforge.tuned.ui.notification.SeparatorBorder.Position;
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import com.cedarsoftware.util.io.JsonReader; import com.cedarsoftware.util.io.JsonReader;
import com.cedarsoftware.util.io.JsonWriter; import com.cedarsoftware.util.io.JsonWriter;
public class FormatDialog extends JDialog {
class FormatDialog extends JDialog {
private boolean submit = false; private boolean submit = false;
private Mode mode; private Mode mode;
private ExpressionFormat format; private ExpressionFormat format;
private MediaBindingBean sample; private MediaBindingBean sample;
private ExecutorService executor = createExecutor(); private ExecutorService executor = createExecutor();
private RunnableFuture<String> currentPreviewFuture; private RunnableFuture<String> currentPreviewFuture;
private JLabel preview = new JLabel(); private JLabel preview = new JLabel();
private JLabel status = new JLabel(); private JLabel status = new JLabel();
private JTextComponent editor = createEditor(); private JTextComponent editor = createEditor();
private ProgressIndicator progressIndicator = new ProgressIndicator(); private ProgressIndicator progressIndicator = new ProgressIndicator();
private JLabel title = new JLabel(); private JLabel title = new JLabel();
private JPanel help = new JPanel(new MigLayout("insets 0, nogrid, fillx")); private JPanel help = new JPanel(new MigLayout("insets 0, nogrid, fillx"));
private static final PreferencesEntry<String> persistentSampleFile = Settings.forPackage(FormatDialog.class).entry("format.sample.file"); private static final PreferencesEntry<String> persistentSampleFile = Settings.forPackage(FormatDialog.class).entry("format.sample.file");
public enum Mode { public enum Mode {
Episode, Movie, Music; Episode, Movie, Music;
public Mode next() { public Mode next() {
if (ordinal() < values().length - 1) if (ordinal() < values().length - 1)
return values()[ordinal() + 1]; return values()[ordinal() + 1];
return values()[0]; return values()[0];
} }
public String key() { public String key() {
return this.name().toLowerCase(); return this.name().toLowerCase();
} }
public Format getFormat() { public Format getFormat() {
switch (this) { switch (this) {
case Episode: case Episode:
return new EpisodeFormat(true, true); return new EpisodeFormat(true, true);
case Movie: // case Movie case Movie: // case Movie
return new MovieFormat(true, true, false); return new MovieFormat(true, true, false);
default: default:
return new AudioTrackFormat(); return new AudioTrackFormat();
} }
} }
public PreferencesEntry<String> persistentSample() { public PreferencesEntry<String> persistentSample() {
return Settings.forPackage(FormatDialog.class).entry("format.sample." + key()); return Settings.forPackage(FormatDialog.class).entry("format.sample." + key());
} }
public PreferencesList<String> persistentFormatHistory() { public PreferencesList<String> persistentFormatHistory() {
return Settings.forPackage(FormatDialog.class).node("format.recent." + key()).asList(); return Settings.forPackage(FormatDialog.class).node("format.recent." + key()).asList();
} }
} }
public FormatDialog(Window owner) { public FormatDialog(Window owner) {
super(owner, ModalityType.DOCUMENT_MODAL); super(owner, ModalityType.DOCUMENT_MODAL);
// initialize hidden // initialize hidden
progressIndicator.setVisible(false); progressIndicator.setVisible(false);
// bold title label in header // bold title label in header
title.setFont(title.getFont().deriveFont(BOLD)); title.setFont(title.getFont().deriveFont(BOLD));
JPanel header = new JPanel(new MigLayout("insets dialog, nogrid")); JPanel header = new JPanel(new MigLayout("insets dialog, nogrid"));
header.setBackground(Color.white); header.setBackground(Color.white);
header.setBorder(new SeparatorBorder(1, new Color(0xB4B4B4), new Color(0xACACAC), GradientStyle.LEFT_TO_RIGHT, Position.BOTTOM)); header.setBorder(new SeparatorBorder(1, new Color(0xB4B4B4), new Color(0xACACAC), GradientStyle.LEFT_TO_RIGHT, Position.BOTTOM));
header.add(progressIndicator, "pos 1al 0al, hidemode 3"); header.add(progressIndicator, "pos 1al 0al, hidemode 3");
header.add(title, "wrap unrel:push"); header.add(title, "wrap unrel:push");
header.add(preview, "hmin 16px, gap indent, hidemode 3, wmax 90%"); header.add(preview, "hmin 16px, gap indent, hidemode 3, wmax 90%");
header.add(status, "hmin 16px, gap indent, hidemode 3, wmax 90%, newline"); header.add(status, "hmin 16px, gap indent, hidemode 3, wmax 90%, newline");
JPanel content = new JPanel(new MigLayout("insets dialog, nogrid, fill")); JPanel content = new JPanel(new MigLayout("insets dialog, nogrid, fill"));
content.add(editor, "w 120px:min(pref, 420px), h 40px!, growx, wrap 4px, id editor"); content.add(editor, "w 120px:min(pref, 420px), h 40px!, growx, wrap 4px, id editor");
content.add(createImageButton(changeSampleAction), "w 25!, h 19!, pos n editor.y2+1 editor.x2 n"); content.add(createImageButton(changeSampleAction), "w 25!, h 19!, pos n editor.y2+1 editor.x2 n");
content.add(help, "growx, wrap 25px:push"); content.add(help, "growx, wrap 25px:push");
content.add(new JButton(switchEditModeAction), "tag left"); content.add(new JButton(switchEditModeAction), "tag left");
content.add(new JButton(approveFormatAction), "tag apply"); content.add(new JButton(approveFormatAction), "tag apply");
content.add(new JButton(cancelAction), "tag cancel"); content.add(new JButton(cancelAction), "tag cancel");
JComponent pane = (JComponent) getContentPane(); JComponent pane = (JComponent) getContentPane();
pane.setLayout(new MigLayout("insets 0, fill")); pane.setLayout(new MigLayout("insets 0, fill"));
pane.add(header, "h 60px, growx, dock north"); pane.add(header, "h 60px, growx, dock north");
pane.add(content, "grow"); pane.add(content, "grow");
addPropertyChangeListener("sample", new PropertyChangeListener() { addPropertyChangeListener("sample", new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
checkFormatInBackground(); checkFormatInBackground();
} }
}); });
// focus editor by default // focus editor by default
addWindowFocusListener(new WindowAdapter() { addWindowFocusListener(new WindowAdapter() {
@Override @Override
public void windowGainedFocus(WindowEvent e) { public void windowGainedFocus(WindowEvent e) {
editor.requestFocusInWindow(); editor.requestFocusInWindow();
} }
}); });
// finish dialog and close window manually // finish dialog and close window manually
addWindowListener(new WindowAdapter() { addWindowListener(new WindowAdapter() {
@Override @Override
public void windowClosing(WindowEvent e) { public void windowClosing(WindowEvent e) {
finish(false); finish(false);
} }
}); });
// install editor suggestions popup // install editor suggestions popup
editor.setComponentPopupMenu(createRecentFormatPopup()); editor.setComponentPopupMenu(createRecentFormatPopup());
TunedUtilities.installAction(editor, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new AbstractAction("Recent") { TunedUtilities.installAction(editor, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new AbstractAction("Recent") {
@Override @Override
public void actionPerformed(ActionEvent evt) { public void actionPerformed(ActionEvent evt) {
// display popup below format editor // display popup below format editor
editor.getComponentPopupMenu().show(editor, 0, editor.getHeight() + 3); editor.getComponentPopupMenu().show(editor, 0, editor.getHeight() + 3);
} }
}); });
// episode mode by default // episode mode by default
setMode(Mode.Episode); setMode(Mode.Episode);
// initialize window properties // initialize window properties
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setSize(610, 430); setSize(610, 430);
} }
public void setMode(Mode mode) { public void setMode(Mode mode) {
this.mode = mode; this.mode = mode;
this.setTitle(String.format("%s Format", mode)); this.setTitle(String.format("%s Format", mode));
title.setText(this.getTitle()); title.setText(this.getTitle());
status.setVisible(false); status.setVisible(false);
switchEditModeAction.putValue(Action.NAME, String.format("Switch to %s Format", mode.next())); switchEditModeAction.putValue(Action.NAME, String.format("Switch to %s Format", mode.next()));
updateHelpPanel(mode); updateHelpPanel(mode);
// update preview to current format // update preview to current format
sample = restoreSample(mode); sample = restoreSample(mode);
// restore editor state // restore editor state
editor.setText(mode.persistentFormatHistory().isEmpty() ? "" : mode.persistentFormatHistory().get(0)); editor.setText(mode.persistentFormatHistory().isEmpty() ? "" : mode.persistentFormatHistory().get(0));
// update examples // update examples
fireSampleChanged(); fireSampleChanged();
} }
private JComponent updateHelpPanel(Mode mode) { private JComponent updateHelpPanel(Mode mode) {
help.removeAll(); help.removeAll();
help.add(new JLabel("Syntax"), "gap indent+unrel, wrap 0"); help.add(new JLabel("Syntax"), "gap indent+unrel, wrap 0");
help.add(createSyntaxPanel(mode), "gapx indent indent, wrap 8px"); help.add(createSyntaxPanel(mode), "gapx indent indent, wrap 8px");
help.add(new JLabel("Examples"), "gap indent+unrel, wrap 0"); help.add(new JLabel("Examples"), "gap indent+unrel, wrap 0");
help.add(createExamplesPanel(mode), "growx, h pref!, gapx indent indent"); help.add(createExamplesPanel(mode), "growx, h pref!, gapx indent indent");
return help; return help;
} }
private JTextComponent createEditor() { private JTextComponent createEditor() {
final JTextComponent editor = new JTextField(new ExpressionFormatDocument(), null, 0); final RSyntaxTextArea editor = new RSyntaxTextArea(new RSyntaxDocument(SyntaxConstants.SYNTAX_STYLE_GROOVY) {
@Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
super.insertString(offs, str.replaceAll("\\s", " "), a); // FORCE SINGLE LINE
}
}, null, 1, 80);
editor.setAntiAliasingEnabled(true);
editor.setAnimateBracketMatching(false);
editor.setAutoIndentEnabled(false);
editor.setClearWhitespaceLinesEnabled(false);
editor.setBracketMatchingEnabled(true);
editor.setCloseCurlyBraces(false);
editor.setCodeFoldingEnabled(false);
editor.setHyperlinksEnabled(false);
editor.setUseFocusableTips(false);
editor.setHighlightCurrentLine(false);
editor.setLineWrap(false);
editor.setBorder(new CompoundBorder(new JTextField().getBorder(), new EmptyBorder(7, 2, 7, 2)));
editor.setFont(new Font(MONOSPACED, PLAIN, 14)); editor.setFont(new Font(MONOSPACED, PLAIN, 14));
// enable undo/redo
installUndoSupport(editor);
// update format on change // update format on change
editor.getDocument().addDocumentListener(new LazyDocumentListener() { editor.getDocument().addDocumentListener(new LazyDocumentListener() {
@Override @Override
public void update(DocumentEvent e) { public void update(DocumentEvent e) {
checkFormatInBackground(); checkFormatInBackground();
} }
}); });
// improved cursor behaviour, use delayed listener, so we apply our cursor updates, after the text component is finished with its own
editor.getDocument().addDocumentListener(new LazyDocumentListener(0) {
@Override
public void update(DocumentEvent evt) {
if (evt.getType() == DocumentEvent.EventType.INSERT) {
ExpressionFormatDocument document = (ExpressionFormatDocument) evt.getDocument();
if (document.getLastCompletion() != null) {
editor.setCaretPosition(editor.getCaretPosition() - 1);
}
}
}
});
return editor; return editor;
} }
private JComponent createSyntaxPanel(Mode mode) { private JComponent createSyntaxPanel(Mode mode) {
JPanel panel = new JPanel(new MigLayout("fill, nogrid")); JPanel panel = new JPanel(new MigLayout("fill, nogrid"));
panel.setBorder(createLineBorder(new Color(0xACA899))); panel.setBorder(createLineBorder(new Color(0xACA899)));
panel.setBackground(new Color(0xFFFFE1)); panel.setBackground(new Color(0xFFFFE1));
panel.setOpaque(true); panel.setOpaque(true);
panel.add(new LinkButton(new AbstractAction(ResourceBundle.getBundle(FormatDialog.class.getName()).getString(mode.key() + ".syntax")) { panel.add(new LinkButton(new AbstractAction(ResourceBundle.getBundle(FormatDialog.class.getName()).getString(mode.key() + ".syntax")) {
@Override @Override
public void actionPerformed(ActionEvent evt) { public void actionPerformed(ActionEvent evt) {
try { try {
@ -313,53 +309,51 @@ class FormatDialog extends JDialog {
} }
} }
})); }));
return panel; return panel;
} }
private JComponent createExamplesPanel(Mode mode) { private JComponent createExamplesPanel(Mode mode) {
JPanel panel = new JPanel(new MigLayout("fill, wrap 3")); JPanel panel = new JPanel(new MigLayout("fill, wrap 3"));
panel.setBorder(createLineBorder(new Color(0xACA899))); panel.setBorder(createLineBorder(new Color(0xACA899)));
panel.setBackground(new Color(0xFFFFE1)); panel.setBackground(new Color(0xFFFFE1));
ResourceBundle bundle = ResourceBundle.getBundle(getClass().getName()); ResourceBundle bundle = ResourceBundle.getBundle(getClass().getName());
TreeMap<String, String> examples = new TreeMap<String, String>(); TreeMap<String, String> examples = new TreeMap<String, String>();
// extract all example entries and sort by key // extract all example entries and sort by key
for (String key : bundle.keySet()) { for (String key : bundle.keySet()) {
if (key.startsWith(mode.key() + ".example")) if (key.startsWith(mode.key() + ".example"))
examples.put(key, bundle.getString(key)); examples.put(key, bundle.getString(key));
} }
for (final String format : examples.values()) { for (final String format : examples.values()) {
LinkButton formatLink = new LinkButton(new AbstractAction(format) { LinkButton formatLink = new LinkButton(new AbstractAction(format) {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
editor.setText(format); editor.setText(format);
} }
}); });
formatLink.setFont(new Font(MONOSPACED, PLAIN, 11)); formatLink.setFont(new Font(MONOSPACED, PLAIN, 11));
// compute format label in background // compute format label in background
final JLabel formatExample = new JLabel("[evaluate]"); final JLabel formatExample = new JLabel("[evaluate]");
// bind text to preview // bind text to preview
addPropertyChangeListener("sample", new PropertyChangeListener() { addPropertyChangeListener("sample", new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
new SwingWorker<String, Void>() { new SwingWorker<String, Void>() {
@Override @Override
protected String doInBackground() throws Exception { protected String doInBackground() throws Exception {
return new ExpressionFormat(format).format(sample); return new ExpressionFormat(format).format(sample);
} }
@Override @Override
protected void done() { protected void done() {
try { try {
@ -371,20 +365,19 @@ class FormatDialog extends JDialog {
}.execute(); }.execute();
} }
}); });
panel.add(formatLink); panel.add(formatLink);
panel.add(new JLabel("")); panel.add(new JLabel(""));
panel.add(formatExample); panel.add(formatExample);
} }
return panel; return panel;
} }
private MediaBindingBean restoreSample(Mode mode) { private MediaBindingBean restoreSample(Mode mode) {
Object info = null; Object info = null;
File media = null; File media = null;
try { try {
// restore sample from user preferences // restore sample from user preferences
String sample = mode.persistentSample().getValue(); String sample = mode.persistentSample().getValue();
@ -402,93 +395,90 @@ class FormatDialog extends JDialog {
throw new RuntimeException(illegalSample); // won't happen throw new RuntimeException(illegalSample); // won't happen
} }
} }
// restore media file // restore media file
String path = persistentSampleFile.getValue(); String path = persistentSampleFile.getValue();
if (path != null && !path.isEmpty()) { if (path != null && !path.isEmpty()) {
media = new File(path); media = new File(path);
} }
return new MediaBindingBean(info, media, null); return new MediaBindingBean(info, media, null);
} }
private ExecutorService createExecutor() { private ExecutorService createExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1), new DefaultThreadFactory("PreviewFormatter")) { ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1), new DefaultThreadFactory("PreviewFormatter")) {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public List<Runnable> shutdownNow() { public List<Runnable> shutdownNow() {
List<Runnable> remaining = super.shutdownNow(); List<Runnable> remaining = super.shutdownNow();
try { try {
if (!awaitTermination(3, TimeUnit.SECONDS)) { if (!awaitTermination(3, TimeUnit.SECONDS)) {
// if the thread has not terminated after 4 seconds, it is probably stuck // if the thread has not terminated after 4 seconds, it is probably stuck
ThreadGroup threadGroup = ((DefaultThreadFactory) getThreadFactory()).getThreadGroup(); ThreadGroup threadGroup = ((DefaultThreadFactory) getThreadFactory()).getThreadGroup();
// kill background thread by force // kill background thread by force
threadGroup.stop(); threadGroup.stop();
// log access of potentially unsafe method // log access of potentially unsafe method
Logger.getLogger(getClass().getName()).warning("Thread was forcibly terminated"); Logger.getLogger(getClass().getName()).warning("Thread was forcibly terminated");
} }
} catch (Exception e) { } catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Thread was not terminated", e); Logger.getLogger(getClass().getName()).log(Level.WARNING, "Thread was not terminated", e);
} }
return remaining; return remaining;
} }
}; };
// only keep the latest task in the queue // only keep the latest task in the queue
executor.setRejectedExecutionHandler(new DiscardOldestPolicy()); executor.setRejectedExecutionHandler(new DiscardOldestPolicy());
return executor; return executor;
} }
private void checkFormatInBackground() { private void checkFormatInBackground() {
try { try {
// check syntax in foreground // check syntax in foreground
final ExpressionFormat format = new ExpressionFormat(editor.getText().trim()); final ExpressionFormat format = new ExpressionFormat(editor.getText().trim());
// activate delayed to avoid flickering when formatting takes only a couple of milliseconds // activate delayed to avoid flickering when formatting takes only a couple of milliseconds
final Timer progressIndicatorTimer = TunedUtilities.invokeLater(400, new Runnable() { final Timer progressIndicatorTimer = TunedUtilities.invokeLater(400, new Runnable() {
@Override @Override
public void run() { public void run() {
progressIndicator.setVisible(true); progressIndicator.setVisible(true);
} }
}); });
// cancel old worker later // cancel old worker later
Future<String> obsoletePreviewFuture = currentPreviewFuture; Future<String> obsoletePreviewFuture = currentPreviewFuture;
// create new worker // create new worker
currentPreviewFuture = new SwingWorker<String, Void>() { currentPreviewFuture = new SwingWorker<String, Void>() {
@Override @Override
protected String doInBackground() throws Exception { protected String doInBackground() throws Exception {
return format.format(sample); return format.format(sample);
} }
@Override @Override
protected void done() { protected void done() {
try { try {
preview.setText(get()); preview.setText(get());
// check internal script exception // check internal script exception
if (format.caughtScriptException() != null) { if (format.caughtScriptException() != null) {
throw format.caughtScriptException(); throw format.caughtScriptException();
} }
// check empty output // check empty output
if (get().trim().isEmpty()) { if (get().trim().isEmpty()) {
throw new RuntimeException("Formatted value is empty"); throw new RuntimeException("Formatted value is empty");
} }
// no warning or error // no warning or error
status.setVisible(false); status.setVisible(false);
} catch (CancellationException e) { } catch (CancellationException e) {
@ -501,10 +491,10 @@ class FormatDialog extends JDialog {
} finally { } finally {
preview.setVisible(preview.getText().trim().length() > 0); preview.setVisible(preview.getText().trim().length() > 0);
editor.setForeground(preview.getForeground()); editor.setForeground(preview.getForeground());
// stop progress indicator from becoming visible, if we have been fast enough // stop progress indicator from becoming visible, if we have been fast enough
progressIndicatorTimer.stop(); progressIndicatorTimer.stop();
// hide progress indicator, if this still is the current worker // hide progress indicator, if this still is the current worker
if (this == currentPreviewFuture) { if (this == currentPreviewFuture) {
progressIndicator.setVisible(false); progressIndicator.setVisible(false);
@ -512,83 +502,76 @@ class FormatDialog extends JDialog {
} }
} }
}; };
// cancel old worker, after new worker has been created, because done() might be called from within cancel() // cancel old worker, after new worker has been created, because done() might be called from within cancel()
if (obsoletePreviewFuture != null) { if (obsoletePreviewFuture != null) {
obsoletePreviewFuture.cancel(true); obsoletePreviewFuture.cancel(true);
} }
// submit new worker // submit new worker
executor.execute(currentPreviewFuture); executor.execute(currentPreviewFuture);
} catch (ScriptException e) { } catch (ScriptException e) {
// incorrect syntax // incorrect syntax
status.setText(ExceptionUtilities.getRootCauseMessage(e)); status.setText(ExceptionUtilities.getRootCauseMessage(e));
status.setIcon(ResourceManager.getIcon("status.error")); status.setIcon(ResourceManager.getIcon("status.error"));
status.setVisible(true); status.setVisible(true);
preview.setVisible(false); preview.setVisible(false);
editor.setForeground(Color.red); editor.setForeground(Color.red);
} }
} }
public boolean submit() { public boolean submit() {
return submit; return submit;
} }
public Mode getMode() { public Mode getMode() {
return mode; return mode;
} }
public ExpressionFormat getFormat() { public ExpressionFormat getFormat() {
return format; return format;
} }
private void finish(boolean submit) { private void finish(boolean submit) {
this.submit = submit; this.submit = submit;
// force shutdown // force shutdown
executor.shutdownNow(); executor.shutdownNow();
setVisible(false); setVisible(false);
dispose(); dispose();
} }
private JPopupMenu createRecentFormatPopup() { private JPopupMenu createRecentFormatPopup() {
JPopupMenu popup = new JPopupMenu(); JPopupMenu popup = new JPopupMenu();
popup.addPopupMenuListener(new PopupMenuListener() { popup.addPopupMenuListener(new PopupMenuListener() {
@Override @Override
public void popupMenuWillBecomeVisible(PopupMenuEvent evt) { public void popupMenuWillBecomeVisible(PopupMenuEvent evt) {
// make sure to reset state // make sure to reset state
popupMenuWillBecomeInvisible(evt); popupMenuWillBecomeInvisible(evt);
JPopupMenu popup = (JPopupMenu) evt.getSource(); JPopupMenu popup = (JPopupMenu) evt.getSource();
for (final String expression : mode.persistentFormatHistory()) { for (final String expression : mode.persistentFormatHistory()) {
JMenuItem item = popup.add(new AbstractAction(expression) { JMenuItem item = popup.add(new AbstractAction(expression) {
@Override @Override
public void actionPerformed(ActionEvent evt) { public void actionPerformed(ActionEvent evt) {
editor.setText(expression); editor.setText(expression);
} }
}); });
item.setFont(new Font(MONOSPACED, PLAIN, 11)); item.setFont(new Font(MONOSPACED, PLAIN, 11));
} }
} }
@Override @Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent evt) { public void popupMenuWillBecomeInvisible(PopupMenuEvent evt) {
JPopupMenu popup = (JPopupMenu) evt.getSource(); JPopupMenu popup = (JPopupMenu) evt.getSource();
popup.removeAll(); popup.removeAll();
} }
@Override @Override
public void popupMenuCanceled(PopupMenuEvent evt) { public void popupMenuCanceled(PopupMenuEvent evt) {
popupMenuWillBecomeInvisible(evt); popupMenuWillBecomeInvisible(evt);
@ -596,93 +579,92 @@ class FormatDialog extends JDialog {
}); });
return popup; return popup;
} }
protected final Action changeSampleAction = new AbstractAction("Change Sample", ResourceManager.getIcon("action.variable")) { protected final Action changeSampleAction = new AbstractAction("Change Sample", ResourceManager.getIcon("action.variable")) {
@Override @Override
public void actionPerformed(ActionEvent evt) { public void actionPerformed(ActionEvent evt) {
BindingDialog dialog = new BindingDialog(getWindow(evt.getSource()), String.format("%s Bindings", mode), mode.getFormat()); BindingDialog dialog = new BindingDialog(getWindow(evt.getSource()), String.format("%s Bindings", mode), mode.getFormat());
dialog.setInfoObject(sample.getInfoObject()); dialog.setInfoObject(sample.getInfoObject());
dialog.setMediaFile(sample.getMediaFile()); dialog.setMediaFile(sample.getMediaFile());
// open dialog // open dialog
dialog.setLocationRelativeTo((Component) evt.getSource()); dialog.setLocationRelativeTo((Component) evt.getSource());
dialog.setVisible(true); dialog.setVisible(true);
if (dialog.submit()) { if (dialog.submit()) {
Object info = dialog.getInfoObject(); Object info = dialog.getInfoObject();
File file = dialog.getMediaFile(); File file = dialog.getMediaFile();
// change sample // change sample
sample = new MediaBindingBean(info, file, null); sample = new MediaBindingBean(info, file, null);
// remember // remember
mode.persistentSample().setValue(info == null ? "" : JsonWriter.toJson(info)); mode.persistentSample().setValue(info == null ? "" : JsonWriter.toJson(info));
persistentSampleFile.setValue(file == null ? "" : sample.getMediaFile().getAbsolutePath()); persistentSampleFile.setValue(file == null ? "" : sample.getMediaFile().getAbsolutePath());
// reevaluate everything // reevaluate everything
fireSampleChanged(); fireSampleChanged();
} }
} }
}; };
protected final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) { protected final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
finish(false); finish(false);
} }
}; };
protected final Action switchEditModeAction = new AbstractAction(null, ResourceManager.getIcon("dialog.switch")) { protected final Action switchEditModeAction = new AbstractAction(null, ResourceManager.getIcon("dialog.switch")) {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
setMode(mode.next()); setMode(mode.next());
} }
}; };
protected final Action approveFormatAction = new AbstractAction("Use Format", ResourceManager.getIcon("dialog.continue")) { protected final Action approveFormatAction = new AbstractAction("Use Format", ResourceManager.getIcon("dialog.continue")) {
@Override @Override
public void actionPerformed(ActionEvent evt) { public void actionPerformed(ActionEvent evt) {
try { try {
// check syntax // check syntax
format = new ExpressionFormat(editor.getText().trim()); format = new ExpressionFormat(editor.getText().trim());
if (format.getExpression().isEmpty()) { if (format.getExpression().isEmpty()) {
throw new ScriptException("Expression is empty"); throw new ScriptException("Expression is empty");
} }
// create new recent history and ignore duplicates // create new recent history and ignore duplicates
Set<String> recent = new LinkedHashSet<String>(); Set<String> recent = new LinkedHashSet<String>();
// add new format first // add new format first
recent.add(format.getExpression()); recent.add(format.getExpression());
// save the 8 most recent formats // save the 8 most recent formats
for (String expression : mode.persistentFormatHistory()) { for (String expression : mode.persistentFormatHistory()) {
recent.add(expression); recent.add(expression);
if (recent.size() >= 8) { if (recent.size() >= 8) {
break; break;
} }
} }
// update persistent history // update persistent history
mode.persistentFormatHistory().set(recent); mode.persistentFormatHistory().set(recent);
finish(true); finish(true);
} catch (ScriptException e) { } catch (ScriptException e) {
UILogger.log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e)); UILogger.log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e));
} }
} }
}; };
protected void fireSampleChanged() { protected void fireSampleChanged() {
firePropertyChange("sample", null, sample); firePropertyChange("sample", null, sample);
} }
} }