aboutsummaryrefslogtreecommitdiffstats
path: root/Framework/MASShortcutBinder.m
diff options
context:
space:
mode:
authorTomáš Znamenáček2014-08-06 18:05:43 +0200
committerTomáš Znamenáček2015-01-07 15:39:39 +0100
commit444d1bccb9770738fa4ea40383c23f44a55089c2 (patch)
treea4f06e82263b751685e51ee3e1479fd9eebb1d16 /Framework/MASShortcutBinder.m
parentd8efb0755ab99ef60411f77fa4b635f466973d0f (diff)
downloadMASShortcut-444d1bccb9770738fa4ea40383c23f44a55089c2.tar.bz2
Refactored the shortcut dispatcher and bindings to user defaults.
This is a big change that was hard to split into smaller commits. There’s now a new class to bind shortcuts to actions, a new class to bind user defaults’ keys to actions, and a new way to associate user defaults with the recorder control (MASShortcutView). I have also updated the demo app to go with the changes. The new class to associate shortcuts with actions is called MASShortcutMonitor. It wraps the Carbon hotkey magic and offers a simple interface to add a shortcut along with a block that should be run when the shortcut is pressed. It’s the lowest-level interface. Since the usual requirement is to store the shortcuts into user defaults, there’s also a higher-level interface offered by the MASShortcutBinder class. That takes a defaults key and associates it with a block. When the shortcut stored under the defaults key changes, the binder automatically switches to the new shortcut. The class is a wrapper built atop of the previous one, the MASShortcutMonitor – it simply adds, updates and removes shortcuts as the user defaults change. I have removed the special user defaults integration code from the recorder control (MASShortcutView) and replaced it with a small Cocoa Bindings shim. This means that in order to keep the recorder control in sync with the defaults you just have to call the usual bind:toObject:withKeyPath:options: method, like this: [_shortcutView bind:MASShortcutBinding toObject:[NSUserDefaultsController sharedUserDefaultsController] withKeyPath[@"values.ExampleDefaultsKey" options:@{NSValueTransformerNameBindingOption:NSKeyedUnarchiveFromDataTransformerName}]; That’s more verbose than the previous solution, but it’s much cleaner and can be swept under a convenience call if needed. I might also add a dictionaryValue property later that would make it possible to bind the value to user defaults directly, without a transformer, and would enable backward compatibility with Shortcut Recorder.
Diffstat (limited to 'Framework/MASShortcutBinder.m')
-rw-r--r--Framework/MASShortcutBinder.m83
1 files changed, 83 insertions, 0 deletions
diff --git a/Framework/MASShortcutBinder.m b/Framework/MASShortcutBinder.m
new file mode 100644
index 0000000..d6249a8
--- /dev/null
+++ b/Framework/MASShortcutBinder.m
@@ -0,0 +1,83 @@
+#import "MASShortcutBinder.h"
+#import "MASShortcut.h"
+
+@interface MASShortcutBinder ()
+@property(strong) NSMutableDictionary *actions;
+@property(strong) NSMutableDictionary *shortcuts;
+@end
+
+@implementation MASShortcutBinder
+
+#pragma mark Initialization
+
+- (id) init
+{
+ self = [super init];
+ [self setActions:[NSMutableDictionary dictionary]];
+ [self setShortcuts:[NSMutableDictionary dictionary]];
+ return self;
+}
+
+- (void) dealloc
+{
+ for (NSString *bindingName in [_actions allKeys]) {
+ [self unbind:bindingName];
+ }
+}
+
+#pragma mark Bindings
+
+- (void) bindShortcutWithDefaultsKey: (NSString*) defaultsKeyName toAction: (dispatch_block_t) action
+{
+ [_actions setObject:[action copy] forKey:defaultsKeyName];
+ NSDictionary *bindingOptions = @{NSValueTransformerNameBindingOption: NSKeyedUnarchiveFromDataTransformerName};
+ [self bind:defaultsKeyName toObject:[NSUserDefaultsController sharedUserDefaultsController]
+ withKeyPath:[@"values." stringByAppendingString:defaultsKeyName] options:bindingOptions];
+}
+
+- (void) breakBindingWithDefaultsKey: (NSString*) defaultsKeyName
+{
+ [_shortcutMonitor unregisterShortcut:[_shortcuts objectForKey:defaultsKeyName]];
+ [_shortcuts removeObjectForKey:defaultsKeyName];
+ [_actions removeObjectForKey:defaultsKeyName];
+ [self unbind:defaultsKeyName];
+}
+
+- (BOOL) isRegisteredAction: (NSString*) name
+{
+ return !![_actions objectForKey:name];
+}
+
+- (id) valueForUndefinedKey: (NSString*) key
+{
+ return [self isRegisteredAction:key] ?
+ [_shortcuts objectForKey:key] :
+ [super valueForUndefinedKey:key];
+}
+
+- (void) setValue: (id) value forUndefinedKey: (NSString*) key
+{
+ if (![self isRegisteredAction:key]) {
+ [super setValue:value forUndefinedKey:key];
+ return;
+ }
+
+ MASShortcut *newShortcut = value;
+ MASShortcut *currentShortcut = [_shortcuts objectForKey:key];
+
+ // Unbind previous shortcut if any
+ if (currentShortcut != nil) {
+ [_shortcutMonitor unregisterShortcut:currentShortcut];
+ }
+
+ // Just deleting the old shortcut
+ if (newShortcut == nil) {
+ return;
+ }
+
+ // Bind new shortcut
+ [_shortcuts setObject:newShortcut forKey:key];
+ [_shortcutMonitor registerShortcut:newShortcut withAction:[_actions objectForKey:key]];
+}
+
+@end