source: psi/trunk/src/tools/globalaccel/globalaccelman_mac.cpp

Last change on this file was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 9.8 KB
Line 
1/*
2 * globalaccelman_mac.cpp - manager for global hotkeys
3 * Copyright (C) 2003 Eric Smith
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21// TODO:
22// - don't invoke hotkey if there is a modal dialog?
23// - do multi-mapping, like the x11 version
24
25#include"globalaccelman.h"
26
27#include<qkeysequence.h>
28#include<qwidget.h>
29#include<qptrlist.h>
30
31#include <Carbon/Carbon.h>
32
33typedef struct {
34 short kchrID;
35 Str255 KCHRname;
36 short transtable[256];
37} Ascii2KeyCodeTable;
38
39
40
41 /* InitAscii2KeyCodeTable initializes the ascii to key code
42 look up table using the currently active KCHR resource. This
43 routine calls GetResource so it cannot be called at interrupt time.*/
44OSStatus InitAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable);
45
46 /* ValidateAscii2KeyCodeTable verifies that the ascii to key code
47 lookup table is synchronized with the current KCHR resource. If
48 it is not synchronized, then the table is re-built. This routine calls
49 GetResource so it cannot be called at interrupt time.*/
50OSStatus ValidateAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable,Boolean *wasChanged);
51
52 /* AsciiToKeyCode looks up the ascii character in the key
53 code look up table and returns the virtual key code for that
54 letter. If there is no virtual key code for that letter, then
55 the value -1 will be returned. */
56short AsciiToKeyCode(Ascii2KeyCodeTable *ttable, short asciiCode);
57
58enum {
59 kTableCountOffset = 256+2,
60 kFirstTableOffset = 256+4,
61 kTableSize = 128
62};
63
64OSStatus InitAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable) {
65 unsigned char *theCurrentKCHR, *ithKeyTable;
66 short count, i, j, resID;
67 Handle theKCHRRsrc;
68 ResType rType;
69 /* set up our table to all minus ones */
70 for (i=0;i<256; i++) ttable->transtable[i] = -1;
71 /* find the current kchr resource ID */
72 ttable->kchrID = (short) GetScriptVariable(smCurrentScript, smScriptKeys);
73 /* get the current KCHR resource */
74 theKCHRRsrc = GetResource('KCHR', ttable->kchrID);
75 if (theKCHRRsrc == NULL) return resNotFound;
76 GetResInfo(theKCHRRsrc,&resID,&rType,ttable->KCHRname);
77 /* dereference the resource */
78 theCurrentKCHR = (unsigned char *) (*theKCHRRsrc);
79 /* get the count from the resource */
80 count = * (short *) (theCurrentKCHR + kTableCountOffset);
81 /* build inverse table by merging all key tables */
82 for (i=0; i<count; i++) {
83 ithKeyTable = theCurrentKCHR + kFirstTableOffset + (i * kTableSize);
84 for (j=0; j<kTableSize; j++) {
85 if ( ttable->transtable[ ithKeyTable[j] ] == -1)
86 ttable->transtable[ ithKeyTable[j] ] = j;
87 }
88 }
89 /* all done */
90 return noErr;
91}
92
93
94// Should probably call this at some point, in case the user has switched keyboard
95// layouts while we were running.
96OSStatus ValidateAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable,Boolean *wasChanged) {
97 short theID;
98 theID = (short) GetScriptVariable(smCurrentScript, smScriptKeys);
99 if (theID != ttable->kchrID)
100 {
101 *wasChanged=true;
102 return InitAscii2KeyCodeTable(ttable);
103 }
104 else
105 {
106 *wasChanged=false;
107 return noErr;
108 }
109}
110
111short AsciiToKeyCode(Ascii2KeyCodeTable *ttable, short asciiCode) {
112 if (asciiCode >= 0 && asciiCode <= 255)
113 return ttable->transtable[asciiCode];
114 else return false;
115}
116
117
118// Not used.
119char KeyCodeToAscii(short virtualKeyCode) {
120 unsigned long state;
121 long keyTrans;
122 char charCode;
123 Ptr kchr;
124 state = 0;
125 kchr = (Ptr) GetScriptVariable(smCurrentScript, smKCHRCache);
126 keyTrans = KeyTranslate(kchr, virtualKeyCode, &state);
127 charCode = keyTrans;
128 if (!charCode) charCode = (keyTrans>>16);
129 return charCode;
130}
131
132
133EventHotKeyID hotKeyID = {
134 'QtHK',
135 0
136};
137
138
139class MacHotKey
140{
141public:
142 MacHotKey(EventHotKeyRef _ref, int _id)
143 {
144 hot_key_ref = _ref;
145 id = _id;
146 }
147
148 ~MacHotKey()
149 {
150 UnregisterEventHotKey(hot_key_ref);
151 }
152
153 EventHotKeyRef hot_key_ref;
154 int id; // GlobalAccelManager unique ID for this hotkey
155};
156
157
158// The following table is from Apple sample-code.
159// Apple's headers don't appear to define any constants for the virtual key
160// codes of special keys, but these constants are somewhat documented in the chart at
161// <http://developer.apple.com/techpubs/mac/Text/Text-571.html#MARKER-9-18>
162//
163// The constants on the chartappear to be the same values as are used in Apple's iGetKeys
164// sample.
165// <http://developer.apple.com/samplecode/Sample_Code/Human_Interface_Toolbox/Mac_OS_High_Level_Toolbox/iGetKeys.htm>.
166//
167// See also <http://www.mactech.com/articles/mactech/Vol.04/04.12/Macinkeys/>.
168//
169struct QtKeyMap
170{
171 int qt_key;
172 int mac_key;
173} qt_key_map[] = {
174 { Qt::Key_Escape, 0x35 },
175 { Qt::Key_Tab, 0x30 },
176 { Qt::Key_Backtab, 0 },
177 { Qt::Key_Backspace, 0x33 },
178 { Qt::Key_Return, 0x24 },
179 { Qt::Key_Enter, 0x4c }, // Return & Enter are different on the Mac
180 { Qt::Key_Insert, 0 },
181 { Qt::Key_Delete, 0x75 },
182 { Qt::Key_Pause, 0 },
183 { Qt::Key_Print, 0 },
184 { Qt::Key_SysReq, 0 },
185 { Qt::Key_Clear, 0x47 },
186 { Qt::Key_Home, 0x73 },
187 { Qt::Key_End, 0x77 },
188 { Qt::Key_Left, 0x7b },
189 { Qt::Key_Up, 0x7e },
190 { Qt::Key_Right, 0x7c },
191 { Qt::Key_Down, 0x7d },
192 { Qt::Key_Prior, 0x74 }, // Page Up
193 { Qt::Key_Next, 0x79 }, // Page Down
194 { Qt::Key_Shift, 0x38 },
195 { Qt::Key_Control, 0x3b },
196 { Qt::Key_Meta, 0x37 }, // Command
197 { Qt::Key_Alt, 0x3a }, // Option
198 { Qt::Key_CapsLock, 57 },
199 { Qt::Key_NumLock, 0 },
200 { Qt::Key_ScrollLock, 0 },
201 { Qt::Key_F1, 0x7a },
202 { Qt::Key_F2, 0x78 },
203 { Qt::Key_F3, 0x63 },
204 { Qt::Key_F4, 0x76 },
205 { Qt::Key_F5, 0x60 },
206 { Qt::Key_F6, 0x61 },
207 { Qt::Key_F7, 0x62 },
208 { Qt::Key_F8, 0x64 },
209 { Qt::Key_F9, 0x65 },
210 { Qt::Key_F10, 0x6d },
211 { Qt::Key_F11, 0x67 },
212 { Qt::Key_F12, 0x6f },
213 { Qt::Key_F13, 0x69 },
214 { Qt::Key_F14, 0x6b },
215 { Qt::Key_F15, 0x71 },
216 { Qt::Key_F16, 0 },
217 { Qt::Key_F17, 0 },
218 { Qt::Key_F18, 0 },
219 { Qt::Key_F19, 0 },
220 { Qt::Key_F20, 0 },
221 { Qt::Key_F21, 0 },
222 { Qt::Key_F22, 0 },
223 { Qt::Key_F23, 0 },
224 { Qt::Key_F24, 0 },
225 { Qt::Key_F25, 0 },
226 { Qt::Key_F26, 0 },
227 { Qt::Key_F27, 0 },
228 { Qt::Key_F28, 0 },
229 { Qt::Key_F29, 0 },
230 { Qt::Key_F30, 0 },
231 { Qt::Key_F31, 0 },
232 { Qt::Key_F32, 0 },
233 { Qt::Key_F33, 0 },
234 { Qt::Key_F34, 0 },
235 { Qt::Key_F35, 0 },
236 { Qt::Key_Super_L, 0 },
237 { Qt::Key_Super_R, 0 },
238 { Qt::Key_Menu, 0 },
239 { Qt::Key_Hyper_L, 0 },
240 { Qt::Key_Hyper_R, 0 },
241 { Qt::Key_Help, 0x72 },
242 { Qt::Key_Direction_L,0 },
243 { Qt::Key_Direction_R,0 },
244
245 { Qt::Key_unknown, 0 }
246};
247
248
249class GlobalAccelManager::Private
250{
251public:
252 Private(GlobalAccelManager *par)
253 {
254 id_at = 1;
255 list.setAutoDelete(true);
256
257 InitAscii2KeyCodeTable(&key_codes);
258 hot_key_function = NewEventHandlerUPP(HotKeyHandler);
259 EventTypeSpec type;
260 type.eventClass = kEventClassKeyboard;
261 type.eventKind = kEventHotKeyPressed;
262 InstallApplicationEventHandler(hot_key_function, 1, &type, par, NULL);
263 }
264
265 ~Private() {
266 }
267
268private:
269 static pascal OSStatus HotKeyHandler(EventHandlerCallRef /*nextHandler*/, EventRef theEvent, void *userData);
270
271public:
272 bool convertKeySequence(const QKeySequence &ks, UInt32 *_mod, UInt32 *_key)
273{
274 int code = ks[0];
275
276 UInt32 mod = 0;
277 if(code & Qt::META)
278 mod |= cmdKey;
279 if(code & Qt::SHIFT)
280 mod |= shiftKey;
281 if(code & Qt::CTRL)
282 mod |= controlKey;
283 if(code & Qt::ALT)
284 mod |= optionKey;
285
286 code &= 0xffff;
287 UInt32 key = 0;
288 for(int n = 0; qt_key_map[n].qt_key != Qt::Key_unknown; ++n) {
289 if(qt_key_map[n].qt_key == code) {
290 key = qt_key_map[n].mac_key;
291 break;
292 }
293 }
294 if (key == 0) {
295 key = AsciiToKeyCode(&key_codes, code & 0xffff);
296 }
297
298 if(_mod)
299 *_mod = mod;
300 if(_key)
301 *_key = key;
302
303 return true;
304}
305
306
307 int insertHotKey(int key, int mod)
308 {
309 EventHotKeyID hotKeyID;
310 hotKeyID.signature = 'QtHK';
311 hotKeyID.id = id_at++;
312 EventHotKeyRef hotKeyRef = 0;
313
314 OSStatus foo = RegisterEventHotKey(key, mod, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef);
315 if (foo != 0) {
316 fprintf(stderr, "RegisterEventHotKey(%x,%x) returned %ld\n", key, mod, foo);
317 return 0;
318 }
319
320 MacHotKey *hk = new MacHotKey(hotKeyRef, hotKeyID.id);
321 list.append(hk);
322 return hk->id;
323 }
324
325
326 int id_at;
327 QPtrList<MacHotKey> list;
328 EventHandlerUPP hot_key_function;
329 Ascii2KeyCodeTable key_codes;
330};
331
332
333// Callback function invoked when the user hits a hot-key.
334pascal OSStatus GlobalAccelManager::Private::HotKeyHandler(EventHandlerCallRef /*nextHandler*/, EventRef theEvent, void *userData)
335{
336 EventHotKeyID hkID;
337 GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(EventHotKeyID), NULL, &hkID);
338 reinterpret_cast<GlobalAccelManager*>(userData)->triggerActivated(hkID.id);
339 return noErr;
340}
341
342
343GlobalAccelManager::GlobalAccelManager()
344{
345 d = new Private(this);
346}
347
348GlobalAccelManager::~GlobalAccelManager()
349{
350 delete d;
351}
352
353int GlobalAccelManager::setAccel(const QKeySequence &ks)
354{
355
356 UInt32 mod, key;
357 if(!d->convertKeySequence(ks, &mod, &key))
358 return 0;
359
360 return d->insertHotKey(key, mod);
361}
362
363void GlobalAccelManager::removeAccel(int id)
364{
365 QPtrListIterator<MacHotKey> it(d->list);
366 for(MacHotKey *hk = 0; (hk = it.current()); ++it) {
367 if(hk->id == id) {
368 d->list.removeRef(hk);
369 return;
370 }
371 }
372}
Note: See TracBrowser for help on using the repository browser.