1 | // |
---|
2 | // NJRHotKeyManager.m |
---|
3 | // Pester |
---|
4 | // |
---|
5 | // Created by Nicholas Riley on Tue Apr 01 2003. |
---|
6 | // Copyright (c) 2003 Nicholas Riley. All rights reserved. |
---|
7 | // |
---|
8 | |
---|
9 | // based on HotKeyCenter, by Quentin Carnicelli |
---|
10 | // renamed, reorganized, cleaned up, pre-10.2 support removed |
---|
11 | |
---|
12 | #import "NJRHotKeyManager.h" |
---|
13 | #import "NJRHotKey.h" |
---|
14 | #import <Carbon/Carbon.h> |
---|
15 | |
---|
16 | const OSType kHotKeyManagerSignature = 'NHKM'; |
---|
17 | |
---|
18 | @interface _NJRHotKeyShortcut : NSObject { |
---|
19 | @public |
---|
20 | BOOL isRegistered; |
---|
21 | EventHotKeyRef hotKeyRef; |
---|
22 | NJRHotKey *hotKey; |
---|
23 | id target; |
---|
24 | SEL action; |
---|
25 | } |
---|
26 | @end |
---|
27 | |
---|
28 | @implementation _NJRHotKeyShortcut |
---|
29 | |
---|
30 | - (void)dealloc; |
---|
31 | { |
---|
32 | [hotKey release]; |
---|
33 | [target release]; |
---|
34 | } |
---|
35 | |
---|
36 | @end |
---|
37 | |
---|
38 | pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon); |
---|
39 | |
---|
40 | @interface NJRHotKeyManager (Private) |
---|
41 | - (OSStatus)_handleHotKeyEvent:(EventRef)inEvent; |
---|
42 | - (BOOL)_registerHotKeyIfNeeded:(_NJRHotKeyShortcut *)shortcut; |
---|
43 | - (void)_unregisterHotKeyIfNeeded:(_NJRHotKeyShortcut *)shortcut; |
---|
44 | - (void)_hotKeyDown:(_NJRHotKeyShortcut *)hotKey; |
---|
45 | - (void)_hotKeyUp:(_NJRHotKeyShortcut *)hotKey; |
---|
46 | - (void)_hotKeyDownWithRef:(EventHotKeyRef)ref; |
---|
47 | - (void)_hotKeyUpWithRef:(EventHotKeyRef)ref; |
---|
48 | - (_NJRHotKeyShortcut *)_findShortcutWithRef:(EventHotKeyRef)ref; |
---|
49 | @end |
---|
50 | |
---|
51 | @implementation NJRHotKeyManager |
---|
52 | |
---|
53 | + (NJRHotKeyManager *)sharedManager; |
---|
54 | { |
---|
55 | static NJRHotKeyManager *manager = nil; |
---|
56 | |
---|
57 | if (manager == nil) { |
---|
58 | manager = [[self alloc] init]; |
---|
59 | |
---|
60 | EventTypeSpec eventSpec[2] = { |
---|
61 | { kEventClassKeyboard, kEventHotKeyPressed }, |
---|
62 | { kEventClassKeyboard, kEventHotKeyReleased } |
---|
63 | }; |
---|
64 | |
---|
65 | InstallEventHandler(GetEventDispatcherTarget(), |
---|
66 | NewEventHandlerUPP((EventHandlerProcPtr) keyEventHandler), |
---|
67 | 2, eventSpec, nil, nil); |
---|
68 | } |
---|
69 | return manager; |
---|
70 | } |
---|
71 | |
---|
72 | - (id)init; |
---|
73 | { |
---|
74 | if ( (self = [super init]) != nil) { |
---|
75 | shortcutsEnabled = YES; |
---|
76 | shortcuts = [[NSMutableDictionary alloc] init]; |
---|
77 | } |
---|
78 | |
---|
79 | return self; |
---|
80 | } |
---|
81 | |
---|
82 | - (void)dealloc; |
---|
83 | { |
---|
84 | [shortcuts release]; |
---|
85 | [super dealloc]; |
---|
86 | } |
---|
87 | |
---|
88 | #pragma mark - |
---|
89 | |
---|
90 | - (BOOL)addShortcutWithIdentifier:(NSString *)identifier hotKey:(NJRHotKey *)hotKey target:(id)target action:(SEL)action; |
---|
91 | { |
---|
92 | NSParameterAssert(identifier != nil); |
---|
93 | NSParameterAssert(hotKey != nil); |
---|
94 | NSParameterAssert(target != nil); |
---|
95 | NSParameterAssert(action != nil); |
---|
96 | |
---|
97 | if ([shortcuts objectForKey: identifier] != nil) |
---|
98 | [self removeShortcutWithIdentifier: identifier]; |
---|
99 | |
---|
100 | _NJRHotKeyShortcut *newShortcut = [[_NJRHotKeyShortcut alloc] init]; |
---|
101 | newShortcut->isRegistered = NO; |
---|
102 | newShortcut->hotKeyRef = nil; |
---|
103 | newShortcut->hotKey = [hotKey retain]; |
---|
104 | newShortcut->target = [target retain]; |
---|
105 | newShortcut->action = action; |
---|
106 | |
---|
107 | [shortcuts setObject: newShortcut forKey: identifier]; |
---|
108 | [newShortcut release]; |
---|
109 | |
---|
110 | return [self _registerHotKeyIfNeeded: newShortcut]; |
---|
111 | } |
---|
112 | |
---|
113 | - (void)removeShortcutWithIdentifier:(NSString *)identifier; |
---|
114 | { |
---|
115 | _NJRHotKeyShortcut *hotKey = [shortcuts objectForKey: identifier]; |
---|
116 | |
---|
117 | if (hotKey == nil) return; |
---|
118 | [self _unregisterHotKeyIfNeeded: hotKey]; |
---|
119 | [shortcuts removeObjectForKey: identifier]; |
---|
120 | } |
---|
121 | |
---|
122 | - (NSArray *)shortcutIdentifiers; |
---|
123 | { |
---|
124 | return [shortcuts allKeys]; |
---|
125 | } |
---|
126 | |
---|
127 | - (NJRHotKey *)hotKeyForShortcutWithIdentifier:(NSString *)identifier; |
---|
128 | { |
---|
129 | _NJRHotKeyShortcut *hotKey = [shortcuts objectForKey: identifier]; |
---|
130 | |
---|
131 | return (hotKey == nil ? nil : [[hotKey->hotKey retain] autorelease]); |
---|
132 | } |
---|
133 | |
---|
134 | - (void)setShortcutsEnabled:(BOOL)enabled; |
---|
135 | { |
---|
136 | NSEnumerator *enumerator = [shortcuts objectEnumerator]; |
---|
137 | _NJRHotKeyShortcut *hotKey; |
---|
138 | |
---|
139 | while ( (hotKey = [enumerator nextObject]) != nil) { |
---|
140 | if (enabled) |
---|
141 | [self _registerHotKeyIfNeeded: hotKey]; |
---|
142 | else |
---|
143 | [self _unregisterHotKeyIfNeeded: hotKey]; |
---|
144 | } |
---|
145 | shortcutsEnabled = enabled; |
---|
146 | } |
---|
147 | |
---|
148 | - (BOOL)shortcutsEnabled; |
---|
149 | { |
---|
150 | return shortcutsEnabled; |
---|
151 | } |
---|
152 | |
---|
153 | #pragma mark - |
---|
154 | |
---|
155 | - (OSStatus)_handleHotKeyEvent:(EventRef)inEvent; |
---|
156 | { |
---|
157 | OSStatus err; |
---|
158 | EventHotKeyID hotKeyID; |
---|
159 | _NJRHotKeyShortcut *shortcut; |
---|
160 | |
---|
161 | NSAssert(GetEventClass(inEvent) == kEventClassKeyboard, @"Unhandled event class"); |
---|
162 | |
---|
163 | if ( (err = GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, nil, |
---|
164 | sizeof(EventHotKeyID), nil, &hotKeyID)) != noErr) |
---|
165 | return err; |
---|
166 | |
---|
167 | NSAssert(hotKeyID.signature == kHotKeyManagerSignature, @"Unknown hot key"); |
---|
168 | |
---|
169 | shortcut = (_NJRHotKeyShortcut *)hotKeyID.id; |
---|
170 | NSAssert(shortcut != nil, @"Got bad hot key"); |
---|
171 | |
---|
172 | switch (GetEventKind(inEvent)) { |
---|
173 | case kEventHotKeyPressed: |
---|
174 | [self _hotKeyDown: shortcut]; |
---|
175 | break; |
---|
176 | case kEventHotKeyReleased: |
---|
177 | [self _hotKeyUp: shortcut]; |
---|
178 | break; |
---|
179 | default: |
---|
180 | break; |
---|
181 | } |
---|
182 | |
---|
183 | return noErr; |
---|
184 | } |
---|
185 | |
---|
186 | #pragma mark - |
---|
187 | |
---|
188 | - (BOOL)_registerHotKeyIfNeeded:(_NJRHotKeyShortcut *)shortcut; |
---|
189 | { |
---|
190 | NJRHotKey *hotKey; |
---|
191 | |
---|
192 | NSParameterAssert(shortcut != nil); |
---|
193 | |
---|
194 | hotKey = shortcut->hotKey; |
---|
195 | |
---|
196 | if (shortcutsEnabled && !(shortcut->isRegistered)) { |
---|
197 | EventHotKeyID keyID; |
---|
198 | OSStatus err; |
---|
199 | |
---|
200 | keyID.signature = kHotKeyManagerSignature; |
---|
201 | keyID.id = (unsigned long)shortcut; |
---|
202 | if ( (err = RegisterEventHotKey([hotKey keyCode], [hotKey modifiers], keyID, GetEventDispatcherTarget(), 0, &shortcut->hotKeyRef)) != noErr) |
---|
203 | return NO; |
---|
204 | |
---|
205 | shortcut->isRegistered = YES; |
---|
206 | } |
---|
207 | |
---|
208 | return YES; |
---|
209 | } |
---|
210 | |
---|
211 | - (void)_unregisterHotKeyIfNeeded:(_NJRHotKeyShortcut *)shortcut; |
---|
212 | { |
---|
213 | NSParameterAssert(shortcut != nil); |
---|
214 | |
---|
215 | if (shortcut->isRegistered && shortcut->hotKeyRef != nil) |
---|
216 | UnregisterEventHotKey(shortcut->hotKeyRef); |
---|
217 | } |
---|
218 | |
---|
219 | - (void)_hotKeyDown:(_NJRHotKeyShortcut *)hotKey; |
---|
220 | { |
---|
221 | id target = hotKey->target; |
---|
222 | SEL action = hotKey->action; |
---|
223 | |
---|
224 | [target performSelector: action withObject: self]; |
---|
225 | } |
---|
226 | |
---|
227 | - (void)_hotKeyUp:(_NJRHotKeyShortcut *)hotKey; |
---|
228 | { |
---|
229 | } |
---|
230 | |
---|
231 | - (void)_hotKeyDownWithRef:(EventHotKeyRef)ref; |
---|
232 | { |
---|
233 | _NJRHotKeyShortcut *hotKey = [self _findShortcutWithRef: ref]; |
---|
234 | |
---|
235 | if (hotKey != nil) |
---|
236 | [self _hotKeyDown: hotKey]; |
---|
237 | } |
---|
238 | |
---|
239 | - (void)_hotKeyUpWithRef:(EventHotKeyRef)ref; |
---|
240 | { |
---|
241 | } |
---|
242 | |
---|
243 | - (_NJRHotKeyShortcut *)_findShortcutWithRef:(EventHotKeyRef)ref; |
---|
244 | { |
---|
245 | NSEnumerator *enumerator = [shortcuts objectEnumerator]; |
---|
246 | _NJRHotKeyShortcut *hotKey; |
---|
247 | |
---|
248 | while ( (hotKey = [enumerator nextObject]) != nil) { |
---|
249 | if (hotKey->hotKeyRef == ref) |
---|
250 | return hotKey; |
---|
251 | } |
---|
252 | return nil; |
---|
253 | } |
---|
254 | |
---|
255 | @end |
---|
256 | |
---|
257 | pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon) |
---|
258 | { |
---|
259 | return [[NJRHotKeyManager sharedManager] _handleHotKeyEvent: inEvent]; |
---|
260 | } |
---|