diff options
| author | Dave DeLong | 2013-10-26 10:42:28 -0700 |
|---|---|---|
| committer | Dave DeLong | 2013-10-26 10:42:28 -0700 |
| commit | 6e52b1f4753d8363c4ced6dd779ac92716e49fb0 (patch) | |
| tree | d7300ecc85ec3e0c9e26a538bec865836d6c69ab | |
| parent | bc90e5a3f070877f00e8f7e6fe99880555450284 (diff) | |
| download | DDHotKey-6e52b1f4753d8363c4ced6dd779ac92716e49fb0.tar.bz2 | |
DDHotKey now requires ARC.
It also has a basic "HotKeyTextField" for creating user-defined hotkeys.
| -rw-r--r-- | DDHotKey.xcodeproj/project.pbxproj | 58 | ||||
| -rw-r--r-- | DDHotKeyAppDelegate.h | 7 | ||||
| -rw-r--r-- | DDHotKeyAppDelegate.m | 35 | ||||
| -rw-r--r-- | DDHotKeyCenter.h | 26 | ||||
| -rw-r--r-- | DDHotKeyCenter.m | 193 | ||||
| -rw-r--r-- | DDHotKeyTextField.h | 20 | ||||
| -rw-r--r-- | DDHotKeyTextField.m | 136 | ||||
| -rw-r--r-- | DDHotKeyUtilities.h | 14 | ||||
| -rw-r--r-- | DDHotKeyUtilities.m | 136 | ||||
| -rw-r--r-- | README.markdown | 29 |
10 files changed, 486 insertions, 168 deletions
diff --git a/DDHotKey.xcodeproj/project.pbxproj b/DDHotKey.xcodeproj/project.pbxproj index 05bc2cd..d420d6d 100644 --- a/DDHotKey.xcodeproj/project.pbxproj +++ b/DDHotKey.xcodeproj/project.pbxproj @@ -9,8 +9,10 @@ /* Begin PBXBuildFile section */ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; 256AC3DA0F4B6AC300CF3369 /* DDHotKeyAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* DDHotKeyAppDelegate.m */; }; - 55378E8A1135C08E0038E405 /* DDHotKeyCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 55378E891135C08E0038E405 /* DDHotKeyCenter.m */; }; 55378E8C1135C0A70038E405 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55378E8B1135C0A70038E405 /* Carbon.framework */; }; + 55CD8D52181C222C003799D0 /* DDHotKeyCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 55CD8D4D181C222B003799D0 /* DDHotKeyCenter.m */; }; + 55CD8D53181C222C003799D0 /* DDHotKeyTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 55CD8D4F181C222C003799D0 /* DDHotKeyTextField.m */; }; + 55CD8D54181C222C003799D0 /* DDHotKeyUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 55CD8D51181C222C003799D0 /* DDHotKeyUtilities.m */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; @@ -27,9 +29,13 @@ 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; }; - 55378E881135C08E0038E405 /* DDHotKeyCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDHotKeyCenter.h; sourceTree = "<group>"; }; - 55378E891135C08E0038E405 /* DDHotKeyCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDHotKeyCenter.m; sourceTree = "<group>"; }; 55378E8B1135C0A70038E405 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; + 55CD8D4C181C222B003799D0 /* DDHotKeyCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDHotKeyCenter.h; sourceTree = "<group>"; }; + 55CD8D4D181C222B003799D0 /* DDHotKeyCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDHotKeyCenter.m; sourceTree = "<group>"; }; + 55CD8D4E181C222B003799D0 /* DDHotKeyTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDHotKeyTextField.h; sourceTree = "<group>"; }; + 55CD8D4F181C222C003799D0 /* DDHotKeyTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDHotKeyTextField.m; sourceTree = "<group>"; }; + 55CD8D50181C222C003799D0 /* DDHotKeyUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDHotKeyUtilities.h; sourceTree = "<group>"; }; + 55CD8D51181C222C003799D0 /* DDHotKeyUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDHotKeyUtilities.m; sourceTree = "<group>"; }; 8D1107310486CEB800E47090 /* DDHotKey-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "DDHotKey-Info.plist"; sourceTree = "<group>"; }; 8D1107320486CEB800E47090 /* DDHotKey.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DDHotKey.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -50,8 +56,7 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( - 55378E881135C08E0038E405 /* DDHotKeyCenter.h */, - 55378E891135C08E0038E405 /* DDHotKeyCenter.m */, + 55CD8D55181C2F9E003799D0 /* DDHotKey */, 256AC3D80F4B6AC300CF3369 /* DDHotKeyAppDelegate.h */, 256AC3D90F4B6AC300CF3369 /* DDHotKeyAppDelegate.m */, ); @@ -61,6 +66,7 @@ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( + 55378E8B1135C0A70038E405 /* Carbon.framework */, 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; @@ -92,7 +98,6 @@ 29B97317FDCFA39411CA2CEA /* Resources */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, - 55378E8B1135C0A70038E405 /* Carbon.framework */, ); name = DDHotKey; sourceTree = "<group>"; @@ -125,6 +130,19 @@ name = Frameworks; sourceTree = "<group>"; }; + 55CD8D55181C2F9E003799D0 /* DDHotKey */ = { + isa = PBXGroup; + children = ( + 55CD8D4C181C222B003799D0 /* DDHotKeyCenter.h */, + 55CD8D4D181C222B003799D0 /* DDHotKeyCenter.m */, + 55CD8D4E181C222B003799D0 /* DDHotKeyTextField.h */, + 55CD8D4F181C222C003799D0 /* DDHotKeyTextField.m */, + 55CD8D50181C222C003799D0 /* DDHotKeyUtilities.h */, + 55CD8D51181C222C003799D0 /* DDHotKeyUtilities.m */, + ); + name = DDHotKey; + sourceTree = "<group>"; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -152,7 +170,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0450; + LastUpgradeCheck = 0500; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "DDHotKey" */; compatibilityVersion = "Xcode 3.2"; @@ -191,8 +209,10 @@ buildActionMask = 2147483647; files = ( 8D11072D0486CEB800E47090 /* main.m in Sources */, + 55CD8D52181C222C003799D0 /* DDHotKeyCenter.m in Sources */, + 55CD8D54181C222C003799D0 /* DDHotKeyUtilities.m in Sources */, 256AC3DA0F4B6AC300CF3369 /* DDHotKeyAppDelegate.m in Sources */, - 55378E8A1135C08E0038E405 /* DDHotKeyCenter.m in Sources */, + 55CD8D53181C222C003799D0 /* DDHotKeyTextField.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -253,11 +273,20 @@ C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_OPTIMIZATION_LEVEL = 0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -267,10 +296,19 @@ C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; SDKROOT = macosx; }; diff --git a/DDHotKeyAppDelegate.h b/DDHotKeyAppDelegate.h index 08cac15..e6959d8 100644 --- a/DDHotKeyAppDelegate.h +++ b/DDHotKeyAppDelegate.h @@ -1,7 +1,7 @@ /* DDHotKey -- DDHotKeyAppDelegate.h - Copyright (c) 2010, Dave DeLong <http://www.davedelong.com> + Copyright (c) Dave DeLong <http://www.davedelong.com> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. @@ -10,10 +10,7 @@ #import <Cocoa/Cocoa.h> -@interface DDHotKeyAppDelegate : NSObject /*<NSApplicationDelegate>*/ { - NSWindow *window; - NSTextView *output; -} +@interface DDHotKeyAppDelegate : NSObject /*<NSApplicationDelegate>*/ @property (assign) IBOutlet NSWindow *window; @property (assign) IBOutlet NSTextView *output; diff --git a/DDHotKeyAppDelegate.m b/DDHotKeyAppDelegate.m index 3a38951..119be53 100644 --- a/DDHotKeyAppDelegate.m +++ b/DDHotKeyAppDelegate.m @@ -1,7 +1,7 @@ /* DDHotKey -- DDHotKeyAppDelegate.m - Copyright (c) 2010, Dave DeLong <http://www.davedelong.com> + Copyright (c) Dave DeLong <http://www.davedelong.com> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. @@ -10,13 +10,14 @@ #import "DDHotKeyAppDelegate.h" #import "DDHotKeyCenter.h" +#import <Carbon/Carbon.h> @implementation DDHotKeyAppDelegate @synthesize window, output; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - // Insert code here to initialize your application + // Insert code here to initialize your application } - (void) addOutput:(NSString *)newOutput { @@ -38,69 +39,63 @@ - (IBAction) registerExample1:(id)sender { [self addOutput:@"Attempting to register hotkey for example 1"]; - DDHotKeyCenter * c = [[DDHotKeyCenter alloc] init]; - if (![c registerHotKeyWithKeyCode:9 modifierFlags:NSControlKeyMask target:self action:@selector(hotkeyWithEvent:) object:nil]) { + DDHotKeyCenter *c = [DDHotKeyCenter sharedHotKeyCenter]; + if (![c registerHotKeyWithKeyCode:kVK_ANSI_V modifierFlags:NSControlKeyMask target:self action:@selector(hotkeyWithEvent:) object:nil]) { [self addOutput:@"Unable to register hotkey for example 1"]; } else { [self addOutput:@"Registered hotkey for example 1"]; [self addOutput:[NSString stringWithFormat:@"Registered: %@", [c registeredHotKeys]]]; } - [c release]; } - (IBAction) registerExample2:(id)sender { [self addOutput:@"Attempting to register hotkey for example 2"]; - DDHotKeyCenter * c = [[DDHotKeyCenter alloc] init]; - if (![c registerHotKeyWithKeyCode:9 modifierFlags:(NSControlKeyMask | NSAlternateKeyMask) target:self action:@selector(hotkeyWithEvent:object:) object:@"hello, world!"]) { + DDHotKeyCenter *c = [DDHotKeyCenter sharedHotKeyCenter]; + if (![c registerHotKeyWithKeyCode:kVK_ANSI_V modifierFlags:(NSControlKeyMask | NSAlternateKeyMask) target:self action:@selector(hotkeyWithEvent:object:) object:@"hello, world!"]) { [self addOutput:@"Unable to register hotkey for example 2"]; } else { [self addOutput:@"Registered hotkey for example 2"]; [self addOutput:[NSString stringWithFormat:@"Registered: %@", [c registeredHotKeys]]]; } - [c release]; } - (IBAction) registerExample3:(id)sender { #if NS_BLOCKS_AVAILABLE [self addOutput:@"Attempting to register hotkey for example 3"]; - DDHotKeyCenter * c = [[DDHotKeyCenter alloc] init]; + DDHotKeyCenter *c = [DDHotKeyCenter sharedHotKeyCenter]; int theAnswer = 42; DDHotKeyTask task = ^(NSEvent *hkEvent) { [self addOutput:@"Firing block hotkey"]; [self addOutput:[NSString stringWithFormat:@"Hotkey event: %@", hkEvent]]; [self addOutput:[NSString stringWithFormat:@"the answer is: %d", theAnswer]]; }; - if (![c registerHotKeyWithKeyCode:9 modifierFlags:(NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask) task:task]) { + if (![c registerHotKeyWithKeyCode:kVK_ANSI_V modifierFlags:(NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask) task:task]) { [self addOutput:@"Unable to register hotkey for example 3"]; } else { [self addOutput:@"Registered hotkey for example 3"]; [self addOutput:[NSString stringWithFormat:@"Registered: %@", [c registeredHotKeys]]]; } - [c release]; #else NSRunAlertPanel(@"Blocks not available", @"This example requires the 10.6 SDK", @"OK", nil, nil); #endif } - (IBAction) unregisterExample1:(id)sender { - DDHotKeyCenter * c = [[DDHotKeyCenter alloc] init]; - [c unregisterHotKeyWithKeyCode:9 modifierFlags:NSControlKeyMask]; + DDHotKeyCenter *c = [DDHotKeyCenter sharedHotKeyCenter]; + [c unregisterHotKeyWithKeyCode:kVK_ANSI_V modifierFlags:NSControlKeyMask]; [self addOutput:@"Unregistered hotkey for example 1"]; - [c release]; } - (IBAction) unregisterExample2:(id)sender { - DDHotKeyCenter * c = [[DDHotKeyCenter alloc] init]; - [c unregisterHotKeyWithKeyCode:9 modifierFlags:(NSControlKeyMask | NSAlternateKeyMask)]; + DDHotKeyCenter *c = [DDHotKeyCenter sharedHotKeyCenter]; + [c unregisterHotKeyWithKeyCode:kVK_ANSI_V modifierFlags:(NSControlKeyMask | NSAlternateKeyMask)]; [self addOutput:@"Unregistered hotkey for example 2"]; - [c release]; } - (IBAction) unregisterExample3:(id)sender { - DDHotKeyCenter * c = [[DDHotKeyCenter alloc] init]; - [c unregisterHotKeyWithKeyCode:9 modifierFlags:(NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask)]; + DDHotKeyCenter *c = [DDHotKeyCenter sharedHotKeyCenter]; + [c unregisterHotKeyWithKeyCode:kVK_ANSI_V modifierFlags:(NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask)]; [self addOutput:@"Unregistered hotkey for example 3"]; - [c release]; } @end diff --git a/DDHotKeyCenter.h b/DDHotKeyCenter.h index 58dc9d2..6f79bbb 100644 --- a/DDHotKeyCenter.h +++ b/DDHotKeyCenter.h @@ -1,7 +1,7 @@ /* DDHotKey -- DDHotKeyCenter.h - Copyright (c) 2012, Dave DeLong <http://www.davedelong.com> + Copyright (c) Dave DeLong <http://www.davedelong.com> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. @@ -15,10 +15,13 @@ typedef void (^DDHotKeyTask)(NSEvent*); @interface DDHotKey : NSObject -@property (nonatomic, readonly) id target; +// creates a new hotkey but does not register it ++ (instancetype)hotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task; + +@property (nonatomic, assign, readonly) id target; @property (nonatomic, readonly) SEL action; -@property (nonatomic, readonly) id object; -@property (nonatomic, readonly) DDHotKeyTask task; +@property (nonatomic, strong, readonly) id object; +@property (nonatomic, copy, readonly) DDHotKeyTask task; @property (nonatomic, readonly) unsigned short keyCode; @property (nonatomic, readonly) NSUInteger modifierFlags; @@ -29,21 +32,26 @@ typedef void (^DDHotKeyTask)(NSEvent*); @interface DDHotKeyCenter : NSObject -+ (id)sharedHotKeyCenter; ++ (instancetype)sharedHotKeyCenter; + +/** + Register a hotkey. + */ +- (DDHotKey *)registerHotKey:(DDHotKey *)hotKey; /** Register a target/action hotkey. The modifierFlags must be a bitwise OR of NSCommandKeyMask, NSAlternateKeyMask, NSControlKeyMask, or NSShiftKeyMask; - Returns YES if the hotkey was registered; NO otherwise. + Returns the hotkey registered. If registration failed, returns nil. */ -- (BOOL)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object; +- (DDHotKey *)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object; /** Register a block callback hotkey. The modifierFlags must be a bitwise OR of NSCommandKeyMask, NSAlternateKeyMask, NSControlKeyMask, or NSShiftKeyMask; - Returns YES if the hotkey was registered; NO otherwise. + Returns the hotkey registered. If registration failed, returns nil. */ -- (BOOL)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task; +- (DDHotKey *)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task; /** See if a hotkey exists with the specified keycode and modifier flags. diff --git a/DDHotKeyCenter.m b/DDHotKeyCenter.m index c8bbc7f..e2d5997 100644 --- a/DDHotKeyCenter.m +++ b/DDHotKeyCenter.m @@ -1,37 +1,22 @@ /* DDHotKey -- DDHotKeyCenter.m - Copyright (c) 2012, Dave DeLong <http://www.davedelong.com> + Copyright (c) Dave DeLong <http://www.davedelong.com> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the author(s) or copyright holder(s) be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. */ -#import "DDHotKeyCenter.h" #import <Carbon/Carbon.h> #import <objc/runtime.h> -#if __has_feature(objc_arc) - -#define DDHK_HAS_ARC 1 -#define DDHK_RETAIN(_o) (_o) -#define DDHK_RELEASE(_o) -#define DDHK_AUTORELEASE(_o) (_o) - -#else - -#define DDHK_HAS_ARC 0 -#define DDHK_RETAIN(_o) [(_o) retain] -#define DDHK_RELEASE(_o) [(_o) release] -#define DDHK_AUTORELEASE(_o) [(_o) autorelease] - -#endif +#import "DDHotKeyCenter.h" +#import "DDHotKeyUtilities.h" #pragma mark Private Global Declarations OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData); -UInt32 dd_translateModifierFlags(NSUInteger flags); #pragma mark DDHotKey @@ -40,58 +25,29 @@ UInt32 dd_translateModifierFlags(NSUInteger flags); @property (nonatomic, retain) NSValue *hotKeyRef; @property (nonatomic) UInt32 hotKeyID; -@end -@implementation DDHotKey { - id _target; - SEL _action; - id _object; - - unsigned short _keyCode; - NSUInteger _modifierFlags; - DDHotKeyTask _task; -} +@property (nonatomic, assign, setter = _setTarget:) id target; +@property (nonatomic, setter = _setAction:) SEL action; +@property (nonatomic, strong, setter = _setObject:) id object; +@property (nonatomic, copy, setter = _setTask:) DDHotKeyTask task; -- (void) dealloc { - [[DDHotKeyCenter sharedHotKeyCenter] unregisterHotKey:self]; -#if !DDHK_HAS_ARC - DDHK_RELEASE(_target); _target = nil; - DDHK_RELEASE(_object); _object = nil; - DDHK_RELEASE(_hotKeyRef); _hotKeyRef = nil; - DDHK_RELEASE(_task); _task = nil; - [super dealloc]; -#endif -} +@property (nonatomic, setter = _setKeyCode:) unsigned short keyCode; +@property (nonatomic, setter = _setModifierFlags:) NSUInteger modifierFlags; -- (void)_setTarget:(id)target { - if (target != _target) { - DDHK_RELEASE(_target); - _target = DDHK_RETAIN(target); - } -} - -- (void)_setAction:(SEL)action { - _action = action; -} - -- (void)_setObject:(id)object { - if (object != _object) { - DDHK_RELEASE(_object); - _object = DDHK_RETAIN(object); - } -} +@end -- (void)_setKeyCode:(unsigned short)keyCode { - _keyCode = keyCode; -} +@implementation DDHotKey -- (void)_setModifierFlags:(NSUInteger)modifierFlags { - _modifierFlags = modifierFlags; ++ (instancetype)hotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task { + DDHotKey *newHotKey = [[self alloc] init]; + [newHotKey _setTask:task]; + [newHotKey _setKeyCode:keyCode]; + [newHotKey _setModifierFlags:flags]; + return newHotKey; } -- (void)_setTask:(DDHotKeyTask)task { - DDHK_RELEASE(_task); - _task = [task copy]; +- (void) dealloc { + [[DDHotKeyCenter sharedHotKeyCenter] unregisterHotKey:self]; } - (NSUInteger)hash { @@ -133,71 +89,74 @@ UInt32 dd_translateModifierFlags(NSUInteger flags); } } -- (NSString *)actionString { - return NSStringFromSelector(_action); -} - @end #pragma mark DDHotKeyCenter -static DDHotKeyCenter *center = nil; +static DDHotKeyCenter *sharedHotKeyCenter = nil; @implementation DDHotKeyCenter { NSMutableSet *_registeredHotKeys; UInt32 _nextHotKeyID; } -+ (id)sharedHotKeyCenter { ++ (instancetype)sharedHotKeyCenter { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - center = [[DDHotKeyCenter _alloc] _init]; + sharedHotKeyCenter = [super allocWithZone:nil]; + sharedHotKeyCenter = [sharedHotKeyCenter init]; + + EventTypeSpec eventSpec; + eventSpec.eventClass = kEventClassKeyboard; + eventSpec.eventKind = kEventHotKeyReleased; + InstallApplicationEventHandler(&dd_hotKeyHandler, 1, &eventSpec, NULL, NULL); }); - return center; -} - -+ (id)_alloc { - return [super allocWithZone:nil]; + return sharedHotKeyCenter; } + (id)allocWithZone:(NSZone *)zone { - return DDHK_RETAIN([self sharedHotKeyCenter]); + return sharedHotKeyCenter; } -- (id)_init { +- (id)init { + if (self != sharedHotKeyCenter) { return sharedHotKeyCenter; } + self = [super init]; if (self) { _registeredHotKeys = [[NSMutableSet alloc] init]; _nextHotKeyID = 1; - - EventTypeSpec eventSpec; - eventSpec.eventClass = kEventClassKeyboard; - eventSpec.eventKind = kEventHotKeyReleased; - InstallApplicationEventHandler(&dd_hotKeyHandler, 1, &eventSpec, NULL, NULL); } return self; } -- (NSSet *)hotKeysMatchingPredicate:(NSPredicate *)predicate { +- (NSSet *)hotKeysMatching:(BOOL(^)(DDHotKey *hotkey))matcher { + NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { + return matcher(evaluatedObject); + }]; return [_registeredHotKeys filteredSetUsingPredicate:predicate]; } - (BOOL)hasRegisteredHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags]; - return ([[self hotKeysMatchingPredicate:predicate] count] > 0); + return [self hotKeysMatching:^BOOL(DDHotKey *hotkey) { + return hotkey.keyCode == keyCode && hotkey.modifierFlags == flags; + }].count > 0; } -- (BOOL)_registerHotKey:(DDHotKey *)hotKey { +- (DDHotKey *)_registerHotKey:(DDHotKey *)hotKey { + if ([_registeredHotKeys containsObject:hotKey]) { + return hotKey; + } + EventHotKeyID keyID; keyID.signature = 'htk1'; keyID.id = _nextHotKeyID; EventHotKeyRef carbonHotKey; - UInt32 flags = dd_translateModifierFlags([hotKey modifierFlags]); + UInt32 flags = DDCarbonModifierFlagsFromCocoaModifiers([hotKey modifierFlags]); OSStatus err = RegisterEventHotKey([hotKey keyCode], flags, keyID, GetEventDispatcherTarget(), 0, &carbonHotKey); //error registering hot key - if (err != 0) { return NO; } + if (err != 0) { return nil; } NSValue *refValue = [NSValue valueWithPointer:carbonHotKey]; [hotKey setHotKeyRef:refValue]; @@ -206,7 +165,11 @@ static DDHotKeyCenter *center = nil; _nextHotKeyID++; [_registeredHotKeys addObject:hotKey]; - return YES; + return hotKey; +} + +- (DDHotKey *)registerHotKey:(DDHotKey *)hotKey { + return [self _registerHotKey:hotKey]; } - (void)unregisterHotKey:(DDHotKey *)hotKey { @@ -215,16 +178,16 @@ static DDHotKeyCenter *center = nil; EventHotKeyRef carbonHotKey = (EventHotKeyRef)[hotKeyRef pointerValue]; UnregisterEventHotKey(carbonHotKey); [hotKey setHotKeyRef:nil]; - - [_registeredHotKeys removeObject:hotKey]; } + + [_registeredHotKeys removeObject:hotKey]; } -- (BOOL)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task { +- (DDHotKey *)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task { //we can't add a new hotkey if something already has this combo if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; } - DDHotKey *newHotKey = DDHK_AUTORELEASE([[DDHotKey alloc] init]); + DDHotKey *newHotKey = [[DDHotKey alloc] init]; [newHotKey _setTask:task]; [newHotKey _setKeyCode:keyCode]; [newHotKey _setModifierFlags:flags]; @@ -232,12 +195,12 @@ static DDHotKeyCenter *center = nil; return [self _registerHotKey:newHotKey]; } -- (BOOL)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object { +- (DDHotKey *)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object { //we can't add a new hotkey if something already has this combo if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; } //build the hotkey object: - DDHotKey *newHotKey = DDHK_AUTORELEASE([[DDHotKey alloc] init]); + DDHotKey *newHotKey = [[DDHotKey alloc] init]; [newHotKey _setTarget:target]; [newHotKey _setAction:action]; [newHotKey _setObject:object]; @@ -246,10 +209,10 @@ static DDHotKeyCenter *center = nil; return [self _registerHotKey:newHotKey]; } -- (void)unregisterHotKeysMatchingPredicate:(NSPredicate *)predicate { +- (void)unregisterHotKeysMatching:(BOOL(^)(DDHotKey *hotkey))matcher { //explicitly unregister the hotkey, since relying on the unregistration in -dealloc can be problematic @autoreleasepool { - NSSet *matches = [self hotKeysMatchingPredicate:predicate]; + NSSet *matches = [self hotKeysMatching:matcher]; for (DDHotKey *hotKey in matches) { [self unregisterHotKey:hotKey]; } @@ -257,18 +220,21 @@ static DDHotKeyCenter *center = nil; } - (void)unregisterHotKeysWithTarget:(id)target { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"target = %@", target]; - [self unregisterHotKeysMatchingPredicate:predicate]; + [self unregisterHotKeysMatching:^BOOL(DDHotKey *hotkey) { + return hotkey.target == target; + }]; } - (void)unregisterHotKeysWithTarget:(id)target action:(SEL)action { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"target = %@ AND actionString = %@", target, NSStringFromSelector(action)]; - [self unregisterHotKeysMatchingPredicate:predicate]; + [self unregisterHotKeysMatching:^BOOL(DDHotKey *hotkey) { + return hotkey.target == target && sel_isEqual(hotkey.action, action); + }]; } - (void)unregisterHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags]; - [self unregisterHotKeysMatchingPredicate:predicate]; + [self unregisterHotKeysMatching:^BOOL(DDHotKey *hotkey) { + return hotkey.keyCode == keyCode && hotkey.modifierFlags == flags; + }]; } - (void)unregisterAllHotKeys { @@ -276,11 +242,12 @@ static DDHotKeyCenter *center = nil; for (DDHotKey *key in keys) { [self unregisterHotKey:key]; } - DDHK_RELEASE(keys); } - (NSSet *)registeredHotKeys { - return [self hotKeysMatchingPredicate:[NSPredicate predicateWithFormat:@"hotKeyRef != NULL"]]; + return [self hotKeysMatching:^BOOL(DDHotKey *hotkey) { + return hotkey.hotKeyRef != NULL; + }]; } @end @@ -288,12 +255,13 @@ static DDHotKeyCenter *center = nil; OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) { @autoreleasepool { EventHotKeyID hotKeyID; - GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID),NULL,&hotKeyID); + GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID), NULL, &hotKeyID); UInt32 keyID = hotKeyID.id; - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"hotKeyID = %u", keyID]; - NSSet *matchingHotKeys = [[DDHotKeyCenter sharedHotKeyCenter] hotKeysMatchingPredicate:predicate]; + NSSet *matchingHotKeys = [[DDHotKeyCenter sharedHotKeyCenter] hotKeysMatching:^BOOL(DDHotKey *hotkey) { + return hotkey.hotKeyID == keyID; + }]; if ([matchingHotKeys count] > 1) { NSLog(@"ERROR!"); } DDHotKey *matchingHotKey = [matchingHotKeys anyObject]; @@ -314,12 +282,3 @@ OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, vo return noErr; } - -UInt32 dd_translateModifierFlags(NSUInteger flags) { - UInt32 newFlags = 0; - if ((flags & NSControlKeyMask) > 0) { newFlags |= controlKey; } - if ((flags & NSCommandKeyMask) > 0) { newFlags |= cmdKey; } - if ((flags & NSShiftKeyMask) > 0) { newFlags |= shiftKey; } - if ((flags & NSAlternateKeyMask) > 0) { newFlags |= optionKey; } - return newFlags; -} diff --git a/DDHotKeyTextField.h b/DDHotKeyTextField.h new file mode 100644 index 0000000..c399d62 --- /dev/null +++ b/DDHotKeyTextField.h @@ -0,0 +1,20 @@ +/* + DDHotKey -- DDHotKeyTextField.h + + Copyright (c) Dave DeLong <http://www.davedelong.com> + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + + The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the author(s) or copyright holder(s) be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. + */ + +#import <Foundation/Foundation.h> +#import "DDHotKeyCenter.h" + +@interface DDHotKeyTextField : NSTextField + +@property (nonatomic, strong) DDHotKey *hotKey; + +@end + +@interface DDHotKeyTextFieldCell : NSTextFieldCell @end
\ No newline at end of file diff --git a/DDHotKeyTextField.m b/DDHotKeyTextField.m new file mode 100644 index 0000000..7305e17 --- /dev/null +++ b/DDHotKeyTextField.m @@ -0,0 +1,136 @@ +/* + DDHotKey -- DDHotKeyTextField.m + + Copyright (c) Dave DeLong <http://www.davedelong.com> + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + + The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the author(s) or copyright holder(s) be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. + */ + +#import <Carbon/Carbon.h> + +#import "DDHotKeyTextField.h" +#import "DDHotKeyUtilities.h" + +@interface DDHotKeyTextFieldEditor : NSTextView + +@property (nonatomic, weak) DDHotKeyTextField *hotKeyField; + +@end + +static DDHotKeyTextFieldEditor *DDFieldEditor(void); +static DDHotKeyTextFieldEditor *DDFieldEditor(void) { + static DDHotKeyTextFieldEditor *editor; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + editor = [[DDHotKeyTextFieldEditor alloc] initWithFrame:NSMakeRect(0, 0, 100, 32)]; + [editor setFieldEditor:YES]; + }); + return editor; +} + +@implementation DDHotKeyTextFieldCell + +- (NSTextView *)fieldEditorForView:(NSView *)view { + if ([view isKindOfClass:[DDHotKeyTextField class]]) { + DDHotKeyTextFieldEditor *editor = DDFieldEditor(); + editor.insertionPointColor = editor.backgroundColor; + editor.hotKeyField = (DDHotKeyTextField *)view; + return editor; + } + return nil; +} + +@end + +@implementation DDHotKeyTextField + ++ (Class)cellClass { + return [DDHotKeyTextFieldCell class]; +} + +- (void)setHotKey:(DDHotKey *)hotKey { + if (_hotKey != hotKey) { + _hotKey = hotKey; + [super setStringValue:[DDStringFromKeyCode(hotKey.keyCode, hotKey.modifierFlags) uppercaseString]]; + } +} + +- (void)setStringValue:(NSString *)aString { + NSLog(@"-[DDHotKeyTextField setStringValue:] is not what you want. Use -[DDHotKeyTextField setHotKey:] instead."); + [super setStringValue:aString]; +} + +- (NSString *)stringValue { + NSLog(@"-[DDHotKeyTextField stringValue] is not what you want. Use -[DDHotKeyTextField hotKey] instead."); + return [super stringValue]; +} + +@end + +@implementation DDHotKeyTextFieldEditor { + BOOL _hasSeenKeyDown; + id _globalMonitor; + DDHotKey *_originalHotKey; +} + +- (void)setHotKeyField:(DDHotKeyTextField *)hotKeyField { + _hotKeyField = hotKeyField; + _originalHotKey = _hotKeyField.hotKey; +} + +- (void)processHotkeyEvent:(NSEvent *)event { + NSUInteger flags = event.modifierFlags; + BOOL hasModifier = (flags & (NSCommandKeyMask | NSAlternateKeyMask | NSControlKeyMask | NSShiftKeyMask | NSFunctionKeyMask)) > 0; + + if (event.type == NSKeyDown) { + _hasSeenKeyDown = YES; + unichar character = [event.charactersIgnoringModifiers characterAtIndex:0]; + + + if (hasModifier == NO && ([[NSCharacterSet newlineCharacterSet] characterIsMember:character] || event.keyCode == kVK_Escape)) { + if (event.keyCode == kVK_Escape) { + self.hotKeyField.hotKey = _originalHotKey; + + NSString *str = DDStringFromKeyCode(_originalHotKey.keyCode, _originalHotKey.modifierFlags); + self.textStorage.mutableString.string = [str uppercaseString]; + } + [self.window makeFirstResponder:nil]; + return; + } + } + + if ((event.type == NSKeyDown || (event.type == NSFlagsChanged && _hasSeenKeyDown == NO)) && hasModifier) { + self.hotKeyField.hotKey = [DDHotKey hotKeyWithKeyCode:event.keyCode modifierFlags:flags task:_originalHotKey.task]; + NSString *str = DDStringFromKeyCode(event.keyCode, flags); + [self.textStorage.mutableString setString:[str uppercaseString]]; + } +} + +- (BOOL)becomeFirstResponder { + BOOL ok = [super becomeFirstResponder]; + if (ok) { + _hasSeenKeyDown = NO; + _globalMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyDownMask | NSFlagsChangedMask) handler:^NSEvent*(NSEvent *event){ + [self processHotkeyEvent:event]; + return nil; + }]; + } + return ok; +} + +- (BOOL)resignFirstResponder { + BOOL ok = [super resignFirstResponder]; + if (ok) { + self.hotKeyField = nil; + if (_globalMonitor) { + [NSEvent removeMonitor:_globalMonitor]; + _globalMonitor = nil; + } + } + + return ok; +} + +@end diff --git a/DDHotKeyUtilities.h b/DDHotKeyUtilities.h new file mode 100644 index 0000000..54b25a4 --- /dev/null +++ b/DDHotKeyUtilities.h @@ -0,0 +1,14 @@ +/* + DDHotKey -- DDHotKeyUtilities.h + + Copyright (c) Dave DeLong <http://www.davedelong.com> + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + + The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the author(s) or copyright holder(s) be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. + */ + +#import <Foundation/Foundation.h> + +extern NSString *DDStringFromKeyCode(unsigned short keyCode, NSUInteger modifiers); +extern UInt32 DDCarbonModifierFlagsFromCocoaModifiers(NSUInteger flags); diff --git a/DDHotKeyUtilities.m b/DDHotKeyUtilities.m new file mode 100644 index 0000000..4c44ac1 --- /dev/null +++ b/DDHotKeyUtilities.m @@ -0,0 +1,136 @@ +/* + DDHotKey -- DDHotKeyUtilities.m + + Copyright (c) Dave DeLong <http://www.davedelong.com> + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + + The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the author(s) or copyright holder(s) be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. + */ + +#import "DDHotKeyUtilities.h" +#import <Carbon/Carbon.h> + +static NSDictionary *_DDKeyCodeToCharacterMap(void); +static NSDictionary *_DDKeyCodeToCharacterMap(void) { + static NSDictionary *keyCodeMap = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + keyCodeMap = @{ + @(kVK_Return) : @"↩", + @(kVK_Tab) : @"⇥", + @(kVK_Space) : @"⎵", + @(kVK_Delete) : @"⌫", + @(kVK_Escape) : @"⎋", + @(kVK_Command) : @"⌘", + @(kVK_Shift) : @"⇧", + @(kVK_CapsLock) : @"⇪", + @(kVK_Option) : @"⌥", + @(kVK_Control) : @"⌃", + @(kVK_RightShift) : @"⇧", + @(kVK_RightOption) : @"⌥", + @(kVK_RightControl) : @"⌃", + @(kVK_VolumeUp) : @"🔊", + @(kVK_VolumeDown) : @"🔈", + @(kVK_Mute) : @"🔇", + @(kVK_Function) : @"\u2318", + @(kVK_F1) : @"F1", + @(kVK_F2) : @"F2", + @(kVK_F3) : @"F3", + @(kVK_F4) : @"F4", + @(kVK_F5) : @"F5", + @(kVK_F6) : @"F6", + @(kVK_F7) : @"F7", + @(kVK_F8) : @"F8", + @(kVK_F9) : @"F9", + @(kVK_F10) : @"F10", + @(kVK_F11) : @"F11", + @(kVK_F12) : @"F12", + @(kVK_F13) : @"F13", + @(kVK_F14) : @"F14", + @(kVK_F15) : @"F15", + @(kVK_F16) : @"F16", + @(kVK_F17) : @"F17", + @(kVK_F18) : @"F18", + @(kVK_F19) : @"F19", + @(kVK_F20) : @"F20", + // @(kVK_Help) : @"", + @(kVK_ForwardDelete) : @"⌦", + @(kVK_Home) : @"↖", + @(kVK_End) : @"↘", + @(kVK_PageUp) : @"⇞", + @(kVK_PageDown) : @"⇟", + @(kVK_LeftArrow) : @"←", + @(kVK_RightArrow) : @"→", + @(kVK_DownArrow) : @"↓", + @(kVK_UpArrow) : @"↑", + }; + }); + return keyCodeMap; +} + +NSString *DDStringFromKeyCode(unsigned short keyCode, NSUInteger modifiers) { + NSMutableString *final = [NSMutableString stringWithString:@""]; + NSDictionary *characterMap = _DDKeyCodeToCharacterMap(); + + if (modifiers & NSControlKeyMask) { + [final appendString:[characterMap objectForKey:@(kVK_Control)]]; + } + if (modifiers & NSAlternateKeyMask) { + [final appendString:[characterMap objectForKey:@(kVK_Option)]]; + } + if (modifiers & NSShiftKeyMask) { + [final appendString:[characterMap objectForKey:@(kVK_Shift)]]; + } + if (modifiers & NSCommandKeyMask) { + [final appendString:[characterMap objectForKey:@(kVK_Command)]]; + } + + if (keyCode == kVK_Control || keyCode == kVK_Option || keyCode == kVK_Shift || keyCode == kVK_Command) { + return final; + } + + NSString *mapped = [characterMap objectForKey:@(keyCode)]; + if (mapped != nil) { + [final appendString:mapped]; + } else { + + TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); + CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); + const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr); + + if (keyboardLayout) { + UInt32 deadKeyState = 0; + UniCharCount maxStringLength = 255; + UniCharCount actualStringLength = 0; + UniChar unicodeString[maxStringLength]; + + UInt32 keyModifiers = DDCarbonModifierFlagsFromCocoaModifiers(modifiers); + + OSStatus status = UCKeyTranslate(keyboardLayout, + keyCode, kUCKeyActionDown, keyModifiers, + LMGetKbdType(), 0, + &deadKeyState, + maxStringLength, + &actualStringLength, unicodeString); + + if (actualStringLength > 0 && status == noErr) { + NSString *characterString = [NSString stringWithCharacters:unicodeString length:(NSUInteger)actualStringLength]; + + [final appendString:characterString]; + } + } + } + + return final; +} + +UInt32 DDCarbonModifierFlagsFromCocoaModifiers(NSUInteger flags) { + UInt32 newFlags = 0; + if ((flags & NSControlKeyMask) > 0) { newFlags |= controlKey; } + if ((flags & NSCommandKeyMask) > 0) { newFlags |= cmdKey; } + if ((flags & NSShiftKeyMask) > 0) { newFlags |= shiftKey; } + if ((flags & NSAlternateKeyMask) > 0) { newFlags |= optionKey; } + if ((flags & NSAlphaShiftKeyMask) > 0) { newFlags |= alphaLock; } + return newFlags; +} diff --git a/README.markdown b/README.markdown index 767885f..26a6b85 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,6 @@ # DDHotKey -Copyright © 2012, Dave DeLong <http://www.davedelong.com> +Copyright © Dave DeLong <http://www.davedelong.com> ## About @@ -14,23 +14,36 @@ The license for this framework is included in every source file, and is repoduce Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. -## How to use +## Usage -Your application will need to link against `Carbon.framework`, and you will need to compile your application with the Clang compiler. +### Including DDHotKey in your project + +You will need to copy these six files into your project: + +- DDHotKeyCenter.h +- DDHotKeyCenter.m +- DDHotKeyUtilities.h +- DDHotKeyUtilities.m +- DDHotKeyTextField.h +- DDHotKeyTextField.m + +Your application will need to link against `Carbon.framework`, and you will need to compile your application with the Clang compiler. DDHotKey has been tested with Xcode 5 on OS X Mavericks. No attempt has been made to preserve backwards compatibility. + +### Using DDHotKey in your code When you wish to create a hotkey, you'll need to do so via the `DDHotKeyCenter` singleton. -You can register a hotkey in one of two ways: via a target/action mechanism, or with a block. The target/action mechanism can take a single extra "object" parameter, which it will pass into the action when the hotkey is fired. Both the `target` and the `object` parameters are retained by the `DDHotKeyCenter`. In addition, an `NSEvent` object is passed, which contains information regarding the hotkey event (such as the location, the keyCode, the modifierFlags, etc). +You can register a hotkey in one of two ways: via a target/action mechanism, or with a block. The target/action mechanism can take a single extra "object" parameter, which it will pass into the action when the hotkey is fired. Only the `object` parameter is retained by the `DDHotKeyCenter`. In addition, an `NSEvent` object is passed, which contains information regarding the hotkey event (such as the location, the keyCode, the modifierFlags, etc). Hotkey actions must have one of two method signatures (the actual selector is irrelevant): //a method with a single NSEvent parameter - - (void) hotkeyAction:(NSEvent*)hotKeyEvent; + - (void)hotkeyAction:(NSEvent*)hotKeyEvent; OR //a method with an NSEvent parameter and an object parameter - - (void) hotkeyAction:(NSEvent*)hotKeyEvent withObject:(id)anObject; + - (void)hotkeyAction:(NSEvent*)hotKeyEvent withObject:(id)anObject; The other way to register a hotkey is with a block callback. The block must have the following signature: @@ -38,4 +51,6 @@ The other way to register a hotkey is with a block callback. The block must hav `DDHotKeyCenter.h` contains a typedef statement to typedef this signature as a `DDHotKeyTask`, for convenience. -Finally, you can unregister a hotkey based on its target, its target and action, or its keycode and modifier flags.
\ No newline at end of file +Any hotkey that you have registered via `DDHotKeyCenter` can be unregistered based on its target, its target and action, or its keycode and modifier flags. + +DDHotKey also includes a rudimentary `DDHotKeyTextField`, which is an `NSTextField` subclass that simplifies the process of creating a key combination. Simply drop an `NSTextField` into your xib and change its class to `DDHotKeyTextField`. Programmatically, you'll get an NSTextField into which you can type arbitrary key combinations. You access the resulting combination via the textfield's `hotKey` property.
\ No newline at end of file |
