Refactor Presets and enable keyboard shortcuts for Presets 1..9 via number keys

This commit is contained in:
Reinhard Pointner 2016-08-11 07:06:18 +08:00
parent dcfcc91090
commit 824ce14c62
5 changed files with 102 additions and 83 deletions

View File

@ -16,6 +16,7 @@ import net.filebot.archive.Archive.Extractor;
import net.filebot.cli.ArgumentBean; import net.filebot.cli.ArgumentBean;
import net.filebot.util.PreferencesList; import net.filebot.util.PreferencesList;
import net.filebot.util.PreferencesMap; import net.filebot.util.PreferencesMap;
import net.filebot.util.PreferencesMap.JsonAdapter;
import net.filebot.util.PreferencesMap.PreferencesEntry; import net.filebot.util.PreferencesMap.PreferencesEntry;
import net.filebot.util.PreferencesMap.StringAdapter; import net.filebot.util.PreferencesMap.StringAdapter;
@ -234,10 +235,18 @@ public final class Settings {
return PreferencesMap.map(prefs); return PreferencesMap.map(prefs);
} }
public <T> PreferencesMap<T> asTypedMap(Class<T> cls) {
return PreferencesMap.map(prefs, new JsonAdapter(cls));
}
public PreferencesList<String> asList() { public PreferencesList<String> asList() {
return PreferencesList.map(prefs); return PreferencesList.map(prefs);
} }
public <T> PreferencesList<T> asTypedList(Class<T> cls) {
return PreferencesList.map(prefs, new JsonAdapter(cls));
}
public void clear() { public void clear() {
try { try {
// remove child nodes // remove child nodes

View File

@ -41,12 +41,11 @@ import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.border.CompoundBorder; import javax.swing.border.CompoundBorder;
import javax.swing.border.TitledBorder; import javax.swing.border.TitledBorder;
import com.cedarsoftware.util.io.JsonReader;
import com.cedarsoftware.util.io.JsonWriter;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.EventList;
@ -110,6 +109,8 @@ public class RenamePanel extends JComponent {
private static final PreferencesEntry<String> persistentPreferredLanguage = Settings.forPackage(RenamePanel.class).entry("rename.language").defaultValue("en"); private static final PreferencesEntry<String> persistentPreferredLanguage = Settings.forPackage(RenamePanel.class).entry("rename.language").defaultValue("en");
private static final PreferencesEntry<String> persistentPreferredEpisodeOrder = Settings.forPackage(RenamePanel.class).entry("rename.episode.order").defaultValue("Airdate"); private static final PreferencesEntry<String> persistentPreferredEpisodeOrder = Settings.forPackage(RenamePanel.class).entry("rename.episode.order").defaultValue("Airdate");
private static final Map<String, Preset> persistentPresets = Settings.forPackage(RenamePanel.class).node("presets").asTypedMap(Preset.class);
public RenamePanel() { public RenamePanel() {
namesList.setTitle("New Names"); namesList.setTitle("New Names");
namesList.setTransferablePolicy(new NamesListTransferablePolicy(renameModel.values())); namesList.setTransferablePolicy(new NamesListTransferablePolicy(renameModel.values()));
@ -241,9 +242,8 @@ public class RenamePanel extends JComponent {
filesList.getButtonPanel().add(createImageButton(openHistoryAction), "gap indent"); filesList.getButtonPanel().add(createImageButton(openHistoryAction), "gap indent");
// create macros popup // create macros popup
final Action macrosAction = new ShowPresetsPopupAction("Presets", ResourceManager.getIcon("action.script")); JButton presetsButton = createImageButton(new ShowPresetsPopupAction("Presets", ResourceManager.getIcon("action.script")));
JButton macrosButton = createImageButton(macrosAction); filesList.getButtonPanel().add(presetsButton, "gap 0");
filesList.getButtonPanel().add(macrosButton, "gap 0");
// show popup on actionPerformed only when names list is empty // show popup on actionPerformed only when names list is empty
matchButton.addActionListener(evt -> { matchButton.addActionListener(evt -> {
@ -302,8 +302,13 @@ public class RenamePanel extends JComponent {
add(new LoadingOverlayPane(namesList, namesList, "37px", "30px"), "grow, sizegroupx list"); add(new LoadingOverlayPane(namesList, namesList, "37px", "30px"), "grow, sizegroupx list");
// install F2 and 1..9 keystroke actions
SwingUtilities.invokeLater(this::installKeyStrokeActions);
}
private void installKeyStrokeActions() {
// manual force name via F2 // manual force name via F2
installAction(namesList.getListComponent(), getKeyStroke(VK_F2, 0), newAction("Force Name", evt -> { installAction(this, WHEN_IN_FOCUSED_WINDOW, getKeyStroke(VK_F2, 0), newAction("Force Name", evt -> {
try { try {
if (namesList.getModel().isEmpty()) { if (namesList.getModel().isEmpty()) {
withWaitCursor(evt.getSource(), () -> { withWaitCursor(evt.getSource(), () -> {
@ -336,61 +341,72 @@ public class RenamePanel extends JComponent {
} }
} }
} catch (Exception e) { } catch (Exception e) {
debug.log(Level.WARNING, e.getMessage(), e); debug.log(Level.WARNING, e::toString);
} }
})); }));
// map 1..9 number keys to presets
try {
Preset[] presets = persistentPresets.values().toArray(new Preset[0]);
for (int i = 0; i < presets.length && i < 9; i++) {
Preset preset = presets[i];
int key = Character.forDigit(i + 1, 10);
installAction(this, WHEN_IN_FOCUSED_WINDOW, getKeyStroke(key, 0), newAction(preset.getName(), new ApplyPresetAction(preset)::actionPerformed));
}
} catch (Exception e) {
debug.log(Level.WARNING, e::toString);
}
} }
private boolean isMatchModeStrict() { private boolean isMatchModeStrict() {
return MATCH_MODE_STRICT.equalsIgnoreCase(persistentPreferredMatchMode.getValue()); return MATCH_MODE_STRICT.equalsIgnoreCase(persistentPreferredMatchMode.getValue());
} }
protected ActionPopup createPresetsPopup() { private ActionPopup createPresetsPopup() {
Map<String, String> persistentPresets = Settings.forPackage(RenamePanel.class).node("presets").asMap();
ActionPopup actionPopup = new ActionPopup("Presets", ResourceManager.getIcon("action.script")); ActionPopup actionPopup = new ActionPopup("Presets", ResourceManager.getIcon("action.script"));
if (persistentPresets.size() > 0) { try {
for (String it : persistentPresets.values()) { if (persistentPresets.size() > 0) {
try { for (Preset preset : persistentPresets.values()) {
Preset p = (Preset) JsonReader.jsonToJava(it); actionPopup.add(new ApplyPresetAction(preset));
actionPopup.add(new ApplyPresetAction(p));
} catch (Exception e) {
debug.log(Level.SEVERE, e.getMessage(), e);
} }
actionPopup.addSeparator();
} }
actionPopup.addSeparator(); } catch (Exception e) {
debug.log(Level.WARNING, e, e::toString);
} }
actionPopup.add(newAction("Edit Presets", ResourceManager.getIcon("script.add"), evt -> { actionPopup.add(newAction("Edit Presets", ResourceManager.getIcon("script.add"), evt -> {
try { try {
String newPresetOption = "New Preset …"; String newPreset = "New Preset …";
List<String> presetNames = new ArrayList<String>(persistentPresets.keySet()); List<String> presetNames = new ArrayList<String>(persistentPresets.keySet());
presetNames.add(newPresetOption); presetNames.add(newPreset);
String selection = (String) showInputDialog(getWindow(evt.getSource()), "Edit or create a preset:", "Edit Preset", PLAIN_MESSAGE, null, presetNames.toArray(), newPresetOption); String selection = (String) showInputDialog(getWindow(evt.getSource()), "Edit or create a preset:", "Edit Preset", PLAIN_MESSAGE, null, presetNames.toArray(), newPreset);
if (selection == null) if (selection == null) {
return; return;
Preset preset = null;
if (selection == newPresetOption) {
selection = (String) showInputDialog(getWindow(evt.getSource()), "Preset Name:", newPresetOption, PLAIN_MESSAGE, null, null, "My Preset");
if (selection == null || selection.trim().isEmpty())
return;
preset = new Preset(selection.trim(), null, null, null, null, null, null, null, null);
} else {
preset = (Preset) JsonReader.jsonToJava(persistentPresets.get(selection.toString()));
} }
PresetEditor presetEditor = new PresetEditor(getWindow(evt.getSource())); PresetEditor presetEditor = new PresetEditor(getWindow(evt.getSource()));
if (selection == newPreset) {
selection = (String) showInputDialog(getWindow(evt.getSource()), "Preset Name:", newPreset, PLAIN_MESSAGE, null, null, "My Preset");
if (selection == null || selection.trim().isEmpty()) {
return;
}
presetEditor.setPreset(new Preset(selection.trim(), null, null, null, null, null, null, null, null));
} else {
presetEditor.setPreset(persistentPresets.get(selection));
}
presetEditor.setLocation(getOffsetLocation(presetEditor.getOwner())); presetEditor.setLocation(getOffsetLocation(presetEditor.getOwner()));
presetEditor.setPreset(preset);
presetEditor.setVisible(true); presetEditor.setVisible(true);
switch (presetEditor.getResult()) { switch (presetEditor.getResult()) {
case SET: case SET:
preset = presetEditor.getPreset(); persistentPresets.put(selection, presetEditor.getPreset());
persistentPresets.put(selection, JsonWriter.objectToJson(preset));
break; break;
case DELETE: case DELETE:
persistentPresets.remove(selection); persistentPresets.remove(selection);
@ -399,14 +415,14 @@ public class RenamePanel extends JComponent {
break; break;
} }
} catch (Exception e) { } catch (Exception e) {
debug.log(Level.WARNING, e.toString()); debug.log(Level.WARNING, e, e::toString);
} }
})); }));
return actionPopup; return actionPopup;
} }
protected ActionPopup createFetchPopup() { private ActionPopup createFetchPopup() {
ActionPopup actionPopup = new ActionPopup("Fetch & Match Data", ResourceManager.getIcon("action.fetch")); ActionPopup actionPopup = new ActionPopup("Fetch & Match Data", ResourceManager.getIcon("action.fetch"));
actionPopup.addDescription(new JLabel("Episode Mode:")); actionPopup.addDescription(new JLabel("Episode Mode:"));
@ -508,7 +524,7 @@ public class RenamePanel extends JComponent {
return actionPopup; return actionPopup;
} }
protected ActionPopup createSettingsPopup() { private ActionPopup createSettingsPopup() {
ActionPopup actionPopup = new ActionPopup("Rename Options", ResourceManager.getIcon("action.settings")); ActionPopup actionPopup = new ActionPopup("Rename Options", ResourceManager.getIcon("action.settings"));
actionPopup.addDescription(new JLabel("Extension:")); actionPopup.addDescription(new JLabel("Extension:"));
@ -525,7 +541,7 @@ public class RenamePanel extends JComponent {
return actionPopup; return actionPopup;
} }
protected Mode getFormatEditorMode(MediaBindingBean binding) { private Mode getFormatEditorMode(MediaBindingBean binding) {
if (binding != null) { if (binding != null) {
if (binding.getInfoObject() instanceof Episode) { if (binding.getInfoObject() instanceof Episode) {
return Mode.Episode; return Mode.Episode;
@ -549,7 +565,7 @@ public class RenamePanel extends JComponent {
return Mode.Episode; // default to Episode mode return Mode.Episode; // default to Episode mode
} }
protected void showFormatEditor(MediaBindingBean binding) { private void showFormatEditor(MediaBindingBean binding) {
try { try {
withWaitCursor(this, () -> { withWaitCursor(this, () -> {
FormatDialog dialog = new FormatDialog(getWindowAncestor(RenamePanel.this), getFormatEditorMode(binding), binding); FormatDialog dialog = new FormatDialog(getWindowAncestor(RenamePanel.this), getFormatEditorMode(binding), binding);
@ -586,7 +602,7 @@ public class RenamePanel extends JComponent {
} }
} }
protected final Action clearFilesAction = newAction("Clear All", ResourceManager.getIcon("action.clear"), evt -> { private final Action clearFilesAction = newAction("Clear All", ResourceManager.getIcon("action.clear"), evt -> {
if (isShiftOrAltDown(evt)) { if (isShiftOrAltDown(evt)) {
renameModel.files().clear(); renameModel.files().clear();
} else { } else {
@ -594,7 +610,7 @@ public class RenamePanel extends JComponent {
} }
}); });
protected final Action openHistoryAction = newAction("Open History", ResourceManager.getIcon("action.report"), evt -> { private final Action openHistoryAction = newAction("Open History", ResourceManager.getIcon("action.report"), evt -> {
try { try {
History model = HistorySpooler.getInstance().getCompleteHistory(); History model = HistorySpooler.getInstance().getCompleteHistory();
@ -619,7 +635,7 @@ public class RenamePanel extends JComponent {
} }
} }
protected static class ShowPopupAction extends AbstractAction { private static class ShowPopupAction extends AbstractAction {
public ShowPopupAction(String name, Icon icon) { public ShowPopupAction(String name, Icon icon) {
super(name, icon); super(name, icon);
@ -633,7 +649,7 @@ public class RenamePanel extends JComponent {
} }
}; };
protected class ShowPresetsPopupAction extends AbstractAction { private class ShowPresetsPopupAction extends AbstractAction {
public ShowPresetsPopupAction(String name, Icon icon) { public ShowPresetsPopupAction(String name, Icon icon) {
super(name, icon); super(name, icon);
@ -647,7 +663,7 @@ public class RenamePanel extends JComponent {
} }
}; };
protected class ApplyPresetAction extends AutoCompleteAction { private class ApplyPresetAction extends AutoCompleteAction {
private Preset preset; private Preset preset;
@ -724,7 +740,7 @@ public class RenamePanel extends JComponent {
} }
} }
protected class SetRenameMode extends AbstractAction { private class SetRenameMode extends AbstractAction {
private final boolean activate; private final boolean activate;
@ -745,7 +761,7 @@ public class RenamePanel extends JComponent {
} }
} }
protected class SetRenameAction extends AbstractAction { private class SetRenameAction extends AbstractAction {
private final StandardRenameAction action; private final StandardRenameAction action;
@ -766,7 +782,7 @@ public class RenamePanel extends JComponent {
} }
} }
protected class AutoCompleteAction extends AbstractAction { private class AutoCompleteAction extends AbstractAction {
protected final Supplier<AutoCompleteMatcher> matcher; protected final Supplier<AutoCompleteMatcher> matcher;

View File

@ -2,12 +2,6 @@ package net.filebot.util;
import static net.filebot.Logging.*; import static net.filebot.Logging.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -20,6 +14,9 @@ import java.util.logging.Level;
import java.util.prefs.BackingStoreException; import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import com.cedarsoftware.util.io.JsonReader;
import com.cedarsoftware.util.io.JsonWriter;
public class PreferencesMap<T> implements Map<String, T> { public class PreferencesMap<T> implements Map<String, T> {
private final Preferences prefs; private final Preferences prefs;
@ -220,40 +217,32 @@ public class PreferencesMap<T> implements Map<String, T> {
} }
public static class SerializableAdapter<T extends Serializable> extends AbstractAdapter<T> { public static class JsonAdapter<T> extends AbstractAdapter<T> {
private Class<T> type;
public JsonAdapter(Class<T> type) {
this.type = type;
}
@SuppressWarnings("unchecked")
@Override @Override
public T get(Preferences prefs, String key) { public T get(Preferences prefs, String key) {
byte[] bytes = prefs.getByteArray(key, null); String json = prefs.get(key, null);
if (bytes == null) if (json != null) {
return null; try {
return type.cast(JsonReader.jsonToJava(json));
try { } catch (Exception e) {
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes)); debug.log(Level.WARNING, e, e::getMessage);
Object object = in.readObject(); }
in.close();
return (T) object;
} catch (Exception e) {
throw new RuntimeException(e);
} }
return null;
} }
@Override @Override
public void put(Preferences prefs, String key, T value) { public void put(Preferences prefs, String key, T value) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); prefs.put(key, JsonWriter.objectToJson(value));
try {
ObjectOutputStream out = new ObjectOutputStream(buffer);
out.writeObject(value);
out.close();
prefs.putByteArray(key, buffer.toByteArray());
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
} }

View File

@ -139,20 +139,24 @@ public final class SwingUI {
} }
public static void installAction(JComponent component, KeyStroke keystroke, Action action) { public static void installAction(JComponent component, KeyStroke keystroke, Action action) {
installAction(component, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keystroke, action);
}
public static void installAction(JComponent component, int condition, KeyStroke keystroke, Action action) {
Object key = action.getValue(Action.NAME); Object key = action.getValue(Action.NAME);
if (key == null) { if (key == null) {
throw new IllegalArgumentException("Action must have a name"); throw new IllegalArgumentException("Action must have a name");
} }
component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keystroke, key); component.getInputMap(condition).put(keystroke, key);
component.getActionMap().put(key, action); component.getActionMap().put(key, action);
// automatically add Mac OS X compatibility (on Mac the BACKSPACE key is called DELETE, and there is no DELETE key) // automatically add Mac OS X compatibility (on Mac the BACKSPACE key is called DELETE, and there is no DELETE key)
if (keystroke.getKeyCode() == KeyEvent.VK_DELETE) { if (keystroke.getKeyCode() == KeyEvent.VK_DELETE) {
KeyStroke macKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, keystroke.getModifiers(), keystroke.isOnKeyRelease()); KeyStroke macKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, keystroke.getModifiers(), keystroke.isOnKeyRelease());
Object macKey = "mac." + action.getValue(Action.NAME); Object macKey = "mac." + action.getValue(Action.NAME);
component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(macKeyStroke, macKey); component.getInputMap(condition).put(macKeyStroke, macKey);
component.getActionMap().put(macKey, action); component.getActionMap().put(macKey, action);
} }
} }

View File

@ -14,7 +14,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import net.filebot.util.PreferencesMap.SerializableAdapter; import net.filebot.util.PreferencesMap.JsonAdapter;
import net.filebot.util.PreferencesMap.SimpleAdapter; import net.filebot.util.PreferencesMap.SimpleAdapter;
public class PreferencesMapTest { public class PreferencesMapTest {
@ -144,11 +144,12 @@ public class PreferencesMapTest {
} }
@Test @Test
public void serializableAdapter() { public void jsonAdapter() {
Map<String, Color> map = PreferencesMap.map(temp, new SerializableAdapter<Color>()); Map<String, Color> map = PreferencesMap.map(temp, new JsonAdapter<Color>(Color.class));
Color color = new Color(0.25f, 0.50f, 1.00f); Color color = new Color(0.25f, 0.50f, 1.00f);
map.put("color", color); map.put("color", color);
assertEquals(color, map.get("color")); assertEquals(color, map.get("color"));
} }
} }