+ added replaceTrailingBraces() to ExpressionFormat context
+ added Undo/Redo support in EpisodeListDialog * refactoring
This commit is contained in:
parent
216dd4d383
commit
9aad7deae2
|
@ -31,7 +31,11 @@ String.prototype.space = function(replacement) {
|
|||
* Remove trailing parenthesis including any leading whitespace.
|
||||
*
|
||||
* e.g. "Doctor Who (2005)" -> "Doctor Who"
|
||||
* "Bad Wolf (1)" -> "Bad Wolf, Part 1"
|
||||
*/
|
||||
String.prototype.removeTrailingBraces = function() {
|
||||
return this.replace(/\s*\([^\)]*\)$/, "");
|
||||
String.prototype.replaceTrailingBraces = function(replacement) {
|
||||
// use empty string as default replacement
|
||||
var r = replacement ? replacement : "";
|
||||
|
||||
return this.replace(/\s*\(([^\)]*)\)$/, r);
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ public class ExpressionFormat extends Format {
|
|||
ScriptContext context = new SimpleScriptContext();
|
||||
|
||||
// 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) {
|
||||
if (snipped instanceof CompiledScript) {
|
||||
|
@ -161,7 +161,7 @@ public class ExpressionFormat extends Format {
|
|||
Object snipped = compilation[i];
|
||||
|
||||
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 final Bindings bindings;
|
||||
private final AccessControlContext context;
|
||||
|
||||
|
||||
private PrivilegedBindings(Bindings bindings) {
|
||||
private PrivilegedBindings(Bindings bindings, AccessControlContext context) {
|
||||
this.bindings = bindings;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
|
@ -200,7 +202,7 @@ public class ExpressionFormat extends Format {
|
|||
public Object run() throws Exception {
|
||||
return method.invoke(bindings, args);
|
||||
}
|
||||
});
|
||||
}, context);
|
||||
} catch (PrivilegedActionException e) {
|
||||
Throwable cause = e.getException();
|
||||
|
||||
|
@ -216,8 +218,8 @@ public class ExpressionFormat extends Format {
|
|||
}
|
||||
|
||||
|
||||
public static Bindings newProxy(Bindings bindings) {
|
||||
PrivilegedBindings invocationHandler = new PrivilegedBindings(bindings);
|
||||
public static Bindings newProxy(Bindings bindings, AccessControlContext context) {
|
||||
InvocationHandler invocationHandler = new PrivilegedBindings(bindings, context);
|
||||
|
||||
// create dynamic invocation proxy
|
||||
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 final AccessControlContext sandbox;
|
||||
private final CompiledScript compiledScript;
|
||||
private final AccessControlContext sandbox;
|
||||
|
||||
|
||||
private SecureCompiledScript(AccessControlContext sandbox, CompiledScript compiledScript) {
|
||||
this.sandbox = sandbox;
|
||||
private SecureCompiledScript(CompiledScript compiledScript, AccessControlContext sandbox) {
|
||||
this.compiledScript = compiledScript;
|
||||
this.sandbox = sandbox;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
|||
|
||||
AutoCompleteSupport.install(searchTextField.getEditor(), searchHistory).setFilterMode(TextMatcherEditor.CONTAINS);
|
||||
|
||||
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
|
||||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.awt.Color;
|
|||
import java.awt.Font;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
|
@ -45,8 +44,8 @@ import javax.swing.SwingWorker;
|
|||
import javax.swing.Timer;
|
||||
import javax.swing.border.LineBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import javax.swing.undo.UndoManager;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
|
@ -58,6 +57,7 @@ import net.sourceforge.filebot.web.EpisodeFormat;
|
|||
import net.sourceforge.tuned.DefaultThreadFactory;
|
||||
import net.sourceforge.tuned.ExceptionUtilities;
|
||||
import net.sourceforge.tuned.ui.GradientStyle;
|
||||
import net.sourceforge.tuned.ui.LazyDocumentListener;
|
||||
import net.sourceforge.tuned.ui.LinkButton;
|
||||
import net.sourceforge.tuned.ui.ProgressIndicator;
|
||||
import net.sourceforge.tuned.ui.TunedUtilities;
|
||||
|
@ -136,11 +136,18 @@ public class EpisodeFormatDialog extends JDialog {
|
|||
|
||||
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
|
||||
editor.getDocument().addDocumentListener(new LazyDocumentAdapter() {
|
||||
editor.getDocument().addDocumentListener(new LazyDocumentListener() {
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
public void update(DocumentEvent e) {
|
||||
checkFormatInBackground();
|
||||
}
|
||||
});
|
||||
|
@ -207,7 +214,7 @@ public class EpisodeFormatDialog extends JDialog {
|
|||
File mediaFile = fileChooser.getSelectedFile();
|
||||
|
||||
try {
|
||||
MediaInfoComponent.showMessageDialog(EpisodeFormatDialog.this, mediaFile);
|
||||
MediaInfoPane.showMessageDialog(EpisodeFormatDialog.this, mediaFile);
|
||||
} catch (LinkageError e) {
|
||||
// MediaInfo native library is missing -> notify user
|
||||
Logger.getLogger("ui").log(Level.SEVERE, e.getMessage(), e);
|
||||
|
@ -490,43 +497,4 @@ public class EpisodeFormatDialog extends JDialog {
|
|||
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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class FileBotList<E> extends JComponent {
|
|||
// Shortcut DELETE, disabled by default
|
||||
removeAction.setEnabled(false);
|
||||
|
||||
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Map;
|
|||
import java.util.Map.Entry;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
|
@ -26,18 +27,28 @@ import net.sourceforge.filebot.mediainfo.MediaInfo.StreamKind;
|
|||
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) {
|
||||
insert(mediaInfo);
|
||||
public MediaInfoPane(File file) {
|
||||
// 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) {
|
||||
// create tabs for all streams
|
||||
for (Entry<StreamKind, List<Map<String, String>>> entry : mediaInfo.entrySet()) {
|
||||
for (Map<String, String> parameters : entry.getValue()) {
|
||||
JTable table = new JTable(new ParameterTableModel(parameters));
|
||||
public void addTableTab(String title, Map<String, String> data) {
|
||||
JTable table = new JTable(new ParameterTableModel(data));
|
||||
|
||||
// allow sorting
|
||||
table.setAutoCreateRowSorter(true);
|
||||
|
@ -45,42 +56,33 @@ public class MediaInfoComponent extends JTabbedPane {
|
|||
// sort by parameter name
|
||||
table.getRowSorter().toggleSortOrder(0);
|
||||
|
||||
addTab(entry.getKey().toString(), new JScrollPane(table));
|
||||
}
|
||||
}
|
||||
addTab(title, new JScrollPane(table));
|
||||
}
|
||||
|
||||
|
||||
public static void showMessageDialog(Component parent, File file) {
|
||||
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();
|
||||
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") {
|
||||
Action closeAction = new AbstractAction("OK") {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
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.setVisible(true);
|
||||
}
|
||||
|
||||
|
||||
protected static class ParameterTableModel extends AbstractTableModel {
|
||||
private static class ParameterTableModel extends AbstractTableModel {
|
||||
|
||||
private final List<Entry<String, String>> data;
|
||||
|
|
@ -53,8 +53,8 @@ public class SelectButtonTextField<T> extends JComponent {
|
|||
editor.setRenderer(new CompletionCellRenderer());
|
||||
editor.setUI(new TextFieldComboBoxUI());
|
||||
|
||||
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("ctrl UP"), new SpinClientAction(-1));
|
||||
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("ctrl DOWN"), new SpinClientAction(1));
|
||||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke("ctrl UP"), 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) {
|
||||
super(String.format("Spin%+d", spin));
|
||||
this.spin = spin;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public class SelectDialog<T> extends JDialog {
|
|||
setLocation(TunedUtilities.getPreferredLocation(this));
|
||||
|
||||
// Shortcut Enter
|
||||
TunedUtilities.putActionForKeystroke(list, KeyStroke.getKeyStroke("released ENTER"), selectAction);
|
||||
TunedUtilities.installAction(list, KeyStroke.getKeyStroke("released ENTER"), selectAction);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ class FileTreePanel extends JComponent {
|
|||
});
|
||||
|
||||
// Shortcut DELETE
|
||||
TunedUtilities.putActionForKeystroke(fileTree, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
TunedUtilities.installAction(fileTree, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -64,8 +64,8 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
|||
|
||||
searchTextField.getSelectButton().addPropertyChangeListener(SelectButton.SELECTED_VALUE, selectButtonListener);
|
||||
|
||||
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinSeasonAction(1));
|
||||
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinSeasonAction(-1));
|
||||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke("shift UP"), 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 {
|
||||
|
||||
public SpinSeasonAction(int spin) {
|
||||
super(String.format("Spin%+d", spin));
|
||||
putValue("spin", spin);
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ public class ListPanel extends JComponent {
|
|||
|
||||
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") {
|
||||
|
|
|
@ -4,7 +4,6 @@ package net.sourceforge.filebot.ui.panel.rename;
|
|||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
@ -84,7 +83,7 @@ class History {
|
|||
|
||||
|
||||
public List<Sequence> sequences() {
|
||||
return Collections.unmodifiableList(sequences);
|
||||
return sequences;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ class ValidateNamesDialog extends JDialog {
|
|||
setSize(365, 280);
|
||||
setLocation(TunedUtilities.getPreferredLocation(this));
|
||||
|
||||
TunedUtilities.putActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
|
||||
TunedUtilities.installAction(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ public class SfvPanel extends JComponent {
|
|||
putClientProperty("transferablePolicy", transferablePolicy);
|
||||
|
||||
// Shortcut DELETE
|
||||
TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -18,6 +18,7 @@ import java.beans.PropertyChangeEvent;
|
|||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
@ -29,6 +30,8 @@ import javax.swing.SwingUtilities;
|
|||
import javax.swing.Timer;
|
||||
import javax.swing.event.MouseInputListener;
|
||||
import javax.swing.plaf.basic.BasicTableUI;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import javax.swing.undo.UndoManager;
|
||||
|
||||
import net.sourceforge.tuned.ExceptionUtilities;
|
||||
|
||||
|
@ -50,13 +53,47 @@ public final class TunedUtilities {
|
|||
}
|
||||
|
||||
|
||||
public static void putActionForKeystroke(JComponent component, KeyStroke keystroke, Action action) {
|
||||
Integer key = action.hashCode();
|
||||
public static void installAction(JComponent component, KeyStroke keystroke, Action action) {
|
||||
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.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) {
|
||||
if (component == null)
|
||||
return null;
|
||||
|
|
Loading…
Reference in New Issue