+ added replaceTrailingBraces() to ExpressionFormat context

+ added Undo/Redo support in EpisodeListDialog
* refactoring
This commit is contained in:
Reinhard Pointner 2009-05-16 11:58:28 +00:00
parent 216dd4d383
commit 9aad7deae2
16 changed files with 178 additions and 104 deletions

View File

@ -31,7 +31,11 @@ String.prototype.space = function(replacement) {
* Remove trailing parenthesis including any leading whitespace. * Remove trailing parenthesis including any leading whitespace.
* *
* e.g. "Doctor Who (2005)" -> "Doctor Who" * e.g. "Doctor Who (2005)" -> "Doctor Who"
* "Bad Wolf (1)" -> "Bad Wolf, Part 1"
*/ */
String.prototype.removeTrailingBraces = function() { String.prototype.replaceTrailingBraces = function(replacement) {
return this.replace(/\s*\([^\)]*\)$/, ""); // use empty string as default replacement
var r = replacement ? replacement : "";
return this.replace(/\s*\(([^\)]*)\)$/, r);
} }

View File

@ -117,7 +117,7 @@ public class ExpressionFormat extends Format {
ScriptContext context = new SimpleScriptContext(); ScriptContext context = new SimpleScriptContext();
// use privileged bindings so we are not restricted by the script sandbox // use privileged bindings so we are not restricted by the script sandbox
context.setBindings(PrivilegedBindings.newProxy(bindings), ScriptContext.GLOBAL_SCOPE); context.setBindings(PrivilegedBindings.newProxy(bindings, AccessController.getContext()), ScriptContext.GLOBAL_SCOPE);
for (Object snipped : compilation) { for (Object snipped : compilation) {
if (snipped instanceof CompiledScript) { if (snipped instanceof CompiledScript) {
@ -161,7 +161,7 @@ public class ExpressionFormat extends Format {
Object snipped = compilation[i]; Object snipped = compilation[i];
if (snipped instanceof CompiledScript) { if (snipped instanceof CompiledScript) {
compilation[i] = new SecureCompiledScript(sandbox, (CompiledScript) snipped); compilation[i] = new SecureCompiledScript((CompiledScript) snipped, sandbox);
} }
} }
@ -184,10 +184,12 @@ public class ExpressionFormat extends Format {
private static class PrivilegedBindings implements InvocationHandler { private static class PrivilegedBindings implements InvocationHandler {
private final Bindings bindings; private final Bindings bindings;
private final AccessControlContext context;
private PrivilegedBindings(Bindings bindings) { private PrivilegedBindings(Bindings bindings, AccessControlContext context) {
this.bindings = bindings; this.bindings = bindings;
this.context = context;
} }
@ -200,7 +202,7 @@ public class ExpressionFormat extends Format {
public Object run() throws Exception { public Object run() throws Exception {
return method.invoke(bindings, args); return method.invoke(bindings, args);
} }
}); }, context);
} catch (PrivilegedActionException e) { } catch (PrivilegedActionException e) {
Throwable cause = e.getException(); Throwable cause = e.getException();
@ -216,8 +218,8 @@ public class ExpressionFormat extends Format {
} }
public static Bindings newProxy(Bindings bindings) { public static Bindings newProxy(Bindings bindings, AccessControlContext context) {
PrivilegedBindings invocationHandler = new PrivilegedBindings(bindings); InvocationHandler invocationHandler = new PrivilegedBindings(bindings, context);
// create dynamic invocation proxy // create dynamic invocation proxy
return (Bindings) Proxy.newProxyInstance(PrivilegedBindings.class.getClassLoader(), new Class[] { Bindings.class }, invocationHandler); return (Bindings) Proxy.newProxyInstance(PrivilegedBindings.class.getClassLoader(), new Class[] { Bindings.class }, invocationHandler);
@ -227,13 +229,13 @@ public class ExpressionFormat extends Format {
private static class SecureCompiledScript extends CompiledScript { private static class SecureCompiledScript extends CompiledScript {
private final AccessControlContext sandbox;
private final CompiledScript compiledScript; private final CompiledScript compiledScript;
private final AccessControlContext sandbox;
private SecureCompiledScript(AccessControlContext sandbox, CompiledScript compiledScript) { private SecureCompiledScript(CompiledScript compiledScript, AccessControlContext sandbox) {
this.sandbox = sandbox;
this.compiledScript = compiledScript; this.compiledScript = compiledScript;
this.sandbox = sandbox;
} }

View File

@ -96,7 +96,7 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
AutoCompleteSupport.install(searchTextField.getEditor(), searchHistory).setFilterMode(TextMatcherEditor.CONTAINS); AutoCompleteSupport.install(searchTextField.getEditor(), searchHistory).setFilterMode(TextMatcherEditor.CONTAINS);
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction); TunedUtilities.installAction(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
} }

View File

@ -10,7 +10,6 @@ import java.awt.Color;
import java.awt.Font; import java.awt.Font;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter; import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
@ -45,8 +44,8 @@ import javax.swing.SwingWorker;
import javax.swing.Timer; import javax.swing.Timer;
import javax.swing.border.LineBorder; import javax.swing.border.LineBorder;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.undo.UndoManager;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
@ -58,6 +57,7 @@ import net.sourceforge.filebot.web.EpisodeFormat;
import net.sourceforge.tuned.DefaultThreadFactory; import net.sourceforge.tuned.DefaultThreadFactory;
import net.sourceforge.tuned.ExceptionUtilities; import net.sourceforge.tuned.ExceptionUtilities;
import net.sourceforge.tuned.ui.GradientStyle; import net.sourceforge.tuned.ui.GradientStyle;
import net.sourceforge.tuned.ui.LazyDocumentListener;
import net.sourceforge.tuned.ui.LinkButton; import net.sourceforge.tuned.ui.LinkButton;
import net.sourceforge.tuned.ui.ProgressIndicator; import net.sourceforge.tuned.ui.ProgressIndicator;
import net.sourceforge.tuned.ui.TunedUtilities; import net.sourceforge.tuned.ui.TunedUtilities;
@ -136,11 +136,18 @@ public class EpisodeFormatDialog extends JDialog {
header.setComponentPopupMenu(createPreviewSamplePopup()); header.setComponentPopupMenu(createPreviewSamplePopup());
// setup undo support
final UndoManager undo = new UndoManager();
editor.getDocument().addUndoableEditListener(undo);
// enable undo/redo
TunedUtilities.installUndoSupport(editor);
// update format on change // update format on change
editor.getDocument().addDocumentListener(new LazyDocumentAdapter() { editor.getDocument().addDocumentListener(new LazyDocumentListener() {
@Override @Override
public void update() { public void update(DocumentEvent e) {
checkFormatInBackground(); checkFormatInBackground();
} }
}); });
@ -207,7 +214,7 @@ public class EpisodeFormatDialog extends JDialog {
File mediaFile = fileChooser.getSelectedFile(); File mediaFile = fileChooser.getSelectedFile();
try { try {
MediaInfoComponent.showMessageDialog(EpisodeFormatDialog.this, mediaFile); MediaInfoPane.showMessageDialog(EpisodeFormatDialog.this, mediaFile);
} catch (LinkageError e) { } catch (LinkageError e) {
// MediaInfo native library is missing -> notify user // MediaInfo native library is missing -> notify user
Logger.getLogger("ui").log(Level.SEVERE, e.getMessage(), e); Logger.getLogger("ui").log(Level.SEVERE, e.getMessage(), e);
@ -490,43 +497,4 @@ public class EpisodeFormatDialog extends JDialog {
firePropertyChange("previewSample", null, previewSample); firePropertyChange("previewSample", null, previewSample);
} }
protected static abstract class LazyDocumentAdapter implements DocumentListener {
private final Timer timer = new Timer(200, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
update();
}
});
public LazyDocumentAdapter() {
timer.setRepeats(false);
}
@Override
public void changedUpdate(DocumentEvent e) {
timer.restart();
}
@Override
public void insertUpdate(DocumentEvent e) {
timer.restart();
}
@Override
public void removeUpdate(DocumentEvent e) {
timer.restart();
}
public abstract void update();
}
} }

View File

@ -50,7 +50,7 @@ public class FileBotList<E> extends JComponent {
// Shortcut DELETE, disabled by default // Shortcut DELETE, disabled by default
removeAction.setEnabled(false); removeAction.setEnabled(false);
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction); TunedUtilities.installAction(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
} }

View File

@ -12,6 +12,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JDialog; import javax.swing.JDialog;
@ -26,61 +27,62 @@ import net.sourceforge.filebot.mediainfo.MediaInfo.StreamKind;
import net.sourceforge.tuned.ui.TunedUtilities; import net.sourceforge.tuned.ui.TunedUtilities;
public class MediaInfoComponent extends JTabbedPane { public class MediaInfoPane extends JTabbedPane {
public MediaInfoComponent(Map<StreamKind, List<Map<String, String>>> mediaInfo) { public MediaInfoPane(File file) {
insert(mediaInfo); // get media info
MediaInfo mediaInfo = new MediaInfo();
if (!mediaInfo.open(file))
throw new IllegalArgumentException("Cannot open file: " + file);
// create tab for each stream
for (Entry<StreamKind, List<Map<String, String>>> entry : mediaInfo.snapshot().entrySet()) {
for (Map<String, String> parameters : entry.getValue()) {
addTableTab(entry.getKey().toString(), parameters);
}
}
mediaInfo.close();
} }
public void insert(Map<StreamKind, List<Map<String, String>>> mediaInfo) { public void addTableTab(String title, Map<String, String> data) {
// create tabs for all streams JTable table = new JTable(new ParameterTableModel(data));
for (Entry<StreamKind, List<Map<String, String>>> entry : mediaInfo.entrySet()) {
for (Map<String, String> parameters : entry.getValue()) { // allow sorting
JTable table = new JTable(new ParameterTableModel(parameters)); table.setAutoCreateRowSorter(true);
// allow sorting // sort by parameter name
table.setAutoCreateRowSorter(true); table.getRowSorter().toggleSortOrder(0);
// sort by parameter name addTab(title, new JScrollPane(table));
table.getRowSorter().toggleSortOrder(0);
addTab(entry.getKey().toString(), new JScrollPane(table));
}
}
} }
public static void showMessageDialog(Component parent, File file) { public static void showMessageDialog(Component parent, File file) {
final JDialog dialog = new JDialog(TunedUtilities.getWindow(parent), "MediaInfo", ModalityType.DOCUMENT_MODAL); final JDialog dialog = new JDialog(TunedUtilities.getWindow(parent), "MediaInfo", ModalityType.DOCUMENT_MODAL);
dialog.setLocation(TunedUtilities.getPreferredLocation(dialog)); dialog.setLocationByPlatform(true);
JComponent c = (JComponent) dialog.getContentPane(); Action closeAction = new AbstractAction("OK") {
c.setLayout(new MigLayout("fill", "[align center]", "[fill][pref!]"));
MediaInfo mediaInfo = new MediaInfo();
mediaInfo.open(file);
MediaInfoComponent mediaInfoComponent = new MediaInfoComponent(mediaInfo.snapshot());
mediaInfo.close();
c.add(mediaInfoComponent, "grow, wrap");
c.add(new JButton(new AbstractAction("OK") {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
dialog.setVisible(false); dialog.setVisible(false);
} }
}), "wmin 80px, hmin 25px"); };
JComponent c = (JComponent) dialog.getContentPane();
c.setLayout(new MigLayout("fill", "[align center]", "[fill][pref!]"));
c.add(new MediaInfoPane(file), "grow, wrap");
c.add(new JButton(closeAction), "wmin 80px, hmin 25px");
dialog.pack(); dialog.pack();
dialog.setVisible(true); dialog.setVisible(true);
} }
protected static class ParameterTableModel extends AbstractTableModel { private static class ParameterTableModel extends AbstractTableModel {
private final List<Entry<String, String>> data; private final List<Entry<String, String>> data;

View File

@ -53,8 +53,8 @@ public class SelectButtonTextField<T> extends JComponent {
editor.setRenderer(new CompletionCellRenderer()); editor.setRenderer(new CompletionCellRenderer());
editor.setUI(new TextFieldComboBoxUI()); editor.setUI(new TextFieldComboBoxUI());
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("ctrl UP"), new SpinClientAction(-1)); TunedUtilities.installAction(this, KeyStroke.getKeyStroke("ctrl UP"), new SpinClientAction(-1));
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("ctrl DOWN"), new SpinClientAction(1)); TunedUtilities.installAction(this, KeyStroke.getKeyStroke("ctrl DOWN"), new SpinClientAction(1));
} }
@ -87,6 +87,7 @@ public class SelectButtonTextField<T> extends JComponent {
public SpinClientAction(int spin) { public SpinClientAction(int spin) {
super(String.format("Spin%+d", spin));
this.spin = spin; this.spin = spin;
} }

View File

@ -66,7 +66,7 @@ public class SelectDialog<T> extends JDialog {
setLocation(TunedUtilities.getPreferredLocation(this)); setLocation(TunedUtilities.getPreferredLocation(this));
// Shortcut Enter // Shortcut Enter
TunedUtilities.putActionForKeystroke(list, KeyStroke.getKeyStroke("released ENTER"), selectAction); TunedUtilities.installAction(list, KeyStroke.getKeyStroke("released ENTER"), selectAction);
} }

View File

@ -53,7 +53,7 @@ class FileTreePanel extends JComponent {
}); });
// Shortcut DELETE // Shortcut DELETE
TunedUtilities.putActionForKeystroke(fileTree, KeyStroke.getKeyStroke("pressed DELETE"), removeAction); TunedUtilities.installAction(fileTree, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
} }

