diff --git a/source/net/filebot/Settings.java b/source/net/filebot/Settings.java index f8a9ecbe..80c23b4e 100644 --- a/source/net/filebot/Settings.java +++ b/source/net/filebot/Settings.java @@ -16,6 +16,7 @@ import net.filebot.archive.Archive.Extractor; import net.filebot.cli.ArgumentBean; import net.filebot.util.PreferencesList; import net.filebot.util.PreferencesMap; +import net.filebot.util.PreferencesMap.JsonAdapter; import net.filebot.util.PreferencesMap.PreferencesEntry; import net.filebot.util.PreferencesMap.StringAdapter; @@ -234,10 +235,18 @@ public final class Settings { return PreferencesMap.map(prefs); } + public PreferencesMap asTypedMap(Class cls) { + return PreferencesMap.map(prefs, new JsonAdapter(cls)); + } + public PreferencesList asList() { return PreferencesList.map(prefs); } + public PreferencesList asTypedList(Class cls) { + return PreferencesList.map(prefs, new JsonAdapter(cls)); + } + public void clear() { try { // remove child nodes diff --git a/source/net/filebot/ui/rename/RenamePanel.java b/source/net/filebot/ui/rename/RenamePanel.java index 6ea6911f..2db5eb0f 100644 --- a/source/net/filebot/ui/rename/RenamePanel.java +++ b/source/net/filebot/ui/rename/RenamePanel.java @@ -41,12 +41,11 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.border.CompoundBorder; import javax.swing.border.TitledBorder; -import com.cedarsoftware.util.io.JsonReader; -import com.cedarsoftware.util.io.JsonWriter; import com.google.common.eventbus.Subscribe; import ca.odell.glazedlists.EventList; @@ -110,6 +109,8 @@ public class RenamePanel extends JComponent { private static final PreferencesEntry persistentPreferredLanguage = Settings.forPackage(RenamePanel.class).entry("rename.language").defaultValue("en"); private static final PreferencesEntry persistentPreferredEpisodeOrder = Settings.forPackage(RenamePanel.class).entry("rename.episode.order").defaultValue("Airdate"); + private static final Map persistentPresets = Settings.forPackage(RenamePanel.class).node("presets").asTypedMap(Preset.class); + public RenamePanel() { namesList.setTitle("New Names"); namesList.setTransferablePolicy(new NamesListTransferablePolicy(renameModel.values())); @@ -241,9 +242,8 @@ public class RenamePanel extends JComponent { filesList.getButtonPanel().add(createImageButton(openHistoryAction), "gap indent"); // create macros popup - final Action macrosAction = new ShowPresetsPopupAction("Presets", ResourceManager.getIcon("action.script")); - JButton macrosButton = createImageButton(macrosAction); - filesList.getButtonPanel().add(macrosButton, "gap 0"); + JButton presetsButton = createImageButton(new ShowPresetsPopupAction("Presets", ResourceManager.getIcon("action.script"))); + filesList.getButtonPanel().add(presetsButton, "gap 0"); // show popup on actionPerformed only when names list is empty matchButton.addActionListener(evt -> { @@ -302,8 +302,13 @@ public class RenamePanel extends JComponent { 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 - 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 { if (namesList.getModel().isEmpty()) { withWaitCursor(evt.getSource(), () -> { @@ -336,61 +341,72 @@ public class RenamePanel extends JComponent { } } } 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() { return MATCH_MODE_STRICT.equalsIgnoreCase(persistentPreferredMatchMode.getValue()); } - protected ActionPopup createPresetsPopup() { - Map persistentPresets = Settings.forPackage(RenamePanel.class).node("presets").asMap(); + private ActionPopup createPresetsPopup() { ActionPopup actionPopup = new ActionPopup("Presets", ResourceManager.getIcon("action.script")); - if (persistentPresets.size() > 0) { - for (String it : persistentPresets.values()) { - try { - Preset p = (Preset) JsonReader.jsonToJava(it); - actionPopup.add(new ApplyPresetAction(p)); - } catch (Exception e) { - debug.log(Level.SEVERE, e.getMessage(), e); + try { + if (persistentPresets.size() > 0) { + for (Preset preset : persistentPresets.values()) { + actionPopup.add(new ApplyPresetAction(preset)); } + actionPopup.addSeparator(); } - actionPopup.addSeparator(); + } catch (Exception e) { + debug.log(Level.WARNING, e, e::toString); } actionPopup.add(newAction("Edit Presets", ResourceManager.getIcon("script.add"), evt -> { try { - String newPresetOption = "New Preset …"; + String newPreset = "New Preset …"; List presetNames = new ArrayList(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); - if (selection == null) + String selection = (String) showInputDialog(getWindow(evt.getSource()), "Edit or create a preset:", "Edit Preset", PLAIN_MESSAGE, null, presetNames.toArray(), newPreset); + if (selection == null) { 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())); + + 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.setPreset(preset); presetEditor.setVisible(true); switch (presetEditor.getResult()) { case SET: - preset = presetEditor.getPreset(); - persistentPresets.put(selection, JsonWriter.objectToJson(preset)); + persistentPresets.put(selection, presetEditor.getPreset()); break; case DELETE: persistentPresets.remove(selection); @@ -399,14 +415,14 @@ public class RenamePanel extends JComponent { break; } } catch (Exception e) { - debug.log(Level.WARNING, e.toString()); + debug.log(Level.WARNING, e, e::toString); } })); return actionPopup; } - protected ActionPopup createFetchPopup() { + private ActionPopup createFetchPopup() { ActionPopup actionPopup = new ActionPopup("Fetch & Match Data", ResourceManager.getIcon("action.fetch")); actionPopup.addDescription(new JLabel("Episode Mode:")); @@ -508,7 +524,7 @@ public class RenamePanel extends JComponent { return actionPopup; } - protected ActionPopup createSettingsPopup() { + private ActionPopup createSettingsPopup() { ActionPopup actionPopup = new ActionPopup("Rename Options", ResourceManager.getIcon("action.settings")); actionPopup.addDescription(new JLabel("Extension:")); @@ -525,7 +541,7 @@ public class RenamePanel extends JComponent { return actionPopup; } - protected Mode getFormatEditorMode(MediaBindingBean binding) { + private Mode getFormatEditorMode(MediaBindingBean binding) { if (binding != null) { if (binding.getInfoObject() instanceof Episode) { return Mode.Episode; @@ -549,7 +565,7 @@ public class RenamePanel extends JComponent { return Mode.Episode; // default to Episode mode } - protected void showFormatEditor(MediaBindingBean binding) { + private void showFormatEditor(MediaBindingBean binding) { try { withWaitCursor(this, () -> { 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)) { renameModel.files().clear(); } 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 { 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) { 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) { 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; @@ -724,7 +740,7 @@ public class RenamePanel extends JComponent { } } - protected class SetRenameMode extends AbstractAction { + private class SetRenameMode extends AbstractAction { 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; @@ -766,7 +782,7 @@ public class RenamePanel extends JComponent { } } - protected class AutoCompleteAction extends AbstractAction { + private class AutoCompleteAction extends AbstractAction { protected final Supplier matcher; diff --git a/source/net/filebot/util/PreferencesMap.java b/source/net/filebot/util/PreferencesMap.java index 50c0b431..3c0ab776 100644 --- a/source/net/filebot/util/PreferencesMap.java +++ b/source/net/filebot/util/PreferencesMap.java @@ -2,12 +2,6 @@ package net.filebot.util; 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.util.ArrayList; import java.util.Arrays; @@ -20,6 +14,9 @@ import java.util.logging.Level; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; +import com.cedarsoftware.util.io.JsonReader; +import com.cedarsoftware.util.io.JsonWriter; + public class PreferencesMap implements Map { private final Preferences prefs; @@ -220,40 +217,32 @@ public class PreferencesMap implements Map { } - public static class SerializableAdapter extends AbstractAdapter { + public static class JsonAdapter extends AbstractAdapter { + + private Class type; + + public JsonAdapter(Class type) { + this.type = type; + } - @SuppressWarnings("unchecked") @Override public T get(Preferences prefs, String key) { - byte[] bytes = prefs.getByteArray(key, null); + String json = prefs.get(key, null); - if (bytes == null) - return null; - - try { - ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes)); - Object object = in.readObject(); - in.close(); - - return (T) object; - } catch (Exception e) { - throw new RuntimeException(e); + if (json != null) { + try { + return type.cast(JsonReader.jsonToJava(json)); + } catch (Exception e) { + debug.log(Level.WARNING, e, e::getMessage); + } } + + return null; } @Override public void put(Preferences prefs, String key, T value) { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - - try { - ObjectOutputStream out = new ObjectOutputStream(buffer); - out.writeObject(value); - out.close(); - - prefs.putByteArray(key, buffer.toByteArray()); - } catch (IOException e) { - throw new RuntimeException(e); - } + prefs.put(key, JsonWriter.objectToJson(value)); } } diff --git a/source/net/filebot/util/ui/SwingUI.java b/source/net/filebot/util/ui/SwingUI.java index 51e12abc..765f7d6e 100644 --- a/source/net/filebot/util/ui/SwingUI.java +++ b/source/net/filebot/util/ui/SwingUI.java @@ -139,20 +139,24 @@ public final class SwingUI { } 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); if (key == null) { 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); // 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) { KeyStroke macKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, keystroke.getModifiers(), keystroke.isOnKeyRelease()); 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); } } diff --git a/test/net/filebot/util/PreferencesMapTest.java b/test/net/filebot/util/PreferencesMapTest.java index 3aaeff70..257e47ed 100644 --- a/test/net/filebot/util/PreferencesMapTest.java +++ b/test/net/filebot/util/PreferencesMapTest.java @@ -14,7 +14,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import net.filebot.util.PreferencesMap.SerializableAdapter; +import net.filebot.util.PreferencesMap.JsonAdapter; import net.filebot.util.PreferencesMap.SimpleAdapter; public class PreferencesMapTest { @@ -144,11 +144,12 @@ public class PreferencesMapTest { } @Test - public void serializableAdapter() { - Map map = PreferencesMap.map(temp, new SerializableAdapter()); + public void jsonAdapter() { + Map map = PreferencesMap.map(temp, new JsonAdapter(Color.class)); Color color = new Color(0.25f, 0.50f, 1.00f); map.put("color", color); assertEquals(color, map.get("color")); } + }