4 // Created by Quentin Carnicelli on Sat Aug 02 2003.
5 // Copyright (c) 2003 iThink Software. All rights reserved.
8 #import "ITHotKeyCenter.h"
10 #import "ITKeyCombo.h"
11 #import <Carbon/Carbon.h>
14 #import "NSObjectAdditions.h"
17 @interface ITHotKeyCenter (Private)
18 - (BOOL)_hasCarbonEventSupport;
20 - (ITHotKey*)_hotKeyForCarbonHotKey: (EventHotKeyRef)carbonHotKey;
21 - (EventHotKeyRef)_carbonHotKeyForHotKey: (ITHotKey*)hotKey;
23 - (void)_updateEventHandler;
24 - (void)_hotKeyDown: (ITHotKey*)hotKey;
25 - (void)_hotKeyUp: (ITHotKey*)hotKey;
26 static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon );
29 @implementation ITHotKeyCenter
31 static id _sharedHotKeyCenter = nil;
35 if( _sharedHotKeyCenter == nil )
37 _sharedHotKeyCenter = [[self alloc] init];
39 [_sharedHotKeyCenter releaseOnTerminate];
43 return _sharedHotKeyCenter;
52 mHotKeys = [[NSMutableDictionary alloc] init];
72 - (void)setEnabled:(BOOL)flag
77 - (BOOL)registerHotKey: (ITHotKey*)hotKey
80 EventHotKeyID hotKeyID;
81 EventHotKeyRef carbonHotKey;
84 if( [[self allHotKeys] containsObject: hotKey] == YES )
85 [self unregisterHotKey: hotKey];
87 if( [[hotKey keyCombo] isValidHotKeyCombo] == NO )
90 hotKeyID.signature = 'PTHk';
91 hotKeyID.id = (long)hotKey;
93 err = RegisterEventHotKey( [[hotKey keyCombo] keyCode],
94 [[hotKey keyCombo] modifiers],
96 GetEventDispatcherTarget(),
103 key = [NSValue valueWithPointer: carbonHotKey];
104 [mHotKeys setObject: hotKey forKey: key];
106 [self _updateEventHandler];
111 - (void)unregisterHotKey: (ITHotKey*)hotKey
114 EventHotKeyRef carbonHotKey;
117 if( [[self allHotKeys] containsObject: hotKey] == NO )
120 carbonHotKey = [self _carbonHotKeyForHotKey: hotKey];
121 NSAssert( carbonHotKey != nil, @"" );
123 err = UnregisterEventHotKey( carbonHotKey );
124 //Watch as we ignore 'err':
126 key = [NSValue valueWithPointer: carbonHotKey];
127 [mHotKeys removeObjectForKey: key];
129 [self _updateEventHandler];
131 //See that? Completely ignored
134 - (NSArray*)allHotKeys
136 return [mHotKeys allValues];
141 - (BOOL)_hasCarbonEventSupport
143 return floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_1;
146 - (ITHotKey*)_hotKeyForCarbonHotKey: (EventHotKeyRef)carbonHotKey
148 NSValue* key = [NSValue valueWithPointer: carbonHotKey];
149 return [mHotKeys objectForKey: key];
152 - (EventHotKeyRef)_carbonHotKeyForHotKey: (ITHotKey*)hotKey
157 values = [mHotKeys allKeysForObject: hotKey];
158 NSAssert( [values count] == 1, @"Failed to find Carbon Hotkey for ITHotKey" );
160 value = [values lastObject];
162 return (EventHotKeyRef)[value pointerValue];
165 - (void)_updateEventHandler
167 if( [self _hasCarbonEventSupport] == NO ) //Don't use event handler on these systems
170 if( [mHotKeys count] && mEventHandlerInstalled == NO )
172 EventTypeSpec eventSpec[2] = {
173 { kEventClassKeyboard, kEventHotKeyPressed },
174 { kEventClassKeyboard, kEventHotKeyReleased }
177 InstallEventHandler( GetEventDispatcherTarget(),
178 (EventHandlerProcPtr)hotKeyEventHandler,
179 2, eventSpec, nil, nil);
181 mEventHandlerInstalled = YES;
185 - (void)_hotKeyDown: (ITHotKey*)hotKey
190 - (void)_hotKeyUp: (ITHotKey*)hotKey
194 - (void)sendEvent: (NSEvent*)event
197 EventHotKeyRef carbonHotKey;
203 //We only have to intercept sendEvent to do hot keys on old system versions
204 if( [self _hasCarbonEventSupport] )
207 if( [event type] == NSSystemDefined )
209 subType = [event subtype];
211 if( subType == 6 ) //6 is hot key down
213 carbonHotKey= (EventHotKeyRef)[event data1]; //data1 is our hot key ref
214 if( carbonHotKey != nil )
216 ITHotKey* hotKey = [self _hotKeyForCarbonHotKey: carbonHotKey];
217 [self _hotKeyDown: hotKey];
220 else if( subType == 9 ) //9 is hot key up
222 carbonHotKey= (EventHotKeyRef)[event data1];
223 if( carbonHotKey != nil )
225 ITHotKey* hotKey = [self _hotKeyForCarbonHotKey: carbonHotKey];
226 [self _hotKeyUp: hotKey];
232 - (OSStatus)sendCarbonEvent: (EventRef)event
235 EventHotKeyID hotKeyID;
242 NSAssert( [self _hasCarbonEventSupport], @"" );
243 NSAssert( GetEventClass( event ) == kEventClassKeyboard, @"Unknown event class" );
245 err = GetEventParameter( event,
246 kEventParamDirectObject,
249 sizeof(EventHotKeyID),
256 NSAssert( hotKeyID.signature == 'PTHk', @"Invalid hot key id" );
257 NSAssert( hotKeyID.id != nil, @"Invalid hot key id" );
259 hotKey = (ITHotKey*)hotKeyID.id;
261 switch( GetEventKind( event ) )
263 case kEventHotKeyPressed:
264 [self _hotKeyDown: hotKey];
267 case kEventHotKeyReleased:
268 [self _hotKeyUp: hotKey];
272 NSAssert( 0, @"Unknown event kind" );
279 static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon )
281 return [[ITHotKeyCenter sharedCenter] sendCarbonEvent: inEvent];