View File

@ -64,8 +64,8 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
searchTextField.getSelectButton().addPropertyChangeListener(SelectButton.SELECTED_VALUE, selectButtonListener); searchTextField.getSelectButton().addPropertyChangeListener(SelectButton.SELECTED_VALUE, selectButtonListener);
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinSeasonAction(1)); TunedUtilities.installAction(this, KeyStroke.getKeyStroke("shift UP"), new SpinSeasonAction(1));
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinSeasonAction(-1)); TunedUtilities.installAction(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinSeasonAction(-1));
} }
@ -122,6 +122,7 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
private class SpinSeasonAction extends AbstractAction { private class SpinSeasonAction extends AbstractAction {
public SpinSeasonAction(int spin) { public SpinSeasonAction(int spin) {
super(String.format("Spin%+d", spin));
putValue("spin", spin); putValue("spin", spin);
} }

View File

@ -88,7 +88,7 @@ public class ListPanel extends JComponent {
list.add(buttonPanel, BorderLayout.SOUTH); list.add(buttonPanel, BorderLayout.SOUTH);
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), createAction); TunedUtilities.installAction(this, KeyStroke.getKeyStroke("ENTER"), createAction);
} }
private AbstractAction createAction = new AbstractAction("Create") { private AbstractAction createAction = new AbstractAction("Create") {

View File

@ -4,7 +4,6 @@ package net.sourceforge.filebot.ui.panel.rename;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -84,7 +83,7 @@ class History {
public List<Sequence> sequences() { public List<Sequence> sequences() {
return Collections.unmodifiableList(sequences); return sequences;
} }

View File

@ -72,7 +72,7 @@ class ValidateNamesDialog extends JDialog {
setSize(365, 280); setSize(365, 280);
setLocation(TunedUtilities.getPreferredLocation(this)); setLocation(TunedUtilities.getPreferredLocation(this));
TunedUtilities.putActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction); TunedUtilities.installAction(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
} }

View File

@ -91,7 +91,7 @@ public class SfvPanel extends JComponent {
putClientProperty("transferablePolicy", transferablePolicy); putClientProperty("transferablePolicy", transferablePolicy);
// Shortcut DELETE // Shortcut DELETE
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction); TunedUtilities.installAction(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
} }

View File

@ -0,0 +1,60 @@
package net.sourceforge.tuned.ui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public abstract class LazyDocumentListener implements DocumentListener {
private DocumentEvent lastEvent;
private final Timer timer = new Timer(200, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
update(lastEvent);
// we don't need it anymore
lastEvent = null;
}
});
public LazyDocumentListener() {
timer.setRepeats(false);
}
private void defer(DocumentEvent e) {
lastEvent = e;
timer.restart();
}
@Override
public void changedUpdate(DocumentEvent e) {
defer(e);
}
@Override
public void insertUpdate(DocumentEvent e) {
defer(e);
}
@Override
public void removeUpdate(DocumentEvent e) {
defer(e);
}
public abstract void update(DocumentEvent e);
}

