diff options
| author | Vadim Shpakovski | 2012-07-06 16:40:00 +0300 |
|---|---|---|
| committer | Vadim Shpakovski | 2012-07-06 16:40:00 +0300 |
| commit | 7eb4408ce34dd831ef279c182672976c8aa3f605 (patch) | |
| tree | 03f5c4f547eda26b6c3a860c8083beac4982437b | |
| parent | 27a5c52eda2624ad89a6f9f66925cffa345160be (diff) | |
| download | MASShortcut-7eb4408ce34dd831ef279c182672976c8aa3f605.tar.bz2 | |
Adds super-easy API for associated preference key.
| -rw-r--r-- | MASShortcutView+UserDefaults.h | 7 | ||||
| -rw-r--r-- | MASShortcutView+UserDefaults.m | 115 | ||||
| -rw-r--r-- | MASShortcutView.m | 41 |
3 files changed, 143 insertions, 20 deletions
diff --git a/MASShortcutView+UserDefaults.h b/MASShortcutView+UserDefaults.h new file mode 100644 index 0000000..05d3c5b --- /dev/null +++ b/MASShortcutView+UserDefaults.h @@ -0,0 +1,7 @@ +#import "MASShortcutView.h" + +@interface MASShortcutView (UserDefaults) + +@property (nonatomic, copy) NSString *associatedUserDefaultsKey; + +@end diff --git a/MASShortcutView+UserDefaults.m b/MASShortcutView+UserDefaults.m new file mode 100644 index 0000000..133453e --- /dev/null +++ b/MASShortcutView+UserDefaults.m @@ -0,0 +1,115 @@ +#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 *kDefaultsObserver = &kDefaultsObserver; + +- (NSString *)associatedUserDefaultsKey +{ + MASShortcutDefaultsObserver *defaultsObserver = objc_getAssociatedObject(self, kDefaultsObserver); + return defaultsObserver.userDefaultsKey; +} + +- (void)setAssociatedUserDefaultsKey:(NSString *)associatedUserDefaultsKey +{ + MASShortcutDefaultsObserver *defaultsObserver = [[MASShortcutDefaultsObserver alloc] initWithShortcutView:self userDefaultsKey:associatedUserDefaultsKey]; + objc_setAssociatedObject(self, kDefaultsObserver, 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 standardUserDefaults] setObject:shortcut.data forKey:_userDefaultsKey]; + _internalPreferenceChange = NO; + } + else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + +@end diff --git a/MASShortcutView.m b/MASShortcutView.m index b31ceb0..48dde31 100644 --- a/MASShortcutView.m +++ b/MASShortcutView.m @@ -62,7 +62,7 @@ static MASShortcutView *currentRecorder = nil; if (flag && (currentRecorder != self)) { currentRecorder.recording = NO; - currentRecorder = self; + currentRecorder = flag ? self : nil; } // Only enabled view supports recording @@ -117,10 +117,10 @@ [self getShortcutRect:&shortcutRect hintRect:NULL]; NSString *title = (self.recording ? (_hinting - ? NSLocalizedString(@"Use old shortuct", @"Cancel action button for non-empty shortcut in recording state") + ? NSLocalizedString(@"Use Old Shortuct", @"Cancel action button for non-empty shortcut in recording state") : (self.shortcutPlaceholder.length > 0 ? self.shortcutPlaceholder - : NSLocalizedString(@"Type new shortcut", @"Non-empty shortcut button in recording state"))) + : NSLocalizedString(@"Type New Shortcut", @"Non-empty shortcut button in recording state"))) : _shortcutValue ? _shortcutValue.description : @""); [self drawInRect:shortcutRect withTitle:title alignment:NSCenterTextAlignment state:self.isRecording ? NSOnState : NSOffState]; } @@ -132,15 +132,15 @@ CGRect shortcutRect; [self getShortcutRect:&shortcutRect hintRect:NULL]; NSString *title = (_hinting - ? NSLocalizedString(@"Click to cancel", @"Cancel action button in recording state") + ? NSLocalizedString(@"Cancel", @"Cancel action button in recording state") : (self.shortcutPlaceholder.length > 0 ? self.shortcutPlaceholder - : NSLocalizedString(@"Type shortcut", @"Empty shortcut button in recording state"))); + : NSLocalizedString(@"Type Shortcut", @"Empty shortcut button in recording state"))); [self drawInRect:shortcutRect withTitle:title alignment:NSCenterTextAlignment state:NSOnState]; } else { - [self drawInRect:self.bounds withTitle:NSLocalizedString(@"Click to record", @"Empty shortcut button in normal state") + [self drawInRect:self.bounds withTitle:NSLocalizedString(@"Record Shortcut", @"Empty shortcut button in normal state") alignment:NSCenterTextAlignment state:NSOffState]; } } @@ -216,7 +216,7 @@ } // Forbid hinting if view is disabled - if (self.enabled) return; + if (!self.enabled) return; CGRect hintRect; [self getShortcutRect:NULL hintRect:&hintRect]; @@ -256,7 +256,7 @@ void *kUserDataHint = &kUserDataHint; } if ((self.shortcutValue == nil) || self.recording || !self.enabled) return; - + CGRect shortcutRect, hintRect; [self getShortcutRect:&shortcutRect hintRect:&hintRect]; _shortcutToolTipTag = [self addToolTipRect:shortcutRect owner:self userData:kUserDataShortcut]; @@ -284,23 +284,24 @@ void *kUserDataHint = &kUserDataHint; static id eventMonitor = nil; if (shouldActivate) { + __weak MASShortcutView *weakSelf = self; eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *event) { MASShortcut *shortcut = [MASShortcut shortcutWithEvent:event]; if ((shortcut.keyCode == kVK_Delete) || (shortcut.keyCode == kVK_ForwardDelete)) { // Delete shortcut - self.shortcutValue = nil; - self.recording = NO; + weakSelf.shortcutValue = nil; + weakSelf.recording = NO; event = nil; } else if (shortcut.keyCode == kVK_Escape) { // Cancel recording - self.recording = NO; + weakSelf.recording = NO; event = nil; } else if (shortcut.shouldBypass) { // Command + W, Command + Q, ESC should deactivate recorder - self.recording = NO; + weakSelf.recording = NO; } else { // Verify possible shortcut @@ -310,20 +311,20 @@ void *kUserDataHint = &kUserDataHint; NSError *error = nil; if ([shortcut isTakenError:&error]) { // Prevent cancel of recording when Alert window is key - [self activateResignObserver:NO]; - [self activateEventMonitoring:NO]; + [weakSelf activateResignObserver:NO]; + [weakSelf activateEventMonitoring:NO]; NSString *format = NSLocalizedString(@"The key combination %@ cannot be used", @"Title for alert when shortcut is already used"); NSRunCriticalAlertPanel([NSString stringWithFormat:format, shortcut], error.localizedDescription, NSLocalizedString(@"OK", @"Alert button when shortcut is already used"), nil, nil); - self.shortcutPlaceholder = nil; - [self activateResignObserver:YES]; - [self activateEventMonitoring:YES]; + weakSelf.shortcutPlaceholder = nil; + [weakSelf activateResignObserver:YES]; + [weakSelf activateEventMonitoring:YES]; } else { - self.shortcutValue = shortcut; - self.recording = NO; + weakSelf.shortcutValue = shortcut; + weakSelf.recording = NO; } } else { @@ -333,7 +334,7 @@ void *kUserDataHint = &kUserDataHint; } else { // User is playing with modifier keys - self.shortcutPlaceholder = shortcut.modifierFlagsString; + weakSelf.shortcutPlaceholder = shortcut.modifierFlagsString; } event = nil; } |
