13 Commits

Author SHA1 Message Date
Vadim Shpakovski 652b5bb6ed Merge pull request #8 from trustme/32-bit
Fixes for 32 bit compile
2012-09-22 07:28:16 -07:00
Duane Wandless afc25e4b7d Fixes for 32 bit compile 2012-09-22 09:22:20 -04:00
Vadim Shpakovski 344e3e1999 Fixes memory leaks. 2012-09-22 09:54:37 +03:00
Vadim Shpakovski fe750267fb Improves README. 2012-09-19 13:25:29 +03:00
Vadim Shpakovski cbc369c71a Removes ARC to support 32-bit. 2012-09-19 13:16:23 +03:00
Vadim Shpakovski 4099b62587 Isolates monitoring code from data. 2012-09-19 12:43:39 +03:00
Vadim Shpakovski 243aee3038 Adds support for direct monitoring shortcuts. 2012-09-19 12:36:53 +03:00
Vadim Shpakovski 723eeb4346 Improves API. 2012-09-19 08:57:26 +03:00
Vadim Shpakovski b1f90fa096 Merge. 2012-09-19 01:50:15 +03:00
Vadim Shpakovski 08717ff81e Merge remote-tracking branch 'origin/master' 2012-09-19 01:49:24 +03:00
Vadim Shpakovski da2e1af574 Adds a new appearance and fixes bugs related to key equivalents. 2012-09-19 01:44:49 +03:00
Vadim Shpakovski dddb527bfb Fixes the issue with setting associated User Defaults key twice. 2012-09-19 00:56:51 +03:00
Vadim Shpakovski e01e9075e5 Improves textured view appearance. 2012-09-03 16:23:21 +03:00
13 changed files with 426 additions and 670 deletions
+2 -1
View File
@@ -14,4 +14,5 @@ xcuserdata
profile
*.moved-aside
# Finder
.DS_Store
.DS_Store
DerivedData
+8
View File
@@ -0,0 +1,8 @@
#import "MASShortcut.h"
@interface MASShortcut (Monitoring)
+ (id)addGlobalHotkeyMonitorWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler;
+ (void)removeGlobalHotkeyMonitor:(id)monitor;
@end
@@ -1,44 +1,47 @@
#import "MASShortcut+UserDefaults.h"
#import "MASShortcut+Monitoring.h"
@interface MASShortcutHotKey : NSObject
NSMutableDictionary *MASRegisteredHotKeys();
BOOL InstallCommonEventHandler();
void UninstallEventHandler();
void InstallHotkeyWithShortcut(MASShortcut *shortcut, UInt32 *outCarbonHotKeyID, EventHotKeyRef *outCarbonHotKey);
@property (nonatomic, readonly) NSString *userDefaultsKey;
#pragma mark -
@interface MASShortcutHotKey : NSObject {
MASShortcut *_shortcut;
void (^_handler)();
EventHotKeyRef _carbonHotKey;
UInt32 _carbonHotKeyID;
}
@property (nonatomic, readonly) MASShortcut *shortcut;
@property (nonatomic, readonly, copy) void (^handler)();
@property (nonatomic, readonly) EventHotKeyRef carbonHotKey;
@property (nonatomic, readonly) UInt32 carbonHotKeyID;
- (id)initWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler;
+ (void)uninstallEventHandler;
- (id)initWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler;
@end
#pragma mark -
@implementation MASShortcut (UserDefaults)
@implementation MASShortcut (Monitoring)
+ (NSMutableDictionary *)registeredHotKeys
+ (id)addGlobalHotkeyMonitorWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler
{
static NSMutableDictionary *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [NSMutableDictionary dictionary];
});
return shared;
NSString *monitor = [NSString stringWithFormat:@"%p: %@", shortcut, shortcut.description];
MASShortcutHotKey *hotKey = [[[MASShortcutHotKey alloc] initWithShortcut:shortcut handler:handler] autorelease];
[MASRegisteredHotKeys() setObject:hotKey forKey:monitor];
return monitor;
}
+ (void)registerGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler;
+ (void)removeGlobalHotkeyMonitor:(id)monitor
{
MASShortcutHotKey *hotKey = [[MASShortcutHotKey alloc] initWithUserDefaultsKey:userDefaultsKey handler:handler];
[[self registeredHotKeys] setObject:hotKey forKey:userDefaultsKey];
}
+ (void)unregisterGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey
{
NSMutableDictionary *registeredHotKeys = [self registeredHotKeys];
[registeredHotKeys removeObjectForKey:userDefaultsKey];
if (monitor == nil) return;
NSMutableDictionary *registeredHotKeys = MASRegisteredHotKeys();
[registeredHotKeys removeObjectForKey:monitor];
if (registeredHotKeys.count == 0) {
[MASShortcutHotKey uninstallEventHandler];
UninstallEventHandler();
}
}
@@ -50,65 +53,30 @@
@synthesize carbonHotKeyID = _carbonHotKeyID;
@synthesize handler = _handler;
@synthesize userDefaultsKey = _userDefaultsKey;
@synthesize shortcut = _shortcut;
@synthesize carbonHotKey = _carbonHotKey;
#pragma mark -
- (id)initWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler
- (id)initWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler
{
self = [super init];
if (self) {
_userDefaultsKey = userDefaultsKey.copy;
_shortcut = [shortcut retain];
_handler = [handler copy];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:)
name:NSUserDefaultsDidChangeNotification object:[NSUserDefaults standardUserDefaults]];
[self installHotKeyFromUserDefaults];
InstallHotkeyWithShortcut(shortcut, &_carbonHotKeyID, &_carbonHotKey);
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSUserDefaultsDidChangeNotification object:[NSUserDefaults standardUserDefaults]];
[self uninstallExisitingHotKey];
[_shortcut release];
[_handler release];
[super dealloc];
}
#pragma mark -
- (void)userDefaultsDidChange:(NSNotification *)note
{
[self uninstallExisitingHotKey];
[self installHotKeyFromUserDefaults];
}
#pragma mark - Carbon events
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;
}
}
#pragma mark -
- (void)uninstallExisitingHotKey
{
if (_carbonHotKey) {
@@ -117,20 +85,37 @@ static EventHandlerRef sEventHandler = NULL;
}
}
@end
#pragma mark - Carbon magic
NSMutableDictionary *MASRegisteredHotKeys()
{
static NSMutableDictionary *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[NSMutableDictionary alloc] init];
});
return shared;
}
#pragma mark -
FourCharCode const kMASShortcutSignature = 'MASS';
- (void)installHotKeyFromUserDefaults
void InstallHotkeyWithShortcut(MASShortcut *shortcut, UInt32 *outCarbonHotKeyID, EventHotKeyRef *outCarbonHotKey)
{
NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:_userDefaultsKey];
MASShortcut *shortcut = [MASShortcut shortcutWithData:data];
if ((shortcut == nil) || ![[self class] installCommonEventHandler]) return;
if ((shortcut == nil) || !InstallCommonEventHandler()) return;
static UInt32 sCarbonHotKeyID = 0;
_carbonHotKeyID = ++ sCarbonHotKeyID;
EventHotKeyID hotKeyID = { .signature = kMASShortcutSignature, .id = _carbonHotKeyID };
if (RegisterEventHotKey(shortcut.carbonKeyCode, shortcut.carbonFlags, hotKeyID, GetEventDispatcherTarget(), kEventHotKeyExclusive, &_carbonHotKey) != noErr) {
_carbonHotKey = NULL;
EventHotKeyID hotKeyID = { .signature = kMASShortcutSignature, .id = ++ sCarbonHotKeyID };
EventHotKeyRef carbonHotKey = NULL;
if (RegisterEventHotKey(shortcut.carbonKeyCode, shortcut.carbonFlags, hotKeyID, GetEventDispatcherTarget(), kEventHotKeyExclusive, &carbonHotKey) != noErr) {
carbonHotKey = NULL;
}
if (outCarbonHotKeyID) *outCarbonHotKeyID = hotKeyID.id;
if (outCarbonHotKey) *outCarbonHotKey = carbonHotKey;
}
static OSStatus CarbonCallback(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
@@ -143,7 +128,7 @@ static OSStatus CarbonCallback(EventHandlerCallRef inHandlerCallRef, EventRef in
if (hotKeyID.signature != kMASShortcutSignature) return noErr;
[[MASShortcut registeredHotKeys] enumerateKeysAndObjectsUsingBlock:^(id key, MASShortcutHotKey *hotKey, BOOL *stop) {
[MASRegisteredHotKeys() enumerateKeysAndObjectsUsingBlock:^(id key, MASShortcutHotKey *hotKey, BOOL *stop) {
if (hotKeyID.id == hotKey.carbonHotKeyID) {
if (hotKey.handler) {
hotKey.handler();
@@ -155,4 +140,27 @@ static OSStatus CarbonCallback(EventHandlerCallRef inHandlerCallRef, EventRef in
return noErr;
}
@end
#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;
}
}
-9
View File
@@ -1,9 +0,0 @@
#import "MASShortcut.h"
@interface MASShortcut (UserDefaults)
+ (void)registerGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler;
+ (void)unregisterGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey;
@end
+7
View File
@@ -0,0 +1,7 @@
//
// Prefix header for all source files of the 'MASShortcut' target in the 'MASShortcut' project
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif
+5 -1
View File
@@ -30,13 +30,17 @@ enum {
kMASShortcutGlyphSoutheastArrow = 0x2198,
} MASShortcutGlyph;
@interface MASShortcut : NSObject <NSCoding>
@interface MASShortcut : NSObject <NSCoding> {
NSUInteger _keyCode; // NSNotFound if empty
NSUInteger _modifierFlags; // 0 if empty
}
@property (nonatomic) NSUInteger keyCode;
@property (nonatomic) NSUInteger modifierFlags;
@property (nonatomic, readonly) UInt32 carbonKeyCode;
@property (nonatomic, readonly) UInt32 carbonFlags;
@property (nonatomic, readonly) NSString *keyCodeString;
@property (nonatomic, readonly) NSString *keyCodeStringForKeyEquivalent;
@property (nonatomic, readonly) NSString *modifierFlagsString;
@property (nonatomic, readonly) NSData *data;
@property (nonatomic, readonly) BOOL shouldBypass;
+47 -13
View File
@@ -3,10 +3,7 @@
NSString *const kMASShortcutKeyCode = @"KeyCode";
NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
@implementation MASShortcut {
NSUInteger _keyCode; // NSNotFound if empty
NSUInteger _modifierFlags; // 0 if empty
}
@implementation MASShortcut
@synthesize modifierFlags = _modifierFlags;
@synthesize keyCode = _keyCode;
@@ -42,17 +39,18 @@ NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
+ (MASShortcut *)shortcutWithKeyCode:(NSUInteger)code modifierFlags:(NSUInteger)flags
{
return [[self alloc] initWithKeyCode:code modifierFlags:flags];
return [[[self alloc] initWithKeyCode:code modifierFlags:flags] autorelease];
}
+ (MASShortcut *)shortcutWithEvent:(NSEvent *)event
{
return [[self alloc] initWithKeyCode:event.keyCode modifierFlags:event.modifierFlags];
return [[[self alloc] initWithKeyCode:event.keyCode modifierFlags:event.modifierFlags] autorelease];
}
+ (MASShortcut *)shortcutWithData:(NSData *)data
{
return (data ? (MASShortcut *)[NSKeyedUnarchiver unarchiveObjectWithData:data] : nil);
id shortcut = (data ? [NSKeyedUnarchiver unarchiveObjectWithData:data] : nil);
return shortcut;
}
#pragma mark - Shortcut accessors
@@ -82,6 +80,35 @@ NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
return [NSString stringWithFormat:@"%@%@", self.modifierFlagsString, self.keyCodeString];
}
- (NSString *)keyCodeStringForKeyEquivalent
{
NSString *keyCodeString = self.keyCodeString;
if (keyCodeString.length > 1) {
switch (self.keyCode) {
case kVK_F1: return MASShortcutChar(0xF704);
case kVK_F2: return MASShortcutChar(0xF705);
case kVK_F3: return MASShortcutChar(0xF706);
case kVK_F4: return MASShortcutChar(0xF707);
case kVK_F5: return MASShortcutChar(0xF708);
case kVK_F6: return MASShortcutChar(0xF709);
case kVK_F7: return MASShortcutChar(0xF70a);
case kVK_F8: return MASShortcutChar(0xF70b);
case kVK_F9: return MASShortcutChar(0xF70c);
case kVK_F10: return MASShortcutChar(0xF70d);
case kVK_F11: return MASShortcutChar(0xF70e);
case kVK_F12: return MASShortcutChar(0xF70f);
// From this point down I am guessing F13 etc come sequentially, I don't have a keyboard to test.
case kVK_F13: return MASShortcutChar(0xF710);
case kVK_F14: return MASShortcutChar(0xF711);
case kVK_F15: return MASShortcutChar(0xF712);
case kVK_F16: return MASShortcutChar(0xF713);
case kVK_Space: return MASShortcutChar(0x20);
default: return @"";
}
}
return keyCodeString.lowercaseString;
}
- (NSString *)keyCodeString
{
// Some key codes don't have an equivalent
@@ -165,7 +192,7 @@ NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
if (keystroke.length) {
static NSMutableCharacterSet *validChars = nil;
if (validChars == nil) {
validChars = [[NSMutableCharacterSet alloc] init];
validChars = [[[NSMutableCharacterSet alloc] init] autorelease];
[validChars formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]];
[validChars formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
[validChars formUnionWithCharacterSet:[NSCharacterSet symbolCharacterSet]];
@@ -223,8 +250,15 @@ NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
if (menuItem.hasSubmenu && [self isKeyEquivalent:keyEquivalent flags:flags takenInMenu:menuItem.submenu error:outError]) return YES;
BOOL equalFlags = (MASShortcutClear(menuItem.keyEquivalentModifierMask) == flags);
BOOL equalHotkey = [menuItem.keyEquivalent.uppercaseString isEqualToString:keyEquivalent];
if (equalFlags && equalHotkey) {
BOOL equalHotkeyLowercase = [menuItem.keyEquivalent.lowercaseString isEqualToString:keyEquivalent];
// Check if the cases are different, we know ours is lower and that shift is included in our modifiers
// If theirs is capitol, we need to add shift to their modifiers
if (equalHotkeyLowercase && ![menuItem.keyEquivalent isEqualToString:keyEquivalent]) {
equalFlags = (MASShortcutClear(menuItem.keyEquivalentModifierMask | NSShiftKeyMask) == flags);
}
if (equalFlags && equalHotkeyLowercase) {
if (outError) {
NSString *format = NSLocalizedString(@"This shortcut cannot be used used because it is already used by the menu item %@.",
@"Message for alert when shortcut is already used");
@@ -249,8 +283,8 @@ NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
CFNumberRef code = CFDictionaryGetValue(hotKeyInfo, kHISymbolicHotKeyCode);
CFNumberRef flags = CFDictionaryGetValue(hotKeyInfo, kHISymbolicHotKeyModifiers);
if (([(__bridge NSNumber *)code unsignedIntegerValue] == self.keyCode) &&
([(__bridge NSNumber *)flags unsignedIntegerValue] == self.carbonFlags)) {
if (([(NSNumber *)code unsignedIntegerValue] == self.keyCode) &&
([(NSNumber *)flags unsignedIntegerValue] == self.carbonFlags)) {
if (outError) {
NSString *description = NSLocalizedString(@"This combination cannot be used used because it is already used by a system-wide "
@@ -265,7 +299,7 @@ NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
}
CFRelease(globalHotKeys);
}
return [self isKeyEquivalent:self.keyCodeString flags:self.modifierFlags takenInMenu:[NSApp mainMenu] error:outError];
return [self isKeyEquivalent:self.keyCodeStringForKeyEquivalent flags:self.modifierFlags takenInMenu:[NSApp mainMenu] error:outError];
}
@end
+268
View File
@@ -0,0 +1,268 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
47C3BC1E160DF126006E3107 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47C3BC1D160DF126006E3107 /* Cocoa.framework */; };
47C3BC3A160DF182006E3107 /* MASShortcut.m in Sources */ = {isa = PBXBuildFile; fileRef = 47C3BC36160DF182006E3107 /* MASShortcut.m */; };
47C3BC3B160DF182006E3107 /* MASShortcut+Monitoring.m in Sources */ = {isa = PBXBuildFile; fileRef = 47C3BC37160DF182006E3107 /* MASShortcut+Monitoring.m */; };
47C3BC3F160DF215006E3107 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47C3BC3E160DF215006E3107 /* Carbon.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
47C3BC1A160DF126006E3107 /* MASShortcut.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = MASShortcut.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
47C3BC1D160DF126006E3107 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
47C3BC20160DF126006E3107 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
47C3BC21160DF126006E3107 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
47C3BC22160DF126006E3107 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
47C3BC34160DF176006E3107 /* MASShortcut-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MASShortcut-Prefix.pch"; sourceTree = SOURCE_ROOT; };
47C3BC36160DF182006E3107 /* MASShortcut.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASShortcut.m; sourceTree = SOURCE_ROOT; };
47C3BC37160DF182006E3107 /* MASShortcut+Monitoring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MASShortcut+Monitoring.m"; sourceTree = SOURCE_ROOT; };
47C3BC38160DF182006E3107 /* MASShortcut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASShortcut.h; sourceTree = SOURCE_ROOT; };
47C3BC39160DF182006E3107 /* MASShortcut+Monitoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MASShortcut+Monitoring.h"; sourceTree = SOURCE_ROOT; };
47C3BC3E160DF215006E3107 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
47C3BC17160DF126006E3107 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
47C3BC3F160DF215006E3107 /* Carbon.framework in Frameworks */,
47C3BC1E160DF126006E3107 /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
47C3BC0F160DF126006E3107 = {
isa = PBXGroup;
children = (
47C3BC3E160DF215006E3107 /* Carbon.framework */,
47C3BC23160DF126006E3107 /* MASShortcut */,
47C3BC1C160DF126006E3107 /* Frameworks */,
47C3BC1B160DF126006E3107 /* Products */,
);
sourceTree = "<group>";
};
47C3BC1B160DF126006E3107 /* Products */ = {
isa = PBXGroup;
children = (
47C3BC1A160DF126006E3107 /* MASShortcut.dylib */,
);
name = Products;
sourceTree = "<group>";
};
47C3BC1C160DF126006E3107 /* Frameworks */ = {
isa = PBXGroup;
children = (
47C3BC1D160DF126006E3107 /* Cocoa.framework */,
47C3BC1F160DF126006E3107 /* Other Frameworks */,
);
name = Frameworks;
sourceTree = "<group>";
};
47C3BC1F160DF126006E3107 /* Other Frameworks */ = {
isa = PBXGroup;
children = (
47C3BC20160DF126006E3107 /* AppKit.framework */,
47C3BC21160DF126006E3107 /* CoreData.framework */,
47C3BC22160DF126006E3107 /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
47C3BC23160DF126006E3107 /* MASShortcut */ = {
isa = PBXGroup;
children = (
47C3BC36160DF182006E3107 /* MASShortcut.m */,
47C3BC37160DF182006E3107 /* MASShortcut+Monitoring.m */,
47C3BC38160DF182006E3107 /* MASShortcut.h */,
47C3BC39160DF182006E3107 /* MASShortcut+Monitoring.h */,
47C3BC24160DF126006E3107 /* Supporting Files */,
);
path = MASShortcut;
sourceTree = "<group>";
};
47C3BC24160DF126006E3107 /* Supporting Files */ = {
isa = PBXGroup;
children = (
47C3BC34160DF176006E3107 /* MASShortcut-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
47C3BC18160DF126006E3107 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
47C3BC19160DF126006E3107 /* MASShortcut */ = {
isa = PBXNativeTarget;
buildConfigurationList = 47C3BC2B160DF126006E3107 /* Build configuration list for PBXNativeTarget "MASShortcut" */;
buildPhases = (
47C3BC16160DF126006E3107 /* Sources */,
47C3BC17160DF126006E3107 /* Frameworks */,
47C3BC18160DF126006E3107 /* Headers */,
);
buildRules = (
);
dependencies = (
);
name = MASShortcut;
productName = MASShortcut;
productReference = 47C3BC1A160DF126006E3107 /* MASShortcut.dylib */;
productType = "com.apple.product-type.library.dynamic";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
47C3BC11160DF126006E3107 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0450;
ORGANIZATIONNAME = "Duane Wandless";
};
buildConfigurationList = 47C3BC14160DF126006E3107 /* Build configuration list for PBXProject "MASShortcut" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 47C3BC0F160DF126006E3107;
productRefGroup = 47C3BC1B160DF126006E3107 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
47C3BC19160DF126006E3107 /* MASShortcut */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
47C3BC16160DF126006E3107 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
47C3BC3A160DF182006E3107 /* MASShortcut.m in Sources */,
47C3BC3B160DF182006E3107 /* MASShortcut+Monitoring.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
47C3BC29160DF126006E3107 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.8;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
VALID_ARCHS = i386;
};
name = Debug;
};
47C3BC2A160DF126006E3107 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.8;
SDKROOT = macosx;
VALID_ARCHS = i386;
};
name = Release;
};
47C3BC2C160DF126006E3107 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "MASShortcut-Prefix.pch";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
47C3BC2D160DF126006E3107 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "MASShortcut-Prefix.pch";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
47C3BC14160DF126006E3107 /* Build configuration list for PBXProject "MASShortcut" */ = {
isa = XCConfigurationList;
buildConfigurations = (
47C3BC29160DF126006E3107 /* Debug */,
47C3BC2A160DF126006E3107 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
47C3BC2B160DF126006E3107 /* Build configuration list for PBXNativeTarget "MASShortcut" */ = {
isa = XCConfigurationList;
buildConfigurations = (
47C3BC2C160DF126006E3107 /* Debug */,
47C3BC2D160DF126006E3107 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 47C3BC11160DF126006E3107 /* Project object */;
}
-7
View File
@@ -1,7 +0,0 @@
#import "MASShortcutView.h"
@interface MASShortcutView (UserDefaults)
@property (nonatomic, copy) NSString *associatedUserDefaultsKey;
@end
-120
View File
@@ -1,120 +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 *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;
}
@synthesize userDefaultsKey = _userDefaultsKey;
@synthesize shortcutView = _shortcutView;
#pragma mark -
- (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
-16
View File
@@ -1,16 +0,0 @@
@class MASShortcut;
typedef enum {
MASShortcutViewAppearanceDefault = 0,
MASShortcutViewAppearanceTexturedRect
} MASShortcutViewAppearance;
@interface MASShortcutView : NSView
@property (nonatomic, strong) MASShortcut *shortcutValue;
@property (nonatomic, getter = isRecording) BOOL recording;
@property (nonatomic, getter = isEnabled) BOOL enabled;
@property (nonatomic, copy) void (^shortcutValueChange)(MASShortcutView *sender);
@property (nonatomic) MASShortcutViewAppearance appearance;
@end
-414
View File
@@ -1,414 +0,0 @@
#import "MASShortcutView.h"
#import "MASShortcut.h"
#define HINT_BUTTON_WIDTH 23.0
#define BUTTON_FONT_SIZE 11.0
#define SEGMENT_CHROME_WIDTH 6.0
#pragma mark -
@interface MASShortcutView () // Private accessors
@property (nonatomic, getter = isHinting) BOOL hinting;
@property (nonatomic, copy) NSString *shortcutPlaceholder;
@end
#pragma mark -
@implementation MASShortcutView {
NSButtonCell *_shortcutCell;
NSInteger _shortcutToolTipTag;
NSInteger _hintToolTipTag;
NSTrackingArea *_hintArea;
}
@synthesize enabled = _enabled;
@synthesize hinting = _hinting;
@synthesize shortcutValue = _shortcutValue;
@synthesize shortcutPlaceholder = _shortcutPlaceholder;
@synthesize shortcutValueChange = _shortcutValueChange;
@synthesize recording = _recording;
#pragma mark -
- (id)initWithFrame:(CGRect)frameRect
{
self = [super initWithFrame:frameRect];
if (self) {
_shortcutCell = [[NSButtonCell alloc] init];
_shortcutCell.buttonType = NSPushOnPushOffButton;
_shortcutCell.font = [[NSFontManager sharedFontManager] convertFont:_shortcutCell.font toSize:BUTTON_FONT_SIZE];
_enabled = YES;
[self resetShortcutCellStyle];
}
return self;
}
- (void)dealloc
{
[self activateEventMonitoring:NO];
[self activateResignObserver:NO];
}
#pragma mark - Public accessors
- (void)setEnabled:(BOOL)flag
{
if (_enabled != flag) {
_enabled = flag;
[self updateTrackingAreas];
self.recording = NO;
[self setNeedsDisplay:YES];
}
}
- (void)setAppearance:(MASShortcutViewAppearance)appearance
{
if (_appearance != appearance) {
_appearance = appearance;
[self resetShortcutCellStyle];
[self setNeedsDisplay:YES];
}
}
- (void)resetShortcutCellStyle
{
switch (_appearance) {
case MASShortcutViewAppearanceDefault: {
_shortcutCell.bezelStyle = NSRoundRectBezelStyle;
break;
}
case MASShortcutViewAppearanceTexturedRect: {
_shortcutCell.bezelStyle = NSTexturedRoundedBezelStyle;
break;
}
}
}
- (void)setRecording:(BOOL)flag
{
// Only one recorder can be active at the moment
static MASShortcutView *currentRecorder = nil;
if (flag && (currentRecorder != self)) {
currentRecorder.recording = NO;
currentRecorder = flag ? self : nil;
}
// Only enabled view supports recording
if (flag && !self.enabled) return;
if (_recording != flag) {
_recording = flag;
self.shortcutPlaceholder = nil;
[self resetToolTips];
[self activateEventMonitoring:_recording];
[self activateResignObserver:_recording];
[self setNeedsDisplay:YES];
}
}
- (void)setShortcutValue:(MASShortcut *)shortcutValue
{
_shortcutValue = shortcutValue;
[self resetToolTips];
[self setNeedsDisplay:YES];
if (self.shortcutValueChange) {
self.shortcutValueChange(self);
}
}
- (void)setShortcutPlaceholder:(NSString *)shortcutPlaceholder
{
_shortcutPlaceholder = shortcutPlaceholder.copy;
[self setNeedsDisplay:YES];
}
#pragma mark - Drawing
- (BOOL)isFlipped
{
return YES;
}
- (void)drawInRect:(CGRect)frame withTitle:(NSString *)title alignment:(NSTextAlignment)alignment state:(NSInteger)state
{
_shortcutCell.title = title;
_shortcutCell.alignment = alignment;
_shortcutCell.state = state;
_shortcutCell.enabled = self.enabled;
switch (_appearance) {
case MASShortcutViewAppearanceDefault: {
[_shortcutCell drawWithFrame:frame inView:self];
break;
}
case MASShortcutViewAppearanceTexturedRect: {
[_shortcutCell drawWithFrame:CGRectOffset(frame, 0.0, 1.0) inView:self];
break;
}
}
}
- (void)drawRect:(CGRect)dirtyRect
{
if (self.shortcutValue) {
[self drawInRect:self.bounds withTitle:MASShortcutChar(self.recording ? kMASShortcutGlyphEscape : kMASShortcutGlyphDeleteLeft)
alignment:NSRightTextAlignment state:NSOffState];
CGRect shortcutRect;
[self getShortcutRect:&shortcutRect hintRect:NULL];
NSString *title = (self.recording
? (_hinting
? 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")))
: _shortcutValue ? _shortcutValue.description : @"");
[self drawInRect:shortcutRect withTitle:title alignment:NSCenterTextAlignment state:self.isRecording ? NSOnState : NSOffState];
}
else {
if (self.recording)
{
[self drawInRect:self.bounds withTitle:MASShortcutChar(kMASShortcutGlyphEscape) alignment:NSRightTextAlignment state:NSOffState];
CGRect shortcutRect;
[self getShortcutRect:&shortcutRect hintRect:NULL];
NSString *title = (_hinting
? NSLocalizedString(@"Cancel", @"Cancel action button in recording state")
: (self.shortcutPlaceholder.length > 0
? self.shortcutPlaceholder
: 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(@"Record Shortcut", @"Empty shortcut button in normal state")
alignment:NSCenterTextAlignment state:NSOffState];
}
}
}
#pragma mark - Mouse handling
- (void)getShortcutRect:(CGRect *)shortcutRectRef hintRect:(CGRect *)hintRectRef
{
CGRect shortcutRect, hintRect;
CGRectDivide(self.bounds, &hintRect, &shortcutRect, HINT_BUTTON_WIDTH, CGRectMaxXEdge);
if (shortcutRectRef) *shortcutRectRef = shortcutRect;
if (hintRectRef) *hintRectRef = hintRect;
}
- (BOOL)locationInShortcutRect:(CGPoint)location
{
CGRect shortcutRect;
[self getShortcutRect:&shortcutRect hintRect:NULL];
return CGRectContainsPoint(shortcutRect, [self convertPoint:location fromView:nil]);
}
- (BOOL)locationInHintRect:(CGPoint)location
{
CGRect hintRect;
[self getShortcutRect:NULL hintRect:&hintRect];
return CGRectContainsPoint(hintRect, [self convertPoint:location fromView:nil]);
}
- (void)mouseDown:(NSEvent *)event
{
if (self.enabled) {
if (self.shortcutValue) {
if (self.recording) {
if ([self locationInHintRect:event.locationInWindow]) {
self.recording = NO;
}
}
else {
if ([self locationInShortcutRect:event.locationInWindow]) {
self.recording = YES;
}
else {
self.shortcutValue = nil;
}
}
}
else {
if (self.recording) {
if ([self locationInHintRect:event.locationInWindow]) {
self.recording = NO;
}
}
else {
self.recording = YES;
}
}
}
else {
[super mouseDown:event];
}
}
#pragma mark - Handling mouse over
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
if (_hintArea) {
[self removeTrackingArea:_hintArea];
_hintArea = nil;
}
// Forbid hinting if view is disabled
if (!self.enabled) return;
CGRect hintRect;
[self getShortcutRect:NULL hintRect:&hintRect];
NSTrackingAreaOptions options = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingAssumeInside);
_hintArea = [[NSTrackingArea alloc] initWithRect:hintRect options:options owner:self userInfo:nil];
[self addTrackingArea:_hintArea];
}
- (void)setHinting:(BOOL)flag
{
if (_hinting != flag) {
_hinting = flag;
[self setNeedsDisplay:YES];
}
}
- (void)mouseEntered:(NSEvent *)event
{
self.hinting = YES;
}
- (void)mouseExited:(NSEvent *)event
{
self.hinting = NO;
}
void *kUserDataShortcut = &kUserDataShortcut;
void *kUserDataHint = &kUserDataHint;
- (void)resetToolTips
{
if (_shortcutToolTipTag) {
[self removeToolTip:_shortcutToolTipTag], _shortcutToolTipTag = 0;
}
if (_hintToolTipTag) {
[self removeToolTip:_hintToolTipTag], _hintToolTipTag = 0;
}
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];
_hintToolTipTag = [self addToolTipRect:hintRect owner:self userData:kUserDataHint];
}
- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(CGPoint)point userData:(void *)data
{
if (data == kUserDataShortcut) {
return NSLocalizedString(@"Click to record new shortcut", @"Tooltip for non-empty shortcut button");
}
else if (data == kUserDataHint) {
return NSLocalizedString(@"Delete shortcut", @"Tooltip for hint button near the non-empty shortcut");
}
return nil;
}
#pragma mark - Event monitoring
- (void)activateEventMonitoring:(BOOL)shouldActivate
{
static BOOL isActive = NO;
if (isActive == shouldActivate) return;
isActive = shouldActivate;
static id eventMonitor = nil;
if (shouldActivate) {
__weak MASShortcutView *weakSelf = self;
NSEventMask eventMask = (NSKeyDownMask | NSFlagsChangedMask);
eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^(NSEvent *event) {
MASShortcut *shortcut = [MASShortcut shortcutWithEvent:event];
if ((shortcut.keyCode == kVK_Delete) || (shortcut.keyCode == kVK_ForwardDelete)) {
// Delete shortcut
weakSelf.shortcutValue = nil;
weakSelf.recording = NO;
event = nil;
}
else if (shortcut.keyCode == kVK_Escape) {
// Cancel recording
weakSelf.recording = NO;
event = nil;
}
else if (shortcut.shouldBypass) {
// Command + W, Command + Q, ESC should deactivate recorder
weakSelf.recording = NO;
}
else {
// Verify possible shortcut
if (shortcut.keyCodeString.length > 0) {
if (shortcut.valid) {
// Verify that shortcut is not used
NSError *error = nil;
if ([shortcut isTakenError:&error]) {
// Prevent cancel of recording when Alert window is key
[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);
weakSelf.shortcutPlaceholder = nil;
[weakSelf activateResignObserver:YES];
[weakSelf activateEventMonitoring:YES];
}
else {
weakSelf.shortcutValue = shortcut;
weakSelf.recording = NO;
}
}
else {
// Key press with or without SHIFT is not valid input
NSBeep();
}
}
else {
// User is playing with modifier keys
weakSelf.shortcutPlaceholder = shortcut.modifierFlagsString;
}
event = nil;
}
return event;
}];
}
else {
[NSEvent removeMonitor:eventMonitor];
}
}
- (void)activateResignObserver:(BOOL)shouldActivate
{
static BOOL isActive = NO;
if (isActive == shouldActivate) return;
isActive = shouldActivate;
static id observer = nil;
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
if (shouldActivate) {
__weak MASShortcutView *weakSelf = self;
observer = [notificationCenter addObserverForName:NSWindowDidResignKeyNotification object:self.window
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) {
weakSelf.recording = NO;
}];
}
else {
[notificationCenter removeObserver:observer];
}
}
@end
+5 -13
View File
@@ -4,21 +4,13 @@ Some time ago Cocoa developers used a brilliant framework [ShortcutRecorder](htt
The project MASShortcut introduces modern API and user interface for recording, storing and using global keyboard shortcuts. All code is compatible with Xcode 4.3, Mac OS X 10.7 and the sandboxed environment.
# Usage
# Usage for the branch 32-bit
I hope, it is really easy:
// Make a raw keyboard shortcut
MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:kVK_F1 modifierFlags:NSCommandKeyMask];
// Drop a custom view into XIB and set its class to MASShortcutView
@property (nonatomic, weak) IBOutlet MASShortcutView *shortcutView;
// Think up a preference key to store a global shortcut between launches
NSString *const kPreferenceGlobalShortcut = @"GlobalShortcut";
// Assign the preference key and the shortcut view will take care of persistence
self.shortcutView.associatedUserDefaultsKey = kPreferenceGlobalShortcut;
// Execute your block of code automatically when user triggers a shortcut from preferences
[MASShortcut registerGlobalShortcutWithUserDefaultsKey:kPreferenceGlobalShortcut handler:^{
// Execute your block of code automatically when user triggers a shortcut
[MASShortcut addGlobalHotkeyMonitorWithShortcut:shortcut handler:^{
// Let me know if you find a better or more convenient API.
}];