View File

@ -18,6 +18,7 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.swing.AbstractAction;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
@ -29,6 +30,8 @@ import javax.swing.SwingUtilities;
import javax.swing.Timer; import javax.swing.Timer;
import javax.swing.event.MouseInputListener; import javax.swing.event.MouseInputListener;
import javax.swing.plaf.basic.BasicTableUI; import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.text.JTextComponent;
import javax.swing.undo.UndoManager;
import net.sourceforge.tuned.ExceptionUtilities; import net.sourceforge.tuned.ExceptionUtilities;
@ -50,13 +53,47 @@ public final class TunedUtilities {
} }
public static void putActionForKeystroke(JComponent component, KeyStroke keystroke, Action action) { public static void installAction(JComponent component, KeyStroke keystroke, Action action) {
Integer key = action.hashCode(); Object key = action.getValue(Action.NAME);
if (key == null)
throw new IllegalArgumentException("Action must have a name");
component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keystroke, key); component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keystroke, key);
component.getActionMap().put(key, action); component.getActionMap().put(key, action);
} }
public static UndoManager installUndoSupport(JTextComponent component) {
final UndoManager undoSupport = new UndoManager();
// install undo listener
component.getDocument().addUndoableEditListener(undoSupport);
// install undo action
installAction(component, KeyStroke.getKeyStroke("control Z"), new AbstractAction("Undo") {
@Override
public void actionPerformed(ActionEvent e) {
if (undoSupport.canUndo())
undoSupport.undo();
}
});
// install redo action
installAction(component, KeyStroke.getKeyStroke("control Y"), new AbstractAction("Redo") {
@Override
public void actionPerformed(ActionEvent e) {
if (undoSupport.canRedo())
undoSupport.redo();
}
});
return undoSupport;
}
public static Window getWindow(Component component) { public static Window getWindow(Component component) {
if (component == null) if (component == null)
return null; return null;