cog/ThirdParty/NDHotKeys/NDHotKeyEvent.m

1154 lines
32 KiB
Objective-C
Executable File

/*
* NDHotKeyEvent.m
* NDHotKeyEvent
*
* Created by Nathan Day on Wed Feb 26 2003.
* Copyright (c) 2002 Nathan Day. All rights reserved.
*/
#import "NDHotKeyEvent.h"
#import <objc/runtime.h>
@interface NDHotKeyEvent (Private)
+ (NSMapTable *)allHotKeyEvents;
- (BOOL)addHotKey;
- (void)removeHotKey;
- (BOOL)setCollectiveEnabled:(BOOL)aFlag;
- (BOOL)collectiveEnable;
@end
static NSString * kArchivingKeyCodeKey = @"KeyCodeKey",
* kArchivingCharacterKey = @"CharacterKey",
* kArchivingModifierFlagsKey = @"ModifierFlagsKey",
* kArchivingSelectorReleasedCodeKey = @"SelectorReleasedCodeKey",
* kArchivingSelectorPressedCodeKey = @"SelectorPressedCodeKey";
const OSType NDHotKeyDefaultSignature = 'NDHK';
static OSStatus switchHotKey( NDHotKeyEvent * self, BOOL aFlag );
unichar unicodeForFunctionKey( UInt32 aKeyCode );
/*
* class implementation NDHotKeyEvent
*/
@implementation NDHotKeyEvent
#ifdef NDHotKeyEventThreadSafe
static NSLock * hotKeysLock = NULL;
#warning Thread saftey has been enabled for NDHotKeyEvent class methods
#define NDHotKeyEventLock [hotKeysLock lock]
#define NDHotKeyEventUnlock [hotKeysLock unlock]
#else
#warning The NDHotKeyEvent class methods are NOT thread safe
#define NDHotKeyEventLock // lock
#define NDHotKeyEventUnlock // unlock
#endif
static NSMapTable * allHotKeyEvents = NULL;
static BOOL isInstalled = NO;
static OSType signature = 0;
unsigned int cocoaModifierFlagsToCarbonModifierFlags( unsigned int aModifierFlags );
pascal OSErr eventHandlerCallback( EventHandlerCallRef anInHandlerCallRef, EventRef anInEvent, void * self );
NSUInteger hashValueHashFunction( NSMapTable * aTable, const void * aHotKeyEntry );
BOOL isEqualHashFunction( NSMapTable * aTable, const void * aFirstHotKeyEvent, const void * aSecondHotKeyEvent );
NSString * describeHashFunction( NSMapTable * aTable, const void * aHotKeyEvent );
NSUInteger hashKeyHashFunction( NSMapTable * aTable, const void * aNumber );
BOOL isEqualKeyHashFunction( NSMapTable * aTable, const void * aFirstNumber, const void * aSecondNumber );
void hashKeyRetainFunction( NSMapTable * aTable, const void * aNumber );
void hashKeyReleaseFunction( NSMapTable * aTable, void * aNumber );
struct HotKeyMappingEntry
{
unsigned short keyCode;
unsigned int modifierFlags;
NDHotKeyEvent * hotKeyEvent;
};
/*
* +install
*/
+ (BOOL)install
{
if( isInstalled == NO )
{
NSMapTable * theHotKeyEvents = [self allHotKeyEvents];
EventTypeSpec theTypeSpec[] =
{
{ kEventClassKeyboard, kEventHotKeyPressed },
{ kEventClassKeyboard, kEventHotKeyReleased }
};
NDHotKeyEventLock;
if( theHotKeyEvents != nil && isInstalled == NO )
{
if( InstallEventHandler( GetEventDispatcherTarget(), NewEventHandlerUPP((EventHandlerProcPtr)eventHandlerCallback), 2, theTypeSpec, theHotKeyEvents, nil ) == noErr )
{
isInstalled = YES;
}
else
{
NSLog(@"Could not install Event handler");
}
}
NDHotKeyEventUnlock;
}
return isInstalled;
}
#ifdef NDHotKeyEventThreadSafe
/*
* +initialize:
*/
+ (void)initialize
{
while( hotKeysLock == nil )
{
NSLock * theInstance = [[NSLock alloc] init];
if( !CompareAndSwap( nil, (unsigned long int)theInstance, (unsigned long int*)&hotKeysLock) )
[theInstance release]; // did not use instance
}
}
#endif
/*
* +setSignature:
*/
+ (void)setSignature:(OSType)aSignature
{
NSAssert( signature == 0 || aSignature == signature, @"The signature used by NDHotKeyEvent can only be set once safely" );
signature = aSignature;
}
/*
* +signature
*/
+ (OSType)signature
{
signature = signature ? signature : NDHotKeyDefaultSignature;
return signature;
}
/*
* +setAllEnabled:
*/
+ (BOOL)setAllEnabled:(BOOL)aFlag
{
BOOL theAllSucceeded = YES;
NSMapTable * theMapTable = [NDHotKeyEvent allHotKeyEvents];
/*
* need to install before to make sure the method 'setCollectiveEnabled:'
* doesn't try install since install tries to aquire the lock 'hotKeysLock'
*/
if( theMapTable && [NDHotKeyEvent install] )
{
NSMapEnumerator theEnumerator;
NSUInteger theKey;
struct HotKeyMappingEntry * theHotKeyMapEntry;
NDHotKeyEventLock;
theEnumerator = NSEnumerateMapTable(theMapTable);
while ( NSNextMapEnumeratorPair(&theEnumerator, (void**)&theKey, (void**)&theHotKeyMapEntry) )
{
if( ![theHotKeyMapEntry->hotKeyEvent setCollectiveEnabled:aFlag] )
theAllSucceeded = NO;
}
NDHotKeyEventUnlock;
}
return theAllSucceeded;
}
/*
* +isEnabledKeyCode:modifierFlags:
*/
+ (BOOL)isEnabledKeyCode:(unsigned short)aKeyCode modifierFlags:(unsigned int)aModifierFlags
{
return [[self findHotKeyForKeyCode:aKeyCode modifierFlags:aModifierFlags] isEnabled];
}
/*
* +getHotKeyForKeyCode:character:modifierFlags:
*/
+ (NDHotKeyEvent *)getHotKeyForKeyCode:(unsigned short)aKeyCode character:(unichar)aChar modifierFlags:(unsigned int)aModifierFlags
{
NDHotKeyEvent * theHotKey = nil;
theHotKey = [self findHotKeyForKeyCode:aKeyCode modifierFlags:aModifierFlags];
return theHotKey ? theHotKey : [self hotKeyWithKeyCode:aKeyCode character:aChar modifierFlags:aModifierFlags];
}
/*
* +findHotKeyForKeyCode:modifierFlags:
*/
+ (NDHotKeyEvent *)findHotKeyForKeyCode:(unsigned short)aKeyCode modifierFlags:(unsigned int)aModifierFlags
{
struct HotKeyMappingEntry * theFoundEntry = NULL;
NSMapTable * theMapTable = [NDHotKeyEvent allHotKeyEvents];
if( theMapTable )
{
NDHotKeyEventLock;
theFoundEntry = (struct HotKeyMappingEntry *) NSMapGet( theMapTable, (void*)(NSUInteger)(aKeyCode ^ aModifierFlags) );
if( theFoundEntry != NULL )
[[theFoundEntry->hotKeyEvent retain] autorelease];
NDHotKeyEventUnlock;
}
return (theFoundEntry) ? theFoundEntry->hotKeyEvent : nil;
}
/*
* +hotKeyWithKeyCode:character:modifierFlags:
*/
+ (id)hotKeyWithKeyCode:(unsigned short)aKeyCode character:(unichar)aChar modifierFlags:(unsigned int)aModifierFlags
{
return [self hotKeyWithKeyCode:aKeyCode character:aChar modifierFlags:aModifierFlags target:nil selector:NULL];
}
/*
* +hotKeyWithKeyCode:character:modifierFlags:target:selector:
*/
+ (id)hotKeyWithKeyCode:(unsigned short)aKeyCode character:(unichar)aChar modifierFlags:(unsigned int)aModifierFlags target:(id)aTarget selector:(SEL)aSelector
{
return [[[self alloc] initWithKeyCode:aKeyCode character:aChar modifierFlags:aModifierFlags target:aTarget selector:aSelector] autorelease];
}
+ (id)hotKeyWithWithPropertyList:(id)aPropertyList
{
return [[[self alloc] initWithPropertyList:aPropertyList] autorelease];
}
+ (NSString *)description
{
NSMapTable * theMapTable = [NDHotKeyEvent allHotKeyEvents];
NSString * theDescription = nil;
if( theMapTable )
{
NDHotKeyEventLock;
theDescription = @"";
NDHotKeyEventUnlock;
}
return theDescription;
}
/*
* -init
*/
- (id)init
{
[self release];
NSAssert( NO, @"You can not initialize a Hot Key with the init method" );
return nil;
}
/*
* -initWithKeyCode:character:modifierFlags:
*/
- (id)initWithKeyCode:(unsigned short)aKeyCode character:(unichar)aChar modifierFlags:(unsigned int)aModifierFlags
{
return [self initWithKeyCode:aKeyCode character:aChar modifierFlags:aModifierFlags target:nil selector:NULL];
}
/*
* -initWithKeyCode:character:modifierFlags:target:selector:
*/
- (id)initWithKeyCode:(unsigned short)aKeyCode character:(unichar)aChar modifierFlags:(unsigned int)aModifierFlags target:(id)aTarget selector:(SEL)aSelector
{
if( (self = [super init]) != nil )
{
keyCode = aKeyCode;
character = aChar;
modifierFlags = aModifierFlags;
target = aTarget;
selectorReleased = aSelector;
currentEventType = NDHotKeyNoEvent;
isEnabled.collective = YES;
if( ![self addHotKey] )
{
[self release];
self = nil;
}
}
else
{
[self release];
self = nil;
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if( (self = [super init]) != nil)
{
if( [aDecoder allowsKeyedCoding] )
{
keyCode = [[aDecoder decodeObjectForKey:kArchivingKeyCodeKey] unsignedShortValue];
character = [[aDecoder decodeObjectForKey:kArchivingCharacterKey] unsignedShortValue];
modifierFlags = [[aDecoder decodeObjectForKey:kArchivingModifierFlagsKey] unsignedIntValue];
selectorReleased = NSSelectorFromString( [aDecoder decodeObjectForKey:kArchivingSelectorReleasedCodeKey] );
selectorPressed = NSSelectorFromString( [aDecoder decodeObjectForKey:kArchivingSelectorPressedCodeKey] );
}
else
{
[aDecoder decodeValueOfObjCType:@encode(unsigned short) at:&keyCode];
[aDecoder decodeValueOfObjCType:@encode(unichar) at:&character];
[aDecoder decodeValueOfObjCType:@encode(unsigned int) at:&modifierFlags];
selectorReleased = NSSelectorFromString( [aDecoder decodeObject] );
selectorPressed = NSSelectorFromString( [aDecoder decodeObject] );
}
if( ![self addHotKey] )
{
[self release];
self = nil;
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)anEncoder
{
if( [anEncoder allowsKeyedCoding] )
{
[anEncoder encodeObject:[NSNumber numberWithUnsignedShort:keyCode] forKey:kArchivingKeyCodeKey];
[anEncoder encodeObject:[NSNumber numberWithUnsignedShort:character] forKey:kArchivingCharacterKey];
[anEncoder encodeObject:[NSNumber numberWithUnsignedInt:modifierFlags] forKey:kArchivingModifierFlagsKey];
[anEncoder encodeObject:NSStringFromSelector( selectorReleased ) forKey:kArchivingSelectorReleasedCodeKey];
[anEncoder encodeObject:NSStringFromSelector( selectorPressed ) forKey:kArchivingSelectorPressedCodeKey];
}
else
{
[anEncoder encodeValueOfObjCType:@encode(unsigned short) at:&keyCode];
[anEncoder encodeValueOfObjCType:@encode(unichar) at:&character];
[anEncoder encodeValueOfObjCType:@encode(unsigned int) at:&modifierFlags];
[anEncoder encodeObject:NSStringFromSelector( selectorReleased )];
[anEncoder encodeObject:NSStringFromSelector( selectorPressed )];
}
}
- (id)initWithPropertyList:(id)aPropertyList
{
if( aPropertyList )
{
NSString * theCharacter;
NSNumber * theKeyCode,
* theModiferFlag;
SEL theKeyPressedSelector,
theKeyReleasedSelector;
theKeyCode = [aPropertyList objectForKey:kArchivingKeyCodeKey];
theCharacter = [aPropertyList objectForKey:kArchivingCharacterKey];
theModiferFlag = [aPropertyList objectForKey:kArchivingModifierFlagsKey];
theKeyPressedSelector = NSSelectorFromString([aPropertyList objectForKey:kArchivingSelectorPressedCodeKey]);
theKeyReleasedSelector = NSSelectorFromString([aPropertyList objectForKey:kArchivingSelectorReleasedCodeKey]);
self = [self initWithKeyCode:[theKeyCode unsignedShortValue] character:[theCharacter characterAtIndex:0] modifierFlags:[theModiferFlag unsignedIntValue]];
}
else
{
[self release];
self = nil;
}
return self;
}
- (id)propertyList
{
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedShort:[self keyCode]], kArchivingKeyCodeKey,
[NSString stringWithCharacters:&character length:1] , kArchivingCharacterKey,
[NSNumber numberWithUnsignedInt:[self modifierFlags]], kArchivingModifierFlagsKey,
NSStringFromSelector( selectorPressed ), kArchivingSelectorPressedCodeKey,
NSStringFromSelector( selectorReleased ), kArchivingSelectorReleasedCodeKey,
nil];
}
/*
* -release
*/
- (oneway void)release
{
/*
* We need to remove the hot key from the hash table before it's retain count reaches zero
*/
if( [self retainCount] == 1 )
{
NSMapTable * theMapTable = [NDHotKeyEvent allHotKeyEvents];
if( theMapTable )
{
NDHotKeyEventLock;
switchHotKey( self, NO );
if( [self retainCount] == 1 ) // check again because it might have changed
{
[theMapTable removeObjectForKey:[NSNumber numberWithInteger:[self hash]]];
}
NDHotKeyEventUnlock;
}
}
// else
[super release];
}
- (void)dealloc
{
if( UnregisterEventHotKey( reference ) != noErr ) // in lock from release
NSLog( @"Failed to unregister hot key %@", self );
[super dealloc];
}
/*
* -setEnabled:
*/
- (BOOL)setEnabled:(BOOL)aFlag
{
BOOL theResult = YES;
if( [NDHotKeyEvent install] )
{
/*
* if individual and collective YES then currently ON, otherwise currently off
*/
NDHotKeyEventLock;
if( aFlag == YES && isEnabled.collective == YES && isEnabled.individual == NO )
{
theResult = (switchHotKey( self, YES ) == noErr);
}
else if( aFlag == NO && isEnabled.collective == YES && isEnabled.individual == YES )
{
theResult = (switchHotKey( self, NO ) == noErr);
}
NDHotKeyEventUnlock;
if( theResult )
isEnabled.individual = aFlag;
else
NSLog(@"%s failed ", aFlag ? "enable" : "disable" );
}
else
theResult = NO;
return theResult;
}
/*
* -isEnabled
*/
- (BOOL)isEnabled
{
return isEnabled.individual && isEnabled.collective;
}
/*
* -target
*/
- (id)target
{
return target;
}
/*
* -selector
*/
- (SEL)selector
{
return selectorReleased;
}
/*
* -selectorReleased
*/
- (SEL)selectorReleased
{
return selectorReleased;
}
/*
* -selectorPressed
*/
- (SEL)selectorPressed
{
return selectorPressed;
}
/*
* -currentEventType
* (NDHotKeyNoEvent | NDHotKeyPressedEvent | NDHotKeyReleasedEvent)
*/
- (int)currentEventType
{
return currentEventType;
}
/*
* -setTarget:selector:
*/
- (BOOL)setTarget:(id)aTarget selector:(SEL)aSelector
{
return [self setTarget:aTarget selectorReleased:aSelector selectorPressed:(SEL)0];
}
/*
* -setTarget:selectorReleased:selectorPressed:
*/
- (BOOL)setTarget:(id)aTarget selectorReleased:(SEL)aSelectorReleased selectorPressed:(SEL)aSelectorPressed
{
[self setEnabled:NO];
if( target && target != aTarget )
{
[self setEnabled:NO];
if( ![target respondsToSelector:@selector(targetWillChangeToObject:forHotKeyEvent:)] || [target targetWillChangeToObject:aTarget forHotKeyEvent:self] )
{
target = aTarget;
selectorReleased = aSelectorReleased;
selectorPressed = aSelectorPressed;
}
}
else
{
target = aTarget;
selectorReleased = aSelectorReleased;
selectorPressed = aSelectorPressed;
}
return target == aTarget; // was change succesful
}
/*
* -performHotKeyReleased
*/
- (void)performHotKeyReleased
{
NSAssert( target, @"NDHotKeyEvent tried to perfrom release with no target" );
if( selectorReleased && [target respondsToSelector:selectorReleased])
{
currentEventType = NDHotKeyReleasedEvent;
[target performSelector:selectorReleased withObject:self];
currentEventType = NDHotKeyNoEvent;
}
}
/*
* -performHotKeyPressed
*/
- (void)performHotKeyPressed
{
NSAssert( target, @"NDHotKeyEvent tried to perfrom press with no target" );
if( selectorPressed && [target respondsToSelector:selectorPressed])
{
currentEventType = NDHotKeyPressedEvent;
[target performSelector:selectorPressed withObject:self];
currentEventType = NDHotKeyNoEvent;
}
}
/*
* -keyCode
*/
- (unsigned short)keyCode
{
return keyCode;
}
/*
* -character
*/
- (unichar)character
{
return character;
}
/*
* -modifierFlags
*/
- (unsigned int)modifierFlags
{
return modifierFlags;
}
/*
* -stringValue
*/
- (NSString *)stringValue
{
NSString * theStringValue = nil;
NDHotKeyEventLock;
theStringValue = stringForKeyCodeAndModifierFlags( [self keyCode], [self character], [self modifierFlags] );
NDHotKeyEventUnlock;
return theStringValue;
}
/*
* -isEqual:
*/
- (BOOL)isEqual:(id)anObject
{
return [super isEqual:anObject] || ([anObject isKindOfClass:[self class]] == YES && [self keyCode] == [(NDHotKeyEvent*)anObject keyCode] && [self modifierFlags] == [(NDHotKeyEvent*)anObject modifierFlags]);
}
/*
* -hash
*/
- (NSUInteger)hash
{
return (unsigned int)keyCode ^ modifierFlags; // xor
}
/*
* -description
*/
- (NSString *)description
{
return [NSString stringWithFormat:@"{\n\tKey Combination: %@,\n\tEnabled: %s\n\tKey Press Selector: %@\n\tKey Release Selector: %@\n}\n", [self stringValue],
[self isEnabled] ? "yes" : "no",
NSStringFromSelector([self selectorPressed]),
NSStringFromSelector([self selectorReleased])];
}
/*
* cocoaModifierFlagsToCarbonModifierFlags()
*/
unsigned int cocoaModifierFlagsToCarbonModifierFlags( unsigned int aModifierFlags )
{
unsigned int theCarbonModifierFlags = 0;
if(aModifierFlags & NSShiftKeyMask)
theCarbonModifierFlags |= shiftKey;
if(aModifierFlags & NSControlKeyMask)
theCarbonModifierFlags |= controlKey;
if(aModifierFlags & NSAlternateKeyMask)
theCarbonModifierFlags |= optionKey;
if(aModifierFlags & NSCommandKeyMask)
theCarbonModifierFlags |= cmdKey;
return theCarbonModifierFlags;
}
/*
* eventHandlerCallback()
*/
pascal OSErr eventHandlerCallback( EventHandlerCallRef anInHandlerCallRef, EventRef anInEvent, void * anInUserData )
{
NSMapTable * allHotKeyEvents = (NSMapTable *)anInUserData;
EventHotKeyID theHotKeyID;
OSStatus theError;
NSCAssert( GetEventClass( anInEvent ) == kEventClassKeyboard, @"Got event that is not a hot key event" );
theError = GetEventParameter( anInEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(EventHotKeyID), NULL, &theHotKeyID );
if( theError == noErr )
{
struct HotKeyMappingEntry * theHotKeyMappingEntry;
NDHotKeyEvent * theHotKeyEvent;
UInt32 theEventKind;
NSCAssert( [NDHotKeyEvent signature] == theHotKeyID.signature, @"Got hot key event with wrong signature" );
theHotKeyMappingEntry = (struct HotKeyMappingEntry *) NSMapGet(allHotKeyEvents, (void *)(NSUInteger)theHotKeyID.id);
if ( theHotKeyMappingEntry ) {
theHotKeyEvent = theHotKeyMappingEntry->hotKeyEvent;
theEventKind = GetEventKind( anInEvent );
if( kEventHotKeyPressed == theEventKind )
{
[theHotKeyEvent performHotKeyPressed];
}
else if( kEventHotKeyReleased == theEventKind )
{
[theHotKeyEvent performHotKeyReleased];
}
}
}
return theError;
}
/*
* hashValueHashFunction()
*/
NSUInteger hashValueHashFunction( NSMapTable * aTable, const void * aHotKeyEntry )
{
struct HotKeyMappingEntry * theHotKeyEntry;
unsigned int theKeyCode,
theModifiers;
theHotKeyEntry = (struct HotKeyMappingEntry*)aHotKeyEntry;
theKeyCode = (unsigned int)theHotKeyEntry->keyCode;
theModifiers = (unsigned int)theHotKeyEntry->modifierFlags;
return theKeyCode ^ theModifiers; // xor
}
/*
* isEqualHashFunction()
*/
BOOL isEqualHashFunction( NSMapTable * aTable, const void * aFirstHotKeyEntry, const void * aSecondHotKeyEntry )
{
struct HotKeyMappingEntry * theFirst,
* theSecond;
theFirst = (struct HotKeyMappingEntry*)aFirstHotKeyEntry;
theSecond = (struct HotKeyMappingEntry*)aSecondHotKeyEntry;
return theFirst->keyCode == theSecond->keyCode && theFirst->modifierFlags == theSecond->modifierFlags;
}
/*
* describeHashFunction()
*/
NSString * describeHashFunction( NSMapTable * aTable, const void * aHotKeyEntry )
{
NDHotKeyEvent * theHotKey;
theHotKey = ((struct HotKeyMappingEntry*)aHotKeyEntry)->hotKeyEvent;
return [theHotKey description];
}
NSUInteger hashKeyHashFunction( NSMapTable * aTable, const void * aNumber )
{
NSNumber * theNumber = (NSNumber *) aNumber;
return [theNumber integerValue];
}
BOOL isEqualKeyHashFunction( NSMapTable * aTable, const void * aFirstNumber, const void * aSecondNumber )
{
NSNumber * theFirstNumber = (NSNumber *) aFirstNumber;
NSNumber * theSecondNumber = (NSNumber *) aSecondNumber;
return [theFirstNumber isEqual:theSecondNumber];
}
void hashKeyRetainFunction( NSMapTable * aTable, const void * aNumber )
{
const NSNumber * theNumber = (const NSNumber *) aNumber;
[theNumber retain];
}
void hashKeyReleaseFunction( NSMapTable * aTable, void * aNumber )
{
NSNumber * theNumber = (NSNumber *) aNumber;
[theNumber release];
}
@end
@implementation NDHotKeyEvent (Private)
/*
* +allHotKeyEvents
*/
+ (NSMapTable *)allHotKeyEvents
{
if( allHotKeyEvents == NULL )
{
NDHotKeyEventLock;
if( allHotKeyEvents == NULL )
allHotKeyEvents = NSCreateMapTable(NSIntegerMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0);
NDHotKeyEventUnlock;
}
return allHotKeyEvents;
}
/*
* -addHotKey
*/
- (BOOL)addHotKey
{
NSMapTable * theMapTable = [NDHotKeyEvent allHotKeyEvents];
if( theMapTable )
{
struct HotKeyMappingEntry * theEntry;
theEntry = (struct HotKeyMappingEntry *)malloc(sizeof(struct HotKeyMappingEntry));
theEntry->keyCode = [self keyCode];
theEntry->modifierFlags = [self modifierFlags];
theEntry->hotKeyEvent = self;
NDHotKeyEventLock;
NSMapInsert(theMapTable, (void*)[self hash], theEntry);
NDHotKeyEventUnlock;
}
return YES;
}
/*
* -removeHotKey
*/
- (void)removeHotKey
{
[self setEnabled:NO];
NSMapTable * theMapTable = [NDHotKeyEvent allHotKeyEvents];
if( theMapTable )
{
NDHotKeyEventLock;
free(NSMapGet(theMapTable, (void *)[self hash]));
NSMapRemove(theMapTable, (void*)[self hash]);
NDHotKeyEventUnlock;
}
}
/*
* setCollectiveEnabled:
*/
- (BOOL)setCollectiveEnabled:(BOOL)aFlag
{
BOOL theResult = YES;
if( [NDHotKeyEvent install] )
{
/*
* if individual and collective YES then currently ON, otherwise currently off
*/
NDHotKeyEventLock;
if( aFlag == YES && isEnabled.collective == NO && isEnabled.individual == YES )
{
theResult = (switchHotKey( self, YES ) == noErr);
}
else if( aFlag == NO && isEnabled.collective == YES && isEnabled.individual == YES )
{
theResult = (switchHotKey( self, NO ) == noErr);
}
NDHotKeyEventUnlock;
if( theResult )
isEnabled.collective = aFlag;
else
NSLog(@"%s failed", aFlag ? "enable" : "disable" );
}
else
theResult = NO;
return theResult;
}
/*
* collectiveEnable()
*/
- (BOOL)collectiveEnable
{
return isEnabled.collective;
}
/*
* switchHotKey()
*/
static OSStatus switchHotKey( NDHotKeyEvent * self, BOOL aFlag )
{
OSStatus theError;
if( aFlag )
{
struct HotKeyMappingEntry theDummyEntry;
theDummyEntry.keyCode = [self keyCode];
theDummyEntry.modifierFlags = [self modifierFlags];
theDummyEntry.hotKeyEvent = nil;
EventHotKeyID theHotKeyID;
theHotKeyID.signature = [NDHotKeyEvent signature];
theHotKeyID.id = hashValueHashFunction( nil, (const void *)&theDummyEntry );
NSCAssert( theHotKeyID.signature, @"HotKeyEvent signature has not been set yet" );
NSCParameterAssert(sizeof(unsigned long) >= sizeof(id) );
theError = RegisterEventHotKey( self->keyCode, cocoaModifierFlagsToCarbonModifierFlags(self->modifierFlags), theHotKeyID, GetEventDispatcherTarget(), 0, &self->reference );
}
else
{
theError = UnregisterEventHotKey( self->reference );
}
return theError;
}
@end
/*
* stringForKeyCodeAndModifierFlags()
*/
NSString * stringForKeyCodeAndModifierFlags( unsigned short aKeyCode, unichar aChar, unsigned int aModifierFlags )
{
NSString * stringForCharacter( const unsigned short aKeyCode, unichar aCharacter );
NSString * stringForModifiers( unsigned int aModifierFlags );
return [stringForModifiers(aModifierFlags) stringByAppendingString:stringForCharacter( aKeyCode, aChar )];
}
/*
* unicharForKeyCode()
*/
unichar unicharForKeyCode( unsigned short aKeyCode )
{
UInt32 theChar = kNullCharCode;
TISInputSourceRef theCurrentKeyBoardLayout = TISCopyCurrentKeyboardLayoutInputSource();
UCKeyboardLayout *uchr = (UCKeyboardLayout *) TISGetInputSourceProperty(theCurrentKeyBoardLayout, kTISPropertyUnicodeKeyLayoutData);
if ( uchr != nil )
{
UInt32 deadKeyState = 0;
UInt32 flags = 0;
UniCharCount maxStringLength = 255;
UniCharCount actualStringLength = 0;
UniChar unicodeString[maxStringLength];
UCKeyTranslate(uchr, aKeyCode, kUCKeyActionDown, flags, LMGetKbdType(), 1, &deadKeyState, maxStringLength, &actualStringLength, unicodeString);
if ( actualStringLength == 1 )
theChar = unicodeString[ 0 ];
switch( theChar )
{
case kHomeCharCode: theChar = NSHomeFunctionKey; break;
// case kEnterCharCode: theChar = ; break;
case kEndCharCode: theChar = NSEndFunctionKey; break;
case kHelpCharCode: theChar = NSHelpFunctionKey; break;
// case kBellCharCode: theChar = ; break;
// case kBackspaceCharCode: theChar = ; break;
// case kTabCharCode: theChar = ; break;
// case kLineFeedCharCode: theChar = ; break;
case kPageUpCharCode: theChar = NSPageUpFunctionKey; break;
case kPageDownCharCode: theChar = NSPageDownFunctionKey; break;
// case kReturnCharCode: theChar = ; break;
case kFunctionKeyCharCode: theChar = unicodeForFunctionKey( aKeyCode ); break;
// case kCommandCharCode: theChar = ; break;
// case kCheckCharCode: theChar = ; break;
// case kDiamondCharCode : theChar = ; break;
// case kAppleLogoCharCode: theChar = ; break;
// case kEscapeCharCode: theChar = ; break;
case kClearCharCode:
theChar = (aKeyCode==0x47) ? NSInsertFunctionKey : theChar;
break;
case kLeftArrowCharCode: theChar = NSLeftArrowFunctionKey; break;
case kRightArrowCharCode: theChar = NSRightArrowFunctionKey; break;
case kUpArrowCharCode: theChar = NSUpArrowFunctionKey; break;
case kDownArrowCharCode: theChar = NSDownArrowFunctionKey; break;
// case kSpaceCharCode: theChar = ; break;
case kDeleteCharCode: theChar = NSDeleteCharFunctionKey; break;
// case kBulletCharCode: theChar = ; break;
// case kNonBreakingSpaceCharCode: theChar = ; break;
}
}
return theChar;
}
unichar unicodeForFunctionKey( UInt32 aKeyCode )
{
switch( aKeyCode )
{
case 0x7A: return NSF1FunctionKey;
case 0x78: return NSF2FunctionKey;
case 0x63: return NSF3FunctionKey;
case 0x76: return NSF4FunctionKey;
case 0x60: return NSF5FunctionKey;
case 0x61: return NSF6FunctionKey;
case 0x62: return NSF7FunctionKey;
case 0x64: return NSF8FunctionKey;
case 0x65: return NSF9FunctionKey;
case 0x6D: return NSF10FunctionKey;
case 0x67: return NSF11FunctionKey;
case 0x6F: return NSF12FunctionKey;
default: return 0x00;
}
}
NSString * stringForCharacter( const unsigned short aKeyCode, unichar aCharacter )
{
NSString * theString = nil;
switch( aCharacter )
{
case NSUpArrowFunctionKey:
aCharacter = 0x2191;
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
case NSDownArrowFunctionKey:
aCharacter = 0x2193;
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
case NSLeftArrowFunctionKey:
aCharacter = 0x2190;
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
case NSRightArrowFunctionKey:
aCharacter = 0x2192;
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
case NSF1FunctionKey: theString = @"F1"; break;
case NSF2FunctionKey: theString = @"F2"; break;
case NSF3FunctionKey: theString = @"F3"; break;
case NSF4FunctionKey: theString = @"F4"; break;
case NSF5FunctionKey: theString = @"F5"; break;
case NSF6FunctionKey: theString = @"F6"; break;
case NSF7FunctionKey: theString = @"F7"; break;
case NSF8FunctionKey: theString = @"F8"; break;
case NSF9FunctionKey: theString = @"F9"; break;
case NSF10FunctionKey: theString = @"F10"; break;
case NSF11FunctionKey: theString = @"F11"; break;
case NSF12FunctionKey: theString = @"F12"; break;
case NSF13FunctionKey: theString = @"F13"; break;
case NSF14FunctionKey: theString = @"F14"; break;
case NSF15FunctionKey: theString = @"F15"; break;
case NSF16FunctionKey: theString = @"F16"; break;
case NSF17FunctionKey: theString = @"F17"; break;
case NSF18FunctionKey: theString = @"F18"; break;
case NSF19FunctionKey: theString = @"F19"; break;
case NSF20FunctionKey: theString = @"F20"; break;
case NSF21FunctionKey: theString = @"F21"; break;
case NSF22FunctionKey: theString = @"F22"; break;
case NSF23FunctionKey: theString = @"F23"; break;
case NSF24FunctionKey: theString = @"F24"; break;
case NSF25FunctionKey: theString = @"F25"; break;
case NSF26FunctionKey: theString = @"F26"; break;
case NSF27FunctionKey: theString = @"F27"; break;
case NSF28FunctionKey: theString = @"F28"; break;
case NSF29FunctionKey: theString = @"F29"; break;
case NSF30FunctionKey: theString = @"F30"; break;
case NSF31FunctionKey: theString = @"F31"; break;
case NSF32FunctionKey: theString = @"F32"; break;
case NSF33FunctionKey: theString = @"F33"; break;
case NSF34FunctionKey: theString = @"F34"; break;
case NSF35FunctionKey: theString = @"F35"; break;
case NSInsertFunctionKey: theString = @"Ins"; break;
case NSDeleteFunctionKey: theString = @"Delete"; break;
case NSHomeFunctionKey:
aCharacter = 0x2196;
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
case NSBeginFunctionKey: theString = @"Begin"; break;
case NSEndFunctionKey: theString = @"End"; break;
case NSPageUpFunctionKey:
// aCharacter = 0x21DE;
// theString = [NSString stringWithCharacters:&aCharacter length:1];
theString = @"PgUp";
break;
case NSPageDownFunctionKey:
// aCharacter = 0x21DF;
// theString = [NSString stringWithCharacters:&aCharacter length:1];
theString = @"PgDn";
break;
case NSPrintScreenFunctionKey: theString = @"Print"; break;
case NSScrollLockFunctionKey: theString = @"ScrollLock"; break;
case NSPauseFunctionKey: theString = @"Pause"; break;
case NSSysReqFunctionKey: theString = @"SysReq"; break;
case NSBreakFunctionKey: theString = @"Break"; break;
case NSResetFunctionKey: theString = @"Reset"; break;
case NSStopFunctionKey: theString = @"Stop"; break;
case NSMenuFunctionKey: theString = @"Menu"; break;
case NSUserFunctionKey: theString = @"User"; break;
case NSSystemFunctionKey: theString = @"System"; break;
case NSPrintFunctionKey: theString = @"Print"; break;
case NSClearLineFunctionKey: theString = @"ClearLine"; break;
case NSClearDisplayFunctionKey: theString = @"ClearDisplay"; break;
case NSInsertLineFunctionKey: theString = @"InsertLine"; break;
case NSDeleteLineFunctionKey: theString = @"DeleteLine"; break;
case NSInsertCharFunctionKey: theString = @"InsertChar"; break;
case NSDeleteCharFunctionKey:
aCharacter = 0x2326;
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
case NSPrevFunctionKey: theString = @"Prev"; break;
case NSNextFunctionKey: theString = @"Next"; break;
case NSSelectFunctionKey: theString = @"Select"; break;
case NSExecuteFunctionKey: theString = @"Exec"; break;
case NSUndoFunctionKey: theString = @"Undo"; break;
case NSRedoFunctionKey: theString = @"Redo"; break;
case NSFindFunctionKey: theString = @"Find"; break;
case NSHelpFunctionKey: theString = @"Help"; break;
case NSModeSwitchFunctionKey: theString = @"ModeSwitch"; break;
case kEscapeCharCode: theString = @"Esc"; break;
case kTabCharCode:
aCharacter = 0x21E5;
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
case kSpaceCharCode: theString = @"Space"; break;
case kEnterCharCode:
aCharacter = 0x21B5;
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
case kReturnCharCode:
aCharacter = 0x21A9;
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
case kDeleteCharCode: theString = @"Del"; break;
case '0'...'9':
case '=':
case '/':
case '*':
case '-':
case '+':
case '.':
theString = [NSString stringWithCharacters:&aCharacter length:1];
if( aKeyCode > 60 )
theString = [NSString stringWithFormat:@"[%@]", theString];
break;
default:
aCharacter = unicharForKeyCode(aKeyCode);
if( aCharacter >= 'a' && aCharacter <= 'z' ) // convert to uppercase
aCharacter = aCharacter + 'A' - 'a';
theString = [NSString stringWithCharacters:&aCharacter length:1];
break;
}
return theString;
}
NSString * stringForModifiers( unsigned int aModifierFlags )
{
NSMutableString * theString;
unichar theCharacter;
theString = [NSMutableString string];
if( aModifierFlags & NSControlKeyMask)
{
theCharacter = kControlUnicode;
[theString appendString:[NSString stringWithCharacters:&theCharacter length:1]];
}
if( aModifierFlags & NSAlternateKeyMask)
{
theCharacter = kOptionUnicode;
[theString appendString:[NSString stringWithCharacters:&theCharacter length:1]];
}
if( aModifierFlags & NSShiftKeyMask)
{
theCharacter = kShiftUnicode;
[theString appendString:[NSString stringWithCharacters:&theCharacter length:1]];
}
if( aModifierFlags & NSCommandKeyMask)
{
theCharacter = kCommandUnicode;
[theString appendString:[NSString stringWithCharacters:&theCharacter length:1]];
}
return theString;
}