diff options
| -rw-r--r-- | Demo/AppDelegate.m | 38 | ||||
| -rw-r--r-- | Demo/Prefix.pch | 5 | ||||
| -rw-r--r-- | Framework/MASHotKey.h | 12 | ||||
| -rw-r--r-- | Framework/MASHotKey.m | 44 | ||||
| -rw-r--r-- | Framework/MASShortcut+Monitoring.h | 8 | ||||
| -rw-r--r-- | Framework/MASShortcut+Monitoring.m | 165 | ||||
| -rw-r--r-- | Framework/MASShortcut+UserDefaults.h | 9 | ||||
| -rw-r--r-- | Framework/MASShortcut+UserDefaults.m | 98 | ||||
| -rw-r--r-- | Framework/MASShortcutBinder.h | 10 | ||||
| -rw-r--r-- | Framework/MASShortcutBinder.m | 83 | ||||
| -rw-r--r-- | Framework/MASShortcutBinderTests.m | 82 | ||||
| -rw-r--r-- | Framework/MASShortcutMonitor.h | 9 | ||||
| -rw-r--r-- | Framework/MASShortcutMonitor.m | 84 | ||||
| -rw-r--r-- | Framework/MASShortcutView+UserDefaults.h | 7 | ||||
| -rw-r--r-- | Framework/MASShortcutView+UserDefaults.m | 125 | ||||
| -rw-r--r-- | Framework/MASShortcutView.h | 2 | ||||
| -rw-r--r-- | Framework/MASShortcutView.m | 50 | ||||
| -rw-r--r-- | Framework/Prefix.pch | 1 | ||||
| -rw-r--r-- | Framework/Shortcut.h | 7 | ||||
| -rw-r--r-- | MASShortcut.xcodeproj/project.pbxproj | 60 |
20 files changed, 431 insertions, 468 deletions
diff --git a/Demo/AppDelegate.m b/Demo/AppDelegate.m index 54b64e9..d2c07ba 100644 --- a/Demo/AppDelegate.m +++ b/Demo/AppDelegate.m @@ -1,43 +1,37 @@ #import "AppDelegate.h" -#import <MASShortcut/Shortcut.h> NSString *const MASPreferenceKeyShortcut = @"MASDemoShortcut"; NSString *const MASPreferenceKeyShortcutEnabled = @"MASDemoShortcutEnabled"; NSString *const MASPreferenceKeyConstantShortcutEnabled = @"MASDemoConstantShortcutEnabled"; @implementation AppDelegate { - __weak id _constantShortcutMonitor; + MASShortcutMonitor *_shortcutMonitor; + MASShortcutBinder *_shortcutBinder; } -@synthesize window = _window; -@synthesize shortcutView = _shortcutView; - #pragma mark - - (void)awakeFromNib { [super awakeFromNib]; + _shortcutBinder = [[MASShortcutBinder alloc] init]; + _shortcutMonitor = [[MASShortcutMonitor alloc] init]; + [_shortcutBinder setShortcutMonitor:_shortcutMonitor]; + // Checkbox will enable and disable the shortcut view [self.shortcutView bind:@"enabled" toObject:self withKeyPath:@"shortcutEnabled" options:nil]; } -- (void)dealloc -{ - // Cleanup - [self.shortcutView unbind:@"enabled"]; -} - -#pragma mark - NSApplicationDelegate +#pragma mark NSApplicationDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - // Uncomment the following lines to make Command-Shift-D the default shortcut -// MASShortcut *defaultShortcut = [MASShortcut shortcutWithKeyCode:0x2 modifierFlags:NSCommandKeyMask|NSShiftKeyMask]; -// [MASShortcut setGlobalShortcut:defaultShortcut forUserDefaultsKey:MASPreferenceKeyShortcut]; - // Shortcut view will follow and modify user preferences automatically - self.shortcutView.associatedUserDefaultsKey = MASPreferenceKeyShortcut; + [_shortcutView bind:MASShortcutBinding + toObject:[NSUserDefaultsController sharedUserDefaultsController] + withKeyPath:[@"values." stringByAppendingString:MASPreferenceKeyShortcut] + options:@{NSValueTransformerNameBindingOption:NSKeyedUnarchiveFromDataTransformerName}]; // Activate the global keyboard shortcut if it was enabled last time [self resetShortcutRegistration]; @@ -69,14 +63,14 @@ NSString *const MASPreferenceKeyConstantShortcutEnabled = @"MASDemoConstantShort - (void)resetShortcutRegistration { if (self.shortcutEnabled) { - [MASShortcut registerGlobalShortcutWithUserDefaultsKey:MASPreferenceKeyShortcut handler:^{ + [_shortcutBinder bindShortcutWithDefaultsKey:MASPreferenceKeyShortcut toAction:^{ [[NSAlert alertWithMessageText:NSLocalizedString(@"Global hotkey has been pressed.", @"Alert message for custom shortcut") defaultButton:NSLocalizedString(@"OK", @"Default button for the alert on custom shortcut") alternateButton:nil otherButton:nil informativeTextWithFormat:@""] runModal]; }]; } else { - [MASShortcut unregisterGlobalShortcutWithUserDefaultsKey:MASPreferenceKeyShortcut]; + [_shortcutBinder breakBindingWithDefaultsKey:MASPreferenceKeyShortcut]; } } @@ -97,16 +91,16 @@ NSString *const MASPreferenceKeyConstantShortcutEnabled = @"MASDemoConstantShort - (void)resetConstantShortcutRegistration { + MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:kVK_F2 modifierFlags:NSCommandKeyMask]; if (self.constantShortcutEnabled) { - MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:kVK_F2 modifierFlags:NSCommandKeyMask]; - _constantShortcutMonitor = [MASShortcut addGlobalHotkeyMonitorWithShortcut:shortcut handler:^{ + [_shortcutMonitor registerShortcut:shortcut withAction:^{ [[NSAlert alertWithMessageText:NSLocalizedString(@"⌘F2 has been pressed.", @"Alert message for constant shortcut") defaultButton:NSLocalizedString(@"OK", @"Default button for the alert on constant shortcut") alternateButton:nil otherButton:nil informativeTextWithFormat:@""] runModal]; }]; } else { - [MASShortcut removeGlobalHotkeyMonitor:_constantShortcutMonitor]; + [_shortcutMonitor unregisterShortcut:shortcut]; } } diff --git a/Demo/Prefix.pch b/Demo/Prefix.pch index aabef47..20d47b6 100644 --- a/Demo/Prefix.pch +++ b/Demo/Prefix.pch @@ -1,3 +1,2 @@ -#ifdef __OBJC__ - #import <Cocoa/Cocoa.h> -#endif +#import <Cocoa/Cocoa.h> +#import <MASShortcut/Shortcut.h> diff --git a/Framework/MASHotKey.h b/Framework/MASHotKey.h new file mode 100644 index 0000000..1d267e4 --- /dev/null +++ b/Framework/MASHotKey.h @@ -0,0 +1,12 @@ +#import "MASShortcut.h" + +extern FourCharCode const MASHotKeySignature; + +@interface MASHotKey : NSObject + +@property(readonly) UInt32 carbonID; +@property(copy) dispatch_block_t action; + ++ (instancetype) registeredHotKeyWithShortcut: (MASShortcut*) shortcut; + +@end diff --git a/Framework/MASHotKey.m b/Framework/MASHotKey.m new file mode 100644 index 0000000..7886440 --- /dev/null +++ b/Framework/MASHotKey.m @@ -0,0 +1,44 @@ +#import "MASHotKey.h" + +FourCharCode const MASHotKeySignature = 'MASS'; + +@interface MASHotKey () +@property(assign) EventHotKeyRef hotKeyRef; +@property(assign) UInt32 carbonID; +@end + +@implementation MASHotKey + +- (instancetype) initWithShortcut: (MASShortcut*) shortcut +{ + self = [super init]; + + static UInt32 CarbonHotKeyID = 0; + + _carbonID = ++CarbonHotKeyID; + EventHotKeyID hotKeyID = { .signature = MASHotKeySignature, .id = _carbonID }; + + OSStatus status = RegisterEventHotKey([shortcut carbonKeyCode], [shortcut carbonFlags], + hotKeyID, GetEventDispatcherTarget(), kEventHotKeyExclusive, &_hotKeyRef); + + if (status != noErr) { + return nil; + } + + return self; +} + ++ (instancetype) registeredHotKeyWithShortcut: (MASShortcut*) shortcut +{ + return [[self alloc] initWithShortcut:shortcut]; +} + +- (void) dealloc +{ + if (_hotKeyRef) { + UnregisterEventHotKey(_hotKeyRef); + _hotKeyRef = NULL; + } +} + +@end diff --git a/Framework/MASShortcut+Monitoring.h b/Framework/MASShortcut+Monitoring.h deleted file mode 100644 index aa8f224..0000000 --- a/Framework/MASShortcut+Monitoring.h +++ /dev/null @@ -1,8 +0,0 @@ -#import "MASShortcut.h" - -@interface MASShortcut (Monitoring) - -+ (id)addGlobalHotkeyMonitorWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler; -+ (void)removeGlobalHotkeyMonitor:(id)monitor; - -@end diff --git a/Framework/MASShortcut+Monitoring.m b/Framework/MASShortcut+Monitoring.m deleted file mode 100644 index b6750b4..0000000 --- a/Framework/MASShortcut+Monitoring.m +++ /dev/null @@ -1,165 +0,0 @@ -#import "MASShortcut+Monitoring.h" - -NSMutableDictionary *MASRegisteredHotKeys(void); -BOOL InstallCommonEventHandler(void); -BOOL InstallHotkeyWithShortcut(MASShortcut *shortcut, UInt32 *outCarbonHotKeyID, EventHotKeyRef *outCarbonHotKey); -void UninstallEventHandler(void); - -#pragma mark - - -@interface MASShortcutHotKey : NSObject - -@property (nonatomic, readonly) MASShortcut *shortcut; -@property (nonatomic, readonly, copy) void (^handler)(); -@property (nonatomic, readonly) EventHotKeyRef carbonHotKey; -@property (nonatomic, readonly) UInt32 carbonHotKeyID; - -- (id)initWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler; - -@end - -#pragma mark - - -@implementation MASShortcut (Monitoring) - -+ (id)addGlobalHotkeyMonitorWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler -{ - NSString *monitor = [NSString stringWithFormat:@"%@", shortcut.description]; - if ([MASRegisteredHotKeys() objectForKey:monitor]) return nil; - - MASShortcutHotKey *hotKey = [[MASShortcutHotKey alloc] initWithShortcut:shortcut handler:handler]; - if (hotKey == nil) return nil; - - [MASRegisteredHotKeys() setObject:hotKey forKey:monitor]; - return monitor; -} - -+ (void)removeGlobalHotkeyMonitor:(id)monitor -{ - if (monitor == nil) return; - NSMutableDictionary *registeredHotKeys = MASRegisteredHotKeys(); - [registeredHotKeys removeObjectForKey:monitor]; - if (registeredHotKeys.count == 0) { - UninstallEventHandler(); - } -} - -@end - -#pragma mark - - -@implementation MASShortcutHotKey - -@synthesize carbonHotKeyID = _carbonHotKeyID; -@synthesize handler = _handler; -@synthesize shortcut = _shortcut; -@synthesize carbonHotKey = _carbonHotKey; - -#pragma mark - - -- (id)initWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler -{ - self = [super init]; - if (self) { - _shortcut = shortcut; - _handler = [handler copy]; - - if (!InstallHotkeyWithShortcut(shortcut, &_carbonHotKeyID, &_carbonHotKey)) - self = nil; - } - return self; -} - -- (void)dealloc -{ - [self uninstallExisitingHotKey]; -} - -- (void)uninstallExisitingHotKey -{ - if (_carbonHotKey) { - UnregisterEventHotKey(_carbonHotKey); - _carbonHotKey = NULL; - } -} - -@end - -#pragma mark - Carbon magic - -NSMutableDictionary *MASRegisteredHotKeys() -{ - static NSMutableDictionary *shared = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - shared = [NSMutableDictionary dictionary]; - }); - return shared; -} - -#pragma mark - - -FourCharCode const kMASShortcutSignature = 'MASS'; - -BOOL InstallHotkeyWithShortcut(MASShortcut *shortcut, UInt32 *outCarbonHotKeyID, EventHotKeyRef *outCarbonHotKey) -{ - if ((shortcut == nil) || !InstallCommonEventHandler()) return NO; - - static UInt32 sCarbonHotKeyID = 0; - EventHotKeyID hotKeyID = { .signature = kMASShortcutSignature, .id = ++ sCarbonHotKeyID }; - EventHotKeyRef carbonHotKey = NULL; - if (RegisterEventHotKey(shortcut.carbonKeyCode, shortcut.carbonFlags, hotKeyID, GetEventDispatcherTarget(), kEventHotKeyExclusive, &carbonHotKey) != noErr) { - return NO; - } - - if (outCarbonHotKeyID) *outCarbonHotKeyID = hotKeyID.id; - if (outCarbonHotKey) *outCarbonHotKey = carbonHotKey; - return YES; -} - -static OSStatus CarbonCallback(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) -{ - if (GetEventClass(inEvent) != kEventClassKeyboard) return noErr; - - EventHotKeyID hotKeyID; - OSStatus status = GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID), NULL, &hotKeyID); - if (status != noErr) return status; - - if (hotKeyID.signature != kMASShortcutSignature) return noErr; - - [MASRegisteredHotKeys() enumerateKeysAndObjectsUsingBlock:^(id key, MASShortcutHotKey *hotKey, BOOL *stop) { - if (hotKeyID.id == hotKey.carbonHotKeyID) { - if (hotKey.handler) { - hotKey.handler(); - } - *stop = YES; - } - }]; - - return noErr; -} - -#pragma mark - - -static EventHandlerRef sEventHandler = NULL; - -BOOL InstallCommonEventHandler() -{ - if (sEventHandler == NULL) { - EventTypeSpec hotKeyPressedSpec = { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed }; - OSStatus status = InstallEventHandler(GetEventDispatcherTarget(), CarbonCallback, 1, &hotKeyPressedSpec, NULL, &sEventHandler); - if (status != noErr) { - sEventHandler = NULL; - return NO; - } - } - return YES; -} - -void UninstallEventHandler() -{ - if (sEventHandler) { - RemoveEventHandler(sEventHandler); - sEventHandler = NULL; - } -} diff --git a/Framework/MASShortcut+UserDefaults.h b/Framework/MASShortcut+UserDefaults.h deleted file mode 100644 index 9f2ecb9..0000000 --- a/Framework/MASShortcut+UserDefaults.h +++ /dev/null @@ -1,9 +0,0 @@ -#import "MASShortcut.h" - -@interface MASShortcut (UserDefaults) - -+ (void)registerGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler; -+ (void)unregisterGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey; -+ (void)setGlobalShortcut:(MASShortcut *)shortcut forUserDefaultsKey:(NSString *)userDefaultsKey; - -@end diff --git a/Framework/MASShortcut+UserDefaults.m b/Framework/MASShortcut+UserDefaults.m deleted file mode 100644 index 006c579..0000000 --- a/Framework/MASShortcut+UserDefaults.m +++ /dev/null @@ -1,98 +0,0 @@ -#import "MASShortcut+UserDefaults.h" -#import "MASShortcut+Monitoring.h" - -@interface MASShortcutUserDefaultsHotKey : NSObject - -@property (nonatomic, readonly) NSString *userDefaultsKey; -@property (nonatomic, copy) void (^handler)(); -@property (nonatomic, weak) id monitor; - -- (id)initWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler; - -@end - -#pragma mark - - -@implementation MASShortcut (UserDefaults) - -+ (NSMutableDictionary *)registeredUserDefaultsHotKeys -{ - static NSMutableDictionary *shared = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - shared = [NSMutableDictionary dictionary]; - }); - return shared; -} - -+ (void)registerGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler -{ - MASShortcutUserDefaultsHotKey *hotKey = [[MASShortcutUserDefaultsHotKey alloc] initWithUserDefaultsKey:userDefaultsKey handler:handler]; - [[self registeredUserDefaultsHotKeys] setObject:hotKey forKey:userDefaultsKey]; -} - -+ (void)unregisterGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey -{ - NSMutableDictionary *registeredHotKeys = [self registeredUserDefaultsHotKeys]; - [registeredHotKeys removeObjectForKey:userDefaultsKey]; -} - -+ (void)setGlobalShortcut:(MASShortcut *)shortcut forUserDefaultsKey:(NSString *)userDefaultsKey -{ - NSData *shortcutData = [NSArchiver archivedDataWithRootObject:shortcut]; - if (shortcutData) - [[NSUserDefaults standardUserDefaults] setObject:shortcutData forKey:userDefaultsKey]; - else - [[NSUserDefaults standardUserDefaults] removeObjectForKey:userDefaultsKey]; -} - -@end - -#pragma mark - - -@implementation MASShortcutUserDefaultsHotKey { - NSString *_observableKeyPath; -} - -void *MASShortcutUserDefaultsContext = &MASShortcutUserDefaultsContext; - -- (id)initWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler -{ - self = [super init]; - if (self) { - _userDefaultsKey = userDefaultsKey.copy; - _handler = [handler copy]; - _observableKeyPath = [@"values." stringByAppendingString:_userDefaultsKey]; - [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:_observableKeyPath options:NSKeyValueObservingOptionInitial context:MASShortcutUserDefaultsContext]; - } - return self; -} - -- (void)dealloc -{ - [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:_observableKeyPath context:MASShortcutUserDefaultsContext]; - [MASShortcut removeGlobalHotkeyMonitor:self.monitor]; -} - -#pragma mark - - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - if (context == MASShortcutUserDefaultsContext) { - [MASShortcut removeGlobalHotkeyMonitor:self.monitor]; - [self installHotKeyFromUserDefaults]; - } - else { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - -- (void)installHotKeyFromUserDefaults -{ - NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:_userDefaultsKey]; - MASShortcut *shortcut = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - if (shortcut == nil) return; - self.monitor = [MASShortcut addGlobalHotkeyMonitorWithShortcut:shortcut handler:self.handler]; -} - -@end diff --git a/Framework/MASShortcutBinder.h b/Framework/MASShortcutBinder.h new file mode 100644 index 0000000..74842f3 --- /dev/null +++ b/Framework/MASShortcutBinder.h @@ -0,0 +1,10 @@ +#import "MASShortcutMonitor.h" + +@interface MASShortcutBinder : NSObject + +@property(strong) MASShortcutMonitor *shortcutMonitor; + +- (void) bindShortcutWithDefaultsKey: (NSString*) defaultsKeyName toAction: (dispatch_block_t) action; +- (void) breakBindingWithDefaultsKey: (NSString*) defaultsKeyName; + +@end 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 diff --git a/Framework/MASShortcutBinderTests.m b/Framework/MASShortcutBinderTests.m new file mode 100644 index 0000000..f416304 --- /dev/null +++ b/Framework/MASShortcutBinderTests.m @@ -0,0 +1,82 @@ +#import "MASShortcutBinder.h" + +static NSString *const SampleDefaultsKey = @"sampleShortcut"; + +@interface MASShortcutBinderTests : XCTestCase +@property(strong) MASShortcutBinder *binder; +@property(strong) MASShortcutMonitor *monitor; +@property(strong) NSUserDefaults *defaults; +@end + +@implementation MASShortcutBinderTests + +- (void) setUp +{ + [super setUp]; + + [self setBinder:[[MASShortcutBinder alloc] init]]; + [self setMonitor:[[MASShortcutMonitor alloc] init]]; + [_binder setShortcutMonitor:_monitor]; + + [self setDefaults:[[NSUserDefaults alloc] init]]; + [_defaults removeObjectForKey:SampleDefaultsKey]; +} + +- (void) tearDown +{ + [self setMonitor:nil]; + [self setDefaults:nil]; + [self setBinder:nil]; + [super tearDown]; +} + +- (void) testInitialValueReading +{ + MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:1 modifierFlags:1]; + [_defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:shortcut] forKey:SampleDefaultsKey]; + [_binder bindShortcutWithDefaultsKey:SampleDefaultsKey toAction:^{}]; + XCTAssertTrue([_monitor isShortcutRegistered:shortcut], + @"Pass the initial shortcut from defaults to shortcut monitor."); +} + +- (void) testValueChangeReading +{ + MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:1 modifierFlags:1]; + [_binder bindShortcutWithDefaultsKey:SampleDefaultsKey toAction:^{}]; + [_defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:shortcut] forKey:SampleDefaultsKey]; + XCTAssertTrue([_monitor isShortcutRegistered:shortcut], + @"Pass the shortcut from defaults to shortcut monitor after defaults change."); +} + +- (void) testValueClearing +{ + MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:1 modifierFlags:1]; + [_binder bindShortcutWithDefaultsKey:SampleDefaultsKey toAction:^{}]; + [_defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:shortcut] forKey:SampleDefaultsKey]; + [_defaults removeObjectForKey:SampleDefaultsKey]; + XCTAssertFalse([_monitor isShortcutRegistered:shortcut], + @"Unregister shortcut from monitor after value is cleared from defaults."); +} + +- (void) testBindingRemoval +{ + MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:1 modifierFlags:1]; + [_binder bindShortcutWithDefaultsKey:SampleDefaultsKey toAction:^{}]; + [_defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:shortcut] forKey:SampleDefaultsKey]; + [_binder breakBindingWithDefaultsKey:SampleDefaultsKey]; + XCTAssertFalse([_monitor isShortcutRegistered:shortcut], + @"Unregister shortcut from monitor after binding was removed."); +} + +- (void) testRebinding +{ + MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:1 modifierFlags:1]; + [_defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:shortcut] forKey:SampleDefaultsKey]; + [_binder bindShortcutWithDefaultsKey:SampleDefaultsKey toAction:^{}]; + [_binder breakBindingWithDefaultsKey:SampleDefaultsKey]; + [_binder bindShortcutWithDefaultsKey:SampleDefaultsKey toAction:^{}]; + XCTAssertTrue([_monitor isShortcutRegistered:shortcut], + @"Bind after unbinding."); +} + +@end diff --git a/Framework/MASShortcutMonitor.h b/Framework/MASShortcutMonitor.h new file mode 100644 index 0000000..c29747c --- /dev/null +++ b/Framework/MASShortcutMonitor.h @@ -0,0 +1,9 @@ +#import "MASShortcut.h" + +@interface MASShortcutMonitor : NSObject + +- (void) registerShortcut: (MASShortcut*) shortcut withAction: (dispatch_block_t) action; +- (BOOL) isShortcutRegistered: (MASShortcut*) shortcut; +- (void) unregisterShortcut: (MASShortcut*) shortcut; + +@end diff --git a/Framework/MASShortcutMonitor.m b/Framework/MASShortcutMonitor.m new file mode 100644 index 0000000..f1c7e9c --- /dev/null +++ b/Framework/MASShortcutMonitor.m @@ -0,0 +1,84 @@ +#import "MASShortcutMonitor.h" +#import "MASHotKey.h" + +@interface MASShortcutMonitor () +@property(assign) EventHandlerRef eventHandlerRef; +@property(strong) NSMutableDictionary *hotKeys; +@end + +static OSStatus MASCarbonEventCallback(EventHandlerCallRef, EventRef, void*); + +@implementation MASShortcutMonitor + +- (instancetype) init +{ + self = [super init]; + [self setHotKeys:[NSMutableDictionary dictionary]]; + EventTypeSpec hotKeyPressedSpec = { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed }; + OSStatus status = InstallEventHandler(GetEventDispatcherTarget(), MASCarbonEventCallback, + 1, &hotKeyPressedSpec, (__bridge void*)self, &_eventHandlerRef); + if (status != noErr) { + return nil; + } + return self; +} + +- (void) dealloc +{ + if (_eventHandlerRef) { + RemoveEventHandler(_eventHandlerRef); + _eventHandlerRef = NULL; + } +} + +#pragma mark Registration + +- (void) registerShortcut: (MASShortcut*) shortcut withAction: (dispatch_block_t) action +{ + MASHotKey *hotKey = [MASHotKey registeredHotKeyWithShortcut:shortcut]; + [hotKey setAction:action]; + [_hotKeys setObject:hotKey forKey:shortcut]; +} + +- (void) unregisterShortcut: (MASShortcut*) shortcut +{ + [_hotKeys removeObjectForKey:shortcut]; +} + +- (BOOL) isShortcutRegistered: (MASShortcut*) shortcut +{ + return !![_hotKeys objectForKey:shortcut]; +} + +#pragma mark Event Handling + +- (void) handleEvent: (EventRef) event +{ + if (GetEventClass(event) != kEventClassKeyboard) { + return; + } + + EventHotKeyID hotKeyID; + OSStatus status = GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID), NULL, &hotKeyID); + if (status != noErr || hotKeyID.signature != MASHotKeySignature) { + return; + } + + [_hotKeys enumerateKeysAndObjectsUsingBlock:^(MASShortcut *shortcut, MASHotKey *hotKey, BOOL *stop) { + if (hotKeyID.id == [hotKey carbonID]) { + if ([hotKey action]) { + dispatch_async(dispatch_get_main_queue(), [hotKey action]); + } + *stop = YES; + } + }]; +} + +@end + +static OSStatus MASCarbonEventCallback(EventHandlerCallRef _, EventRef event, void *context) +{ + MASShortcutMonitor *dispatcher = (__bridge id)context; + [dispatcher handleEvent:event]; + return noErr; +} diff --git a/Framework/MASShortcutView+UserDefaults.h b/Framework/MASShortcutView+UserDefaults.h deleted file mode 100644 index 05d3c5b..0000000 --- a/Framework/MASShortcutView+UserDefaults.h +++ /dev/null @@ -1,7 +0,0 @@ -#import "MASShortcutView.h" - -@interface MASShortcutView (UserDefaults) - -@property (nonatomic, copy) NSString *associatedUserDefaultsKey; - -@end diff --git a/Framework/MASShortcutView+UserDefaults.m b/Framework/MASShortcutView+UserDefaults.m deleted file mode 100644 index 55584f4..0000000 --- a/Framework/MASShortcutView+UserDefaults.m +++ /dev/null @@ -1,125 +0,0 @@ -#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 = [NSKeyedUnarchiver unarchiveObjectWithData: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 = [NSKeyedUnarchiver unarchiveObjectWithData: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:[NSKeyedArchiver archivedDataWithRootObject:shortcut] forKey:_userDefaultsKey]; - [defaults synchronize]; - - _internalPreferenceChange = NO; - } - else { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - -@end diff --git a/Framework/MASShortcutView.h b/Framework/MASShortcutView.h index 5d788b9..8cff0ca 100644 --- a/Framework/MASShortcutView.h +++ b/Framework/MASShortcutView.h @@ -1,5 +1,7 @@ @class MASShortcut, MASShortcutValidator; +extern NSString *const MASShortcutBinding; + typedef enum { MASShortcutViewAppearanceDefault = 0, // Height = 19 px MASShortcutViewAppearanceTexturedRect, // Height = 25 px diff --git a/Framework/MASShortcutView.m b/Framework/MASShortcutView.m index 6f562a5..d03efbc 100644 --- a/Framework/MASShortcutView.m +++ b/Framework/MASShortcutView.m @@ -1,6 +1,8 @@ #import "MASShortcutView.h" #import "MASShortcutValidator.h" +NSString *const MASShortcutBinding = @"shortcutValue"; + #define HINT_BUTTON_WIDTH 23.0 #define BUTTON_FONT_SIZE 11.0 #define SEGMENT_CHROME_WIDTH 6.0 @@ -149,6 +151,7 @@ _shortcutValue = shortcutValue; [self resetToolTips]; [self setNeedsDisplay:YES]; + [self propagateValue:shortcutValue forBinding:@"shortcutValue"]; if (self.shortcutValueChange) { self.shortcutValueChange(self); @@ -462,4 +465,51 @@ void *kUserDataHint = &kUserDataHint; } } +#pragma mark Bindings + +// http://tomdalling.com/blog/cocoa/implementing-your-own-cocoa-bindings/ +-(void) propagateValue:(id)value forBinding:(NSString*)binding; +{ + NSParameterAssert(binding != nil); + + //WARNING: bindingInfo contains NSNull, so it must be accounted for + NSDictionary* bindingInfo = [self infoForBinding:binding]; + if(!bindingInfo) + return; //there is no binding + + //apply the value transformer, if one has been set + NSDictionary* bindingOptions = [bindingInfo objectForKey:NSOptionsKey]; + if(bindingOptions){ + NSValueTransformer* transformer = [bindingOptions valueForKey:NSValueTransformerBindingOption]; + if(!transformer || (id)transformer == [NSNull null]){ + NSString* transformerName = [bindingOptions valueForKey:NSValueTransformerNameBindingOption]; + if(transformerName && (id)transformerName != [NSNull null]){ + transformer = [NSValueTransformer valueTransformerForName:transformerName]; + } + } + + if(transformer && (id)transformer != [NSNull null]){ + if([[transformer class] allowsReverseTransformation]){ + value = [transformer reverseTransformedValue:value]; + } else { + NSLog(@"WARNING: binding \"%@\" has value transformer, but it doesn't allow reverse transformations in %s", binding, __PRETTY_FUNCTION__); + } + } + } + + id boundObject = [bindingInfo objectForKey:NSObservedObjectKey]; + if(!boundObject || boundObject == [NSNull null]){ + NSLog(@"ERROR: NSObservedObjectKey was nil for binding \"%@\" in %s", binding, __PRETTY_FUNCTION__); + return; + } + + NSString* boundKeyPath = [bindingInfo objectForKey:NSObservedKeyPathKey]; + if(!boundKeyPath || (id)boundKeyPath == [NSNull null]){ + NSLog(@"ERROR: NSObservedKeyPathKey was nil for binding \"%@\" in %s", binding, __PRETTY_FUNCTION__); + return; + } + + [boundObject setValue:value forKeyPath:boundKeyPath]; +} + @end diff --git a/Framework/Prefix.pch b/Framework/Prefix.pch index 7f2c544..3e71c31 100644 --- a/Framework/Prefix.pch +++ b/Framework/Prefix.pch @@ -1 +1,2 @@ #import <AppKit/AppKit.h> +#import <Carbon/Carbon.h>
\ No newline at end of file diff --git a/Framework/Shortcut.h b/Framework/Shortcut.h index 3087a51..dce07a5 100644 --- a/Framework/Shortcut.h +++ b/Framework/Shortcut.h @@ -1,4 +1,5 @@ #import "MASShortcut.h" -#import "MASShortcutView+UserDefaults.h" -#import "MASShortcut+UserDefaults.h" -#import "MASShortcut+Monitoring.h"
\ No newline at end of file +#import "MASShortcutValidator.h" +#import "MASShortcutMonitor.h" +#import "MASShortcutBinder.h" +#import "MASShortcutView.h" diff --git a/MASShortcut.xcodeproj/project.pbxproj b/MASShortcut.xcodeproj/project.pbxproj index e79cb66..f8f29e5 100644 --- a/MASShortcut.xcodeproj/project.pbxproj +++ b/MASShortcut.xcodeproj/project.pbxproj @@ -10,14 +10,8 @@ 0D827CD71990D4420010B8EF /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D827CD61990D4420010B8EF /* Cocoa.framework */; }; 0D827D251990D55E0010B8EF /* MASShortcut.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D1B1990D55E0010B8EF /* MASShortcut.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0D827D261990D55E0010B8EF /* MASShortcut.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D1C1990D55E0010B8EF /* MASShortcut.m */; }; - 0D827D271990D55E0010B8EF /* MASShortcut+Monitoring.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D1D1990D55E0010B8EF /* MASShortcut+Monitoring.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0D827D281990D55E0010B8EF /* MASShortcut+Monitoring.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D1E1990D55E0010B8EF /* MASShortcut+Monitoring.m */; }; - 0D827D291990D55E0010B8EF /* MASShortcut+UserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D1F1990D55E0010B8EF /* MASShortcut+UserDefaults.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0D827D2A1990D55E0010B8EF /* MASShortcut+UserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D201990D55E0010B8EF /* MASShortcut+UserDefaults.m */; }; 0D827D2B1990D55E0010B8EF /* MASShortcutView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D211990D55E0010B8EF /* MASShortcutView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0D827D2C1990D55E0010B8EF /* MASShortcutView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D221990D55E0010B8EF /* MASShortcutView.m */; }; - 0D827D2D1990D55E0010B8EF /* MASShortcutView+UserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D231990D55E0010B8EF /* MASShortcutView+UserDefaults.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0D827D2E1990D55E0010B8EF /* MASShortcutView+UserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D241990D55E0010B8EF /* MASShortcutView+UserDefaults.m */; }; 0D827D381990D5E70010B8EF /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D827CD61990D4420010B8EF /* Cocoa.framework */; }; 0D827D6F1990D6110010B8EF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D6A1990D6110010B8EF /* AppDelegate.m */; }; 0D827D711990D6110010B8EF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D6D1990D6110010B8EF /* main.m */; }; @@ -32,6 +26,13 @@ 0D827D99199110F60010B8EF /* Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D98199110F60010B8EF /* Prefix.pch */; }; 0D827D9E19911A190010B8EF /* MASShortcutValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D9C19911A190010B8EF /* MASShortcutValidator.h */; }; 0D827D9F19911A190010B8EF /* MASShortcutValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D9D19911A190010B8EF /* MASShortcutValidator.m */; }; + 0D827DA519912D240010B8EF /* MASShortcutMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827DA319912D240010B8EF /* MASShortcutMonitor.h */; }; + 0D827DAD199132840010B8EF /* MASShortcutBinder.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827DAB199132840010B8EF /* MASShortcutBinder.h */; }; + 0DC2F17619922798003A0131 /* MASHotKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 0DC2F17419922798003A0131 /* MASHotKey.h */; }; + 0DC2F17719922798003A0131 /* MASHotKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC2F17519922798003A0131 /* MASHotKey.m */; }; + 0DC2F17C199232EA003A0131 /* MASShortcutMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827DA419912D240010B8EF /* MASShortcutMonitor.m */; }; + 0DC2F17D199232F7003A0131 /* MASShortcutBinder.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827DAC199132840010B8EF /* MASShortcutBinder.m */; }; + 0DC2F18919925F8F003A0131 /* MASShortcutBinderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC2F18819925F8F003A0131 /* MASShortcutBinderTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -67,14 +68,8 @@ 0D827CEB1990D4420010B8EF /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 0D827D1B1990D55E0010B8EF /* MASShortcut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcut.h; path = Framework/MASShortcut.h; sourceTree = "<group>"; }; 0D827D1C1990D55E0010B8EF /* MASShortcut.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcut.m; path = Framework/MASShortcut.m; sourceTree = "<group>"; }; - 0D827D1D1990D55E0010B8EF /* MASShortcut+Monitoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MASShortcut+Monitoring.h"; path = "Framework/MASShortcut+Monitoring.h"; sourceTree = "<group>"; }; - 0D827D1E1990D55E0010B8EF /* MASShortcut+Monitoring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MASShortcut+Monitoring.m"; path = "Framework/MASShortcut+Monitoring.m"; sourceTree = "<group>"; }; - 0D827D1F1990D55E0010B8EF /* MASShortcut+UserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MASShortcut+UserDefaults.h"; path = "Framework/MASShortcut+UserDefaults.h"; sourceTree = "<group>"; }; - 0D827D201990D55E0010B8EF /* MASShortcut+UserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MASShortcut+UserDefaults.m"; path = "Framework/MASShortcut+UserDefaults.m"; sourceTree = "<group>"; }; 0D827D211990D55E0010B8EF /* MASShortcutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcutView.h; path = Framework/MASShortcutView.h; sourceTree = "<group>"; }; 0D827D221990D55E0010B8EF /* MASShortcutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutView.m; path = Framework/MASShortcutView.m; sourceTree = "<group>"; }; - 0D827D231990D55E0010B8EF /* MASShortcutView+UserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MASShortcutView+UserDefaults.h"; path = "Framework/MASShortcutView+UserDefaults.h"; sourceTree = "<group>"; }; - 0D827D241990D55E0010B8EF /* MASShortcutView+UserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MASShortcutView+UserDefaults.m"; path = "Framework/MASShortcutView+UserDefaults.m"; sourceTree = "<group>"; }; 0D827D2F1990D5640010B8EF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = "<group>"; }; 0D827D371990D5E70010B8EF /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0D827D691990D6110010B8EF /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; @@ -92,6 +87,13 @@ 0D827D98199110F60010B8EF /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Prefix.pch; path = Framework/Prefix.pch; sourceTree = "<group>"; }; 0D827D9C19911A190010B8EF /* MASShortcutValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcutValidator.h; path = Framework/MASShortcutValidator.h; sourceTree = "<group>"; }; 0D827D9D19911A190010B8EF /* MASShortcutValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutValidator.m; path = Framework/MASShortcutValidator.m; sourceTree = "<group>"; }; + 0D827DA319912D240010B8EF /* MASShortcutMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcutMonitor.h; path = Framework/MASShortcutMonitor.h; sourceTree = "<group>"; }; + 0D827DA419912D240010B8EF /* MASShortcutMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutMonitor.m; path = Framework/MASShortcutMonitor.m; sourceTree = "<group>"; }; + 0D827DAB199132840010B8EF /* MASShortcutBinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcutBinder.h; path = Framework/MASShortcutBinder.h; sourceTree = "<group>"; }; + 0D827DAC199132840010B8EF /* MASShortcutBinder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutBinder.m; path = Framework/MASShortcutBinder.m; sourceTree = "<group>"; }; + 0DC2F17419922798003A0131 /* MASHotKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASHotKey.h; path = Framework/MASHotKey.h; sourceTree = "<group>"; }; + 0DC2F17519922798003A0131 /* MASHotKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASHotKey.m; path = Framework/MASHotKey.m; sourceTree = "<group>"; }; + 0DC2F18819925F8F003A0131 /* MASShortcutBinderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutBinderTests.m; path = Framework/MASShortcutBinderTests.m; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -161,7 +163,7 @@ isa = PBXGroup; children = ( 0D827DA019912A660010B8EF /* Model */, - 0D827DA219912A870010B8EF /* Handling & Persistence */, + 0D827DA219912A870010B8EF /* Watching & Storage */, 0D827DA119912A6D0010B8EF /* UI */, 0D827D2F1990D5640010B8EF /* Info.plist */, 0D827D98199110F60010B8EF /* Prefix.pch */, @@ -211,21 +213,22 @@ children = ( 0D827D211990D55E0010B8EF /* MASShortcutView.h */, 0D827D221990D55E0010B8EF /* MASShortcutView.m */, - 0D827D231990D55E0010B8EF /* MASShortcutView+UserDefaults.h */, - 0D827D241990D55E0010B8EF /* MASShortcutView+UserDefaults.m */, ); name = UI; sourceTree = "<group>"; }; - 0D827DA219912A870010B8EF /* Handling & Persistence */ = { + 0D827DA219912A870010B8EF /* Watching & Storage */ = { isa = PBXGroup; children = ( - 0D827D1D1990D55E0010B8EF /* MASShortcut+Monitoring.h */, - 0D827D1E1990D55E0010B8EF /* MASShortcut+Monitoring.m */, - 0D827D1F1990D55E0010B8EF /* MASShortcut+UserDefaults.h */, - 0D827D201990D55E0010B8EF /* MASShortcut+UserDefaults.m */, - ); - name = "Handling & Persistence"; + 0DC2F17419922798003A0131 /* MASHotKey.h */, + 0DC2F17519922798003A0131 /* MASHotKey.m */, + 0D827DA319912D240010B8EF /* MASShortcutMonitor.h */, + 0D827DA419912D240010B8EF /* MASShortcutMonitor.m */, + 0D827DAB199132840010B8EF /* MASShortcutBinder.h */, + 0D827DAC199132840010B8EF /* MASShortcutBinder.m */, + 0DC2F18819925F8F003A0131 /* MASShortcutBinderTests.m */, + ); + name = "Watching & Storage"; sourceTree = "<group>"; }; /* End PBXGroup section */ @@ -239,11 +242,11 @@ 0D827D99199110F60010B8EF /* Prefix.pch in Headers */, 0D827D9719910FF70010B8EF /* MASKeyCodes.h in Headers */, 0D827D251990D55E0010B8EF /* MASShortcut.h in Headers */, - 0D827D2D1990D55E0010B8EF /* MASShortcutView+UserDefaults.h in Headers */, - 0D827D271990D55E0010B8EF /* MASShortcut+Monitoring.h in Headers */, + 0D827DAD199132840010B8EF /* MASShortcutBinder.h in Headers */, 0D827D771990F81E0010B8EF /* Shortcut.h in Headers */, - 0D827D291990D55E0010B8EF /* MASShortcut+UserDefaults.h in Headers */, + 0DC2F17619922798003A0131 /* MASHotKey.h in Headers */, 0D827D9E19911A190010B8EF /* MASShortcutValidator.h in Headers */, + 0D827DA519912D240010B8EF /* MASShortcutMonitor.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -368,12 +371,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0DC2F17719922798003A0131 /* MASHotKey.m in Sources */, 0D827D9F19911A190010B8EF /* MASShortcutValidator.m in Sources */, - 0D827D2E1990D55E0010B8EF /* MASShortcutView+UserDefaults.m in Sources */, + 0DC2F17C199232EA003A0131 /* MASShortcutMonitor.m in Sources */, 0D827D2C1990D55E0010B8EF /* MASShortcutView.m in Sources */, - 0D827D2A1990D55E0010B8EF /* MASShortcut+UserDefaults.m in Sources */, 0D827D261990D55E0010B8EF /* MASShortcut.m in Sources */, - 0D827D281990D55E0010B8EF /* MASShortcut+Monitoring.m in Sources */, + 0DC2F17D199232F7003A0131 /* MASShortcutBinder.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -391,6 +394,7 @@ buildActionMask = 2147483647; files = ( 0D827D9419910B740010B8EF /* MASShortcutTests.m in Sources */, + 0DC2F18919925F8F003A0131 /* MASShortcutBinderTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |
