// // HotKeyCenter.m // // Created by Quentin D. Carnicelli on Thu Jun 06 2002. // Copyright (c) 2002 Subband inc.. All rights reserved. // // Feedback welcome at qdc@subband.com // This code is provided AS IS, so don't hurt yourself with it... // #import "HotKeyCenter.h" #import "KeyCombo.h" #import #define kHotKeyCenterSignature 'HKyC' //*** _HotKeyData @interface _HotKeyData : NSObject { @public BOOL mRegistered; EventHotKeyRef mRef; KeyCombo *mCombo; id mTarget; SEL mAction; } @end @implementation _HotKeyData @end //**** HotKeyCenter @interface HotKeyCenter (Private) - (OSStatus)handleHotKeyEvent: (EventRef)inEvent; - (BOOL)_registerHotKeyIfNeeded:(_HotKeyData *)hk; - (void)_unregisterHotKeyIfNeeded:(_HotKeyData *)hk; + (BOOL)_systemSupportsHotKeys; - (void)_hotKeyUp:(_HotKeyData *)hotKey; - (void)_hotKeyDown:(_HotKeyData *)hotKey; - (void)_hotKeyDownWithRef:(EventHotKeyRef)ref; - (void)_hotKeyUpWithRef:(EventHotKeyRef)ref; - (_HotKeyData*)_findHotKeyWithRef:(EventHotKeyRef)ref; pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon); @end @implementation HotKeyCenter static id _sharedHKCenter = nil; + (id)sharedCenter { if (_sharedHKCenter != nil) { return _sharedHKCenter; } _sharedHKCenter = [[HotKeyCenter alloc] init]; if ([self _systemSupportsHotKeys]) { EventTypeSpec eventSpec[2] = { { kEventClassKeyboard, kEventHotKeyPressed }, { kEventClassKeyboard, kEventHotKeyReleased } }; InstallEventHandler( GetEventDispatcherTarget(), NewEventHandlerUPP((EventHandlerProcPtr) keyEventHandler), 2, eventSpec, nil, nil); } return _sharedHKCenter; } - (id)init { if ( (self = [super init]) ) { mEnabled = YES; mHotKeys = [[NSMutableDictionary alloc] init]; } return self; } - (void)dealloc { [mHotKeys release]; [super dealloc]; } - (BOOL)addHotKey:(NSString *)name combo:(KeyCombo *)combo target:(id)target action:(SEL)action { _HotKeyData *oldHotKey; _HotKeyData *newHotKey; NSParameterAssert(name != nil); NSParameterAssert(combo != nil); NSParameterAssert(target != nil); NSParameterAssert(action != nil); //** Check if we have one of these yet oldHotKey = [mHotKeys objectForKey:name]; if (oldHotKey) //Registered already? { [self removeHotKey:name]; } //** Save the hot key to our own list newHotKey = [[[_HotKeyData alloc] init] autorelease]; newHotKey->mRegistered = NO; newHotKey->mRef = nil; newHotKey->mCombo = [combo retain]; newHotKey->mTarget = target; //Retain this? newHotKey->mAction = action; [mHotKeys setObject:newHotKey forKey:name]; return [self _registerHotKeyIfNeeded:newHotKey]; } - (void)removeHotKey:(NSString *)name; { _HotKeyData *hotKey; hotKey = [mHotKeys objectForKey:name]; if (hotKey == nil) //Not registered return; [self _unregisterHotKeyIfNeeded:hotKey]; [hotKey->mCombo release]; //Drop it from our hot key list [mHotKeys removeObjectForKey: name]; } - (NSArray *)allNames { return [mHotKeys allKeys]; } - (KeyCombo *)keyComboForName:(NSString *)name { _HotKeyData * hotKey; hotKey = [mHotKeys objectForKey:name]; if( hotKey == nil ) //Not registered return nil; return hotKey->mCombo; } - (void)setEnabled:(BOOL)enabled { NSEnumerator *enumerator; _HotKeyData *hotKey; enumerator = [mHotKeys objectEnumerator]; while ((hotKey = [enumerator nextObject]) != nil) { if (enabled) [self _registerHotKeyIfNeeded:hotKey]; else [self _unregisterHotKeyIfNeeded:hotKey]; } mEnabled = enabled; } - (BOOL)enabled { return mEnabled; } - (void)sendEvent:(NSEvent *)event; { long subType; EventHotKeyRef hotKeyRef; //We only have to intercept sendEvent to do hot keys on old system versions if ([HotKeyCenter _systemSupportsHotKeys] == YES) return; if ([event type] == NSSystemDefined) { subType = [event subtype]; if (subType == 6) //6 is hot key down { hotKeyRef = (EventHotKeyRef)[event data1]; //data1 is our hot key ref if (hotKeyRef != nil) { [self _hotKeyDownWithRef:hotKeyRef]; } } else if (subType == 9) //9 is hot key up { hotKeyRef = (EventHotKeyRef)[event data1]; if (hotKeyRef != nil) { [self _hotKeyUpWithRef:hotKeyRef]; } } } } - (OSStatus)handleHotKeyEvent:(EventRef)inEvent { OSStatus err; EventHotKeyID hotKeyID; _HotKeyData *hk; //Shouldnt get here on non-hotkey supporting system versions NSAssert([HotKeyCenter _systemSupportsHotKeys] == YES, @""); NSAssert(GetEventClass(inEvent) == kEventClassKeyboard, @"Got unhandled event class"); err = GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, nil, sizeof(EventHotKeyID), nil, &hotKeyID); if (err) { return err; } NSAssert(hotKeyID.signature == kHotKeyCenterSignature, @"Got unknown hot key"); hk = (_HotKeyData *)hotKeyID.id; NSAssert(hk != nil, @"Got bad hot key"); switch (GetEventKind(inEvent)) { case kEventHotKeyPressed: [self _hotKeyDown:hk]; break; case kEventHotKeyReleased: [self _hotKeyUp:hk]; break; default: break; } return noErr; } + (BOOL)_systemSupportsHotKeys { SInt32 vers; Gestalt(gestaltSystemVersion,&vers); return (vers >= 0x00001020); } - (BOOL)_registerHotKeyIfNeeded:(_HotKeyData *)hk { KeyCombo *combo; NSParameterAssert(hk != nil); combo = hk->mCombo; if( mEnabled == YES && hk->mRegistered == NO && [combo isValid] == YES ) { EventHotKeyID keyID; OSStatus err; keyID.signature = kHotKeyCenterSignature; keyID.id = (unsigned long)hk; err = RegisterEventHotKey([combo keyCode], [combo modifiers], keyID, GetEventDispatcherTarget(), 0, &hk->mRef); if (err) { return NO; } hk->mRegistered = YES; } return YES; } - (void)_unregisterHotKeyIfNeeded:(_HotKeyData *)hk { NSParameterAssert(hk != nil); if (hk->mRegistered && hk->mRef != nil) { UnregisterEventHotKey(hk->mRef); } } - (void)_hotKeyDown:(_HotKeyData *)hotKey { id target = hotKey->mTarget; SEL action = hotKey->mAction; [target performSelector:action withObject:self]; } - (void)_hotKeyUp: (_HotKeyData *)hotKey { } - (void)_hotKeyDownWithRef:(EventHotKeyRef)ref { _HotKeyData *hotKey; hotKey = [self _findHotKeyWithRef:ref]; if (hotKey) { [self _hotKeyDown:hotKey]; } } - (void)_hotKeyUpWithRef:(EventHotKeyRef)ref { } - (_HotKeyData *)_findHotKeyWithRef:(EventHotKeyRef)ref { NSEnumerator *enumerator; _HotKeyData *hotKey; enumerator = [mHotKeys objectEnumerator]; while ((hotKey = [enumerator nextObject]) != nil) { if (hotKey->mRef == ref) { return hotKey; } } return nil; } pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon) { return [[HotKeyCenter sharedCenter] handleHotKeyEvent:inEvent]; } @end