// // NJRHotKeyManager.m // Pester // // Created by Nicholas Riley on Tue Apr 01 2003. // Copyright (c) 2003 Nicholas Riley. All rights reserved. // // based on HotKeyCenter, by Quentin Carnicelli // renamed, reorganized, cleaned up, pre-10.2 support removed #import "NJRHotKeyManager.h" #import "NJRHotKey.h" #import const OSType kHotKeyManagerSignature = 'NHKM'; @interface _NJRHotKeyShortcut : NSObject { @public BOOL isRegistered; EventHotKeyRef hotKeyRef; NJRHotKey *hotKey; id target; SEL action; } @end @implementation _NJRHotKeyShortcut - (void)dealloc; { [hotKey release]; [target release]; } @end pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon); @interface NJRHotKeyManager (Private) - (OSStatus)_handleHotKeyEvent:(EventRef)inEvent; - (BOOL)_registerHotKeyIfNeeded:(_NJRHotKeyShortcut *)shortcut; - (void)_unregisterHotKeyIfNeeded:(_NJRHotKeyShortcut *)shortcut; - (void)_hotKeyDown:(_NJRHotKeyShortcut *)hotKey; - (void)_hotKeyUp:(_NJRHotKeyShortcut *)hotKey; - (void)_hotKeyDownWithRef:(EventHotKeyRef)ref; - (void)_hotKeyUpWithRef:(EventHotKeyRef)ref; - (_NJRHotKeyShortcut *)_findShortcutWithRef:(EventHotKeyRef)ref; @end @implementation NJRHotKeyManager + (NJRHotKeyManager *)sharedManager; { static NJRHotKeyManager *manager = nil; if (manager == nil) { manager = [[self alloc] init]; EventTypeSpec eventSpec[2] = { { kEventClassKeyboard, kEventHotKeyPressed }, { kEventClassKeyboard, kEventHotKeyReleased } }; InstallEventHandler(GetEventDispatcherTarget(), NewEventHandlerUPP((EventHandlerProcPtr) keyEventHandler), 2, eventSpec, nil, nil); } return manager; } - (id)init; { if ( (self = [super init]) != nil) { shortcutsEnabled = YES; shortcuts = [[NSMutableDictionary alloc] init]; } return self; } - (void)dealloc; { [shortcuts release]; [super dealloc]; } #pragma mark - - (BOOL)addShortcutWithIdentifier:(NSString *)identifier hotKey:(NJRHotKey *)hotKey target:(id)target action:(SEL)action; { NSParameterAssert(identifier != nil); NSParameterAssert(hotKey != nil); NSParameterAssert(target != nil); NSParameterAssert(action != nil); if ([shortcuts objectForKey: identifier] != nil) [self removeShortcutWithIdentifier: identifier]; _NJRHotKeyShortcut *newShortcut = [[_NJRHotKeyShortcut alloc] init]; newShortcut->isRegistered = NO; newShortcut->hotKeyRef = nil; newShortcut->hotKey = [hotKey retain]; newShortcut->target = [target retain]; newShortcut->action = action; [shortcuts setObject: newShortcut forKey: identifier]; [newShortcut release]; return [self _registerHotKeyIfNeeded: newShortcut]; } - (void)removeShortcutWithIdentifier:(NSString *)identifier; { _NJRHotKeyShortcut *hotKey = [shortcuts objectForKey: identifier]; if (hotKey == nil) return; [self _unregisterHotKeyIfNeeded: hotKey]; [shortcuts removeObjectForKey: identifier]; } - (NSArray *)shortcutIdentifiers; { return [shortcuts allKeys]; } - (NJRHotKey *)hotKeyForShortcutWithIdentifier:(NSString *)identifier; { _NJRHotKeyShortcut *hotKey = [shortcuts objectForKey: identifier]; return (hotKey == nil ? nil : [[hotKey->hotKey retain] autorelease]); } - (void)setShortcutsEnabled:(BOOL)enabled; { NSEnumerator *enumerator = [shortcuts objectEnumerator]; _NJRHotKeyShortcut *hotKey; while ( (hotKey = [enumerator nextObject]) != nil) { if (enabled) [self _registerHotKeyIfNeeded: hotKey]; else [self _unregisterHotKeyIfNeeded: hotKey]; } shortcutsEnabled = enabled; } - (BOOL)shortcutsEnabled; { return shortcutsEnabled; } #pragma mark - - (OSStatus)_handleHotKeyEvent:(EventRef)inEvent; { OSStatus err; EventHotKeyID hotKeyID; _NJRHotKeyShortcut *shortcut; NSAssert(GetEventClass(inEvent) == kEventClassKeyboard, @"Unhandled event class"); if ( (err = GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, nil, sizeof(EventHotKeyID), nil, &hotKeyID)) != noErr) return err; NSAssert(hotKeyID.signature == kHotKeyManagerSignature, @"Unknown hot key"); shortcut = (_NJRHotKeyShortcut *)hotKeyID.id; NSAssert(shortcut != nil, @"Got bad hot key"); switch (GetEventKind(inEvent)) { case kEventHotKeyPressed: [self _hotKeyDown: shortcut]; break; case kEventHotKeyReleased: [self _hotKeyUp: shortcut]; break; default: break; } return noErr; } #pragma mark - - (BOOL)_registerHotKeyIfNeeded:(_NJRHotKeyShortcut *)shortcut; { NJRHotKey *hotKey; NSParameterAssert(shortcut != nil); hotKey = shortcut->hotKey; if (shortcutsEnabled && !(shortcut->isRegistered)) { EventHotKeyID keyID; OSStatus err; keyID.signature = kHotKeyManagerSignature; keyID.id = (unsigned long)shortcut; if ( (err = RegisterEventHotKey([hotKey keyCode], [hotKey modifiers], keyID, GetEventDispatcherTarget(), 0, &shortcut->hotKeyRef)) != noErr) return NO; shortcut->isRegistered = YES; } return YES; } - (void)_unregisterHotKeyIfNeeded:(_NJRHotKeyShortcut *)shortcut; { NSParameterAssert(shortcut != nil); if (shortcut->isRegistered && shortcut->hotKeyRef != nil) UnregisterEventHotKey(shortcut->hotKeyRef); } - (void)_hotKeyDown:(_NJRHotKeyShortcut *)hotKey; { id target = hotKey->target; SEL action = hotKey->action; [target performSelector: action withObject: self]; } - (void)_hotKeyUp:(_NJRHotKeyShortcut *)hotKey; { } - (void)_hotKeyDownWithRef:(EventHotKeyRef)ref; { _NJRHotKeyShortcut *hotKey = [self _findShortcutWithRef: ref]; if (hotKey != nil) [self _hotKeyDown: hotKey]; } - (void)_hotKeyUpWithRef:(EventHotKeyRef)ref; { } - (_NJRHotKeyShortcut *)_findShortcutWithRef:(EventHotKeyRef)ref; { NSEnumerator *enumerator = [shortcuts objectEnumerator]; _NJRHotKeyShortcut *hotKey; while ( (hotKey = [enumerator nextObject]) != nil) { if (hotKey->hotKeyRef == ref) return hotKey; } return nil; } @end pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon) { return [[NJRHotKeyManager sharedManager] _handleHotKeyEvent: inEvent]; }