aboutsummaryrefslogtreecommitdiffstats
path: root/Framework/MASShortcutView+UserDefaults.m
diff options
context:
space:
mode:
Diffstat (limited to 'Framework/MASShortcutView+UserDefaults.m')
-rw-r--r--Framework/MASShortcutView+UserDefaults.m125
1 files changed, 125 insertions, 0 deletions
diff --git a/Framework/MASShortcutView+UserDefaults.m b/Framework/MASShortcutView+UserDefaults.m
new file mode 100644
index 0000000..a84f0c9
--- /dev/null
+++ b/Framework/MASShortcutView+UserDefaults.m
@@ -0,0 +1,125 @@
+#import "MASShortcutView+UserDefaults.h"
+#import "MASShortcut.h"
+#import <objc/runtime.h>
+
+@interface MASShortcutDefaultsObserver : NSObject
+
+@property (nonatomic, readonly) NSString *userDefaultsKey;
+@property (nonatomic, readonly, weak) MASShortcutView *shortcutView;
+
+- (id)initWithShortcutView:(MASShortcutView *)shortcutView userDefaultsKey:(NSString *)userDefaultsKey;
+
+@end
+
+#pragma mark -
+
+@implementation MASShortcutView (UserDefaults)
+
+void *MASAssociatedDefaultsObserver = &MASAssociatedDefaultsObserver;
+
+- (NSString *)associatedUserDefaultsKey
+{
+ MASShortcutDefaultsObserver *defaultsObserver = objc_getAssociatedObject(self, MASAssociatedDefaultsObserver);
+ return defaultsObserver.userDefaultsKey;
+}
+
+- (void)setAssociatedUserDefaultsKey:(NSString *)associatedUserDefaultsKey
+{
+ // First, stop observing previous shortcut view
+ objc_setAssociatedObject(self, MASAssociatedDefaultsObserver, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ if (associatedUserDefaultsKey.length == 0) return;
+
+ // Next, start observing current shortcut view
+ MASShortcutDefaultsObserver *defaultsObserver = [[MASShortcutDefaultsObserver alloc] initWithShortcutView:self userDefaultsKey:associatedUserDefaultsKey];
+ objc_setAssociatedObject(self, MASAssociatedDefaultsObserver, defaultsObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
+
+#pragma mark -
+
+@implementation MASShortcutDefaultsObserver {
+ MASShortcut *_originalShortcut;
+ BOOL _internalPreferenceChange;
+ BOOL _internalShortcutChange;
+}
+
+- (id)initWithShortcutView:(MASShortcutView *)shortcutView userDefaultsKey:(NSString *)userDefaultsKey
+{
+ self = [super init];
+ if (self) {
+ _originalShortcut = shortcutView.shortcutValue;
+ _shortcutView = shortcutView;
+ _userDefaultsKey = userDefaultsKey.copy;
+ [self startObservingShortcutView];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ // __weak _shortcutView is not yet deallocated because it refers MASShortcutDefaultsObserver
+ [self stopObservingShortcutView];
+}
+
+#pragma mark -
+
+void *kShortcutValueObserver = &kShortcutValueObserver;
+
+- (void)startObservingShortcutView
+{
+ // Read initial shortcut value from user preferences
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSData *data = [defaults dataForKey:_userDefaultsKey];
+ _shortcutView.shortcutValue = [MASShortcut shortcutWithData:data];
+
+ // Observe user preferences to update shortcut value when it changed
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:defaults];
+
+ // Observe the keyboard shortcut that user inputs by hand
+ [_shortcutView addObserver:self forKeyPath:@"shortcutValue" options:0 context:kShortcutValueObserver];
+}
+
+- (void)userDefaultsDidChange:(NSNotification *)note
+{
+ // Ignore notifications posted from -[self observeValueForKeyPath:]
+ if (_internalPreferenceChange) return;
+
+ _internalShortcutChange = YES;
+ NSData *data = [note.object dataForKey:_userDefaultsKey];
+ _shortcutView.shortcutValue = [MASShortcut shortcutWithData:data];
+ _internalShortcutChange = NO;
+}
+
+- (void)stopObservingShortcutView
+{
+ // Stop observing keyboard hotkeys entered by user in the shortcut view
+ [_shortcutView removeObserver:self forKeyPath:@"shortcutValue" context:kShortcutValueObserver];
+
+ // Stop observing user preferences
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSUserDefaultsDidChangeNotification object:[NSUserDefaults standardUserDefaults]];
+
+ // Restore original hotkey in the shortcut view
+ _shortcutView.shortcutValue = _originalShortcut;
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ if (context == kShortcutValueObserver) {
+ if (_internalShortcutChange) return;
+ MASShortcut *shortcut = [object valueForKey:keyPath];
+ _internalPreferenceChange = YES;
+
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ [defaults setObject:(shortcut.data ?: [NSKeyedArchiver archivedDataWithRootObject:nil]) forKey:_userDefaultsKey];
+ [defaults synchronize];
+
+ _internalPreferenceChange = NO;
+ }
+ else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ }
+}
+
+@end