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;              } | 
