Use a struct as specified in the VBE 3.0 standard for the
[wine] / dlls / comctl32 / hotkey.c
1 /*
2  * Hotkey control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 2002 Gyorgy 'Nog' Jeney
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * TODO:
22  *   - What are we meant to do with the WM_CHAR message?
23  */
24
25 #include <stdarg.h>
26 #include <string.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "winnls.h"
32 #include "commctrl.h"
33 #include "comctl32.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(hotkey);
37
38 typedef struct tagHOTKEY_INFO
39 {
40     HWND  hwndSelf;
41     HWND  hwndNotify;
42     HFONT hFont;
43     BOOL  bFocus;
44     INT   nHeight;
45     WORD  HotKey;
46     WORD  InvComb;
47     WORD  InvMod;
48     BYTE  CurrMod;
49     INT   CaretPos;
50     DWORD ScanCode;
51     WCHAR strNone[15]; /* hope its long enough ... */
52 } HOTKEY_INFO;
53
54 #define HOTKEY_GetInfoPtr(hwnd) ((HOTKEY_INFO *)GetWindowLongA (hwnd, 0))
55
56 static const WCHAR HOTKEY_plussep[] = { ' ', '+', ' ' };
57
58 #define IsOnlySet(flags) (infoPtr->CurrMod == (flags))
59
60 static BOOL
61 HOTKEY_IsCombInv(HOTKEY_INFO *infoPtr)
62 {
63     TRACE("(infoPtr=%p)\n", infoPtr);
64     if((infoPtr->InvComb & HKCOMB_NONE) && !infoPtr->CurrMod)
65         return TRUE;
66     if((infoPtr->InvComb & HKCOMB_S) && IsOnlySet(HOTKEYF_SHIFT))
67         return TRUE;
68     if((infoPtr->InvComb & HKCOMB_C) && IsOnlySet(HOTKEYF_CONTROL))
69         return TRUE;
70     if((infoPtr->InvComb & HKCOMB_A) && IsOnlySet(HOTKEYF_ALT))
71         return TRUE;
72     if((infoPtr->InvComb & HKCOMB_SC) && 
73        IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL))
74         return TRUE;
75     if((infoPtr->InvComb & HKCOMB_SA) && IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_ALT))
76         return TRUE;
77     if((infoPtr->InvComb & HKCOMB_CA) && 
78        IsOnlySet(HOTKEYF_CONTROL | HOTKEYF_ALT))
79         return TRUE;
80     if((infoPtr->InvComb & HKCOMB_SCA) && 
81        IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL | HOTKEYF_ALT))
82         return TRUE;
83
84     TRACE("() Modifiers are valid\n");
85     return FALSE;
86 }
87 #undef IsOnlySet
88
89 static void
90 HOTKEY_DrawHotKey(HOTKEY_INFO *infoPtr, LPCWSTR KeyName, WORD NameLen, 
91                   LPRECT rc, HDC hdc)
92 {
93     SIZE TextSize;
94     DWORD dwExStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_EXSTYLE);
95
96     /* We have to allow some space for the frame to be drawn */
97     rc->left += 2;
98     rc->top++;
99     DrawTextW(hdc, KeyName, NameLen, rc, DT_LEFT | DT_VCENTER);
100     rc->left -= 2;
101     rc->top--;
102     if(dwExStyle & WS_EX_CLIENTEDGE)
103         DrawEdge(hdc, rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
104
105     /* Get the text size and position the caret accordingly */
106     GetTextExtentPoint32W (hdc, KeyName, NameLen, &TextSize);
107     infoPtr->CaretPos = TextSize.cx + 2;
108     SetCaretPos(infoPtr->CaretPos, 3);
109 }
110
111 /* Draw the names of the keys in the control */
112 static void 
113 HOTKEY_Refresh(HOTKEY_INFO *infoPtr, HDC hdc)
114 {
115     WCHAR KeyName[sizeof(WCHAR) * 64];
116     WORD NameLen = 0;
117     BYTE Modifier;
118     RECT rc;
119
120     GetClientRect(infoPtr->hwndSelf, &rc);
121
122     TRACE("(infoPtr=%p hdc=%p)\n", infoPtr, hdc);
123
124     if(!infoPtr->CurrMod && !infoPtr->HotKey) {
125         HOTKEY_DrawHotKey (infoPtr, infoPtr->strNone, 4, &rc, hdc);
126         return;
127     }
128         
129     if(infoPtr->HotKey)
130         Modifier = HIBYTE(infoPtr->HotKey);
131     else if(HOTKEY_IsCombInv(infoPtr)) 
132         Modifier = infoPtr->InvMod;
133     else
134         Modifier = infoPtr->CurrMod;
135
136     if(Modifier & HOTKEYF_CONTROL) {
137         GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_CONTROL, 0)),
138                                   KeyName, 64);
139         NameLen = lstrlenW(KeyName);
140         memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
141         NameLen += 3;
142     }
143     if(Modifier & HOTKEYF_SHIFT) {
144         GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_SHIFT, 0)),
145                                    &KeyName[NameLen], 64 - NameLen);
146         NameLen = lstrlenW(KeyName);
147         memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
148         NameLen += 3;
149     }
150     if(Modifier & HOTKEYF_ALT) {
151         GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_MENU, 0)),
152                                    &KeyName[NameLen], 64 - NameLen);
153         NameLen = lstrlenW(KeyName);
154         memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
155         NameLen += 3;
156     }
157
158     if(infoPtr->HotKey) {
159         GetKeyNameTextW(infoPtr->ScanCode, &KeyName[NameLen], 64 - NameLen);
160         NameLen = lstrlenW(KeyName);
161     }
162     else
163         KeyName[NameLen] = 0;
164
165     HOTKEY_DrawHotKey (infoPtr, KeyName, NameLen, &rc, hdc);
166 }
167
168 static void
169 HOTKEY_Paint(HOTKEY_INFO *infoPtr, HDC hdc)
170 {
171     if (hdc)
172         HOTKEY_Refresh(infoPtr, hdc);
173     else {
174         PAINTSTRUCT ps;
175         hdc = BeginPaint (infoPtr->hwndSelf, &ps);
176         HOTKEY_Refresh (infoPtr, hdc);
177         EndPaint (infoPtr->hwndSelf, &ps);
178     }
179 }
180
181 static LRESULT
182 HOTKEY_GetHotKey(HOTKEY_INFO *infoPtr)
183 {
184     TRACE("(infoPtr=%p) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr, 
185           HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey));
186     return (LRESULT)infoPtr->HotKey;
187 }
188
189 static void
190 HOTKEY_SetHotKey(HOTKEY_INFO *infoPtr, WPARAM wParam)
191 {
192     infoPtr->HotKey = (WORD)wParam;
193     infoPtr->ScanCode = 
194         MAKELPARAM(0, MapVirtualKeyW(LOBYTE(infoPtr->HotKey), 0));
195     TRACE("(infoPtr=%p wParam=%x) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr,
196           wParam, HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey));
197     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
198 }
199
200 static void 
201 HOTKEY_SetRules(HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
202 {
203     infoPtr->InvComb = (WORD)wParam;
204     infoPtr->InvMod = (WORD)lParam;
205     TRACE("(infoPtr=%p) Invalid Modifers: 0x%x, If Invalid: 0x%x\n", infoPtr,
206           infoPtr->InvComb, infoPtr->InvMod);
207 }
208
209 /* << HOTKEY_Char >> */
210
211 static LRESULT
212 HOTKEY_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
213 {
214     HOTKEY_INFO *infoPtr;
215     TEXTMETRICW tm;
216     HDC hdc;
217
218     /* allocate memory for info structure */
219     infoPtr = (HOTKEY_INFO *)Alloc (sizeof(HOTKEY_INFO));
220     SetWindowLongW (hwnd, 0, (DWORD)infoPtr);
221
222     /* initialize info structure */
223     infoPtr->HotKey = infoPtr->InvComb = infoPtr->InvMod = infoPtr->CurrMod = 0;
224     infoPtr->CaretPos = 2;
225     infoPtr->hwndSelf = hwnd;
226     infoPtr->hwndNotify = ((LPCREATESTRUCTA)lParam)->hwndParent;
227     LoadStringW(COMCTL32_hModule, HKY_NONE, infoPtr->strNone, 15);
228
229     /* get default font height */
230     hdc = GetDC (hwnd);
231     GetTextMetricsW (hdc, &tm);
232     infoPtr->nHeight = tm.tmHeight;
233     ReleaseDC (hwnd, hdc);
234
235     return 0;
236 }
237
238
239 static LRESULT
240 HOTKEY_Destroy (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
241 {
242     HWND hwnd = infoPtr->hwndSelf;
243     /* free hotkey info data */
244     Free (infoPtr);
245     SetWindowLongW (hwnd, 0, 0);
246     return 0;
247 }
248
249
250 static LRESULT
251 HOTKEY_EraseBackground (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
252 {
253     HBRUSH hBrush;
254     RECT   rc;
255
256     hBrush =
257         (HBRUSH)SendMessageW (infoPtr->hwndNotify, WM_CTLCOLOREDIT,
258                                 wParam, (LPARAM)infoPtr->hwndSelf);
259     if (hBrush)
260         hBrush = (HBRUSH)GetStockObject (WHITE_BRUSH);
261     GetClientRect (infoPtr->hwndSelf, &rc);
262
263     FillRect ((HDC)wParam, &rc, hBrush);
264
265     return -1;
266 }
267
268
269 inline static LRESULT
270 HOTKEY_GetFont (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
271 {
272     return (LRESULT)infoPtr->hFont;
273 }
274
275 static LRESULT
276 HOTKEY_KeyDown (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
277 {
278     TRACE("() Key: %d\n", wParam);
279     /* If any key is Pressed, we have to reset the hotkey in the control */
280     infoPtr->HotKey = 0;
281
282     switch (wParam) {
283         case VK_RETURN:
284         case VK_TAB:
285         case VK_SPACE:
286         case VK_DELETE:
287         case VK_ESCAPE:
288         case VK_BACK:
289             InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
290             return DefWindowProcW (infoPtr->hwndSelf, WM_KEYDOWN, wParam, 
291                                    lParam);
292
293         case VK_SHIFT:
294             infoPtr->CurrMod |= HOTKEYF_SHIFT;
295             break;
296         case VK_CONTROL:
297             infoPtr->CurrMod |= HOTKEYF_CONTROL;
298             break;
299         case VK_MENU:
300             infoPtr->CurrMod |= HOTKEYF_ALT;
301             break;
302
303         default:
304             if(HOTKEY_IsCombInv(infoPtr))
305                 infoPtr->HotKey = MAKEWORD(wParam, infoPtr->InvMod);
306             else
307                 infoPtr->HotKey = MAKEWORD(wParam, infoPtr->CurrMod);
308             infoPtr->ScanCode = lParam;
309             break;
310     }
311
312     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
313     return 0;
314 }
315
316
317 static LRESULT
318 HOTKEY_KeyUp (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
319 {
320     TRACE("() Key: %d\n", wParam);
321     switch (wParam) {
322         case VK_SHIFT:
323             infoPtr->CurrMod &= ~HOTKEYF_SHIFT;
324             break;
325         case VK_CONTROL:
326             infoPtr->CurrMod &= ~HOTKEYF_CONTROL;
327             break;
328         case VK_MENU:
329             infoPtr->CurrMod &= ~HOTKEYF_ALT;
330             break;
331         default:
332             return 1;
333     }
334
335     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
336
337     return 0;
338 }
339
340
341 static LRESULT
342 HOTKEY_KillFocus (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
343 {
344     infoPtr->bFocus = FALSE;
345     DestroyCaret ();
346
347     return 0;
348 }
349
350
351 static LRESULT
352 HOTKEY_LButtonDown (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
353 {
354     SetFocus (infoPtr->hwndSelf);
355
356     return 0;
357 }
358
359
360 inline static LRESULT
361 HOTKEY_NCCreate (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
362 {
363     DWORD dwExStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_EXSTYLE);
364     SetWindowLongW (infoPtr->hwndSelf, GWL_EXSTYLE, 
365                     dwExStyle | WS_EX_CLIENTEDGE);
366     return DefWindowProcW (infoPtr->hwndSelf, WM_NCCREATE, wParam, lParam);
367 }
368
369 static LRESULT
370 HOTKEY_SetFocus (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
371 {
372     infoPtr->bFocus = TRUE;
373
374
375     CreateCaret (infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight - 2);
376
377     SetCaretPos (infoPtr->CaretPos, 3);
378
379     ShowCaret (infoPtr->hwndSelf);
380
381
382     return 0;
383 }
384
385
386 inline static LRESULT
387 HOTKEY_SetFont (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
388 {
389     TEXTMETRICW tm;
390     HDC hdc;
391     HFONT hOldFont = 0;
392
393     infoPtr->hFont = (HFONT)wParam;
394
395     hdc = GetDC (infoPtr->hwndSelf);
396     if (infoPtr->hFont)
397         hOldFont = SelectObject (hdc, infoPtr->hFont);
398
399     GetTextMetricsW (hdc, &tm);
400     infoPtr->nHeight = tm.tmHeight;
401
402     if (infoPtr->hFont)
403         SelectObject (hdc, hOldFont);
404     ReleaseDC (infoPtr->hwndSelf, hdc);
405
406     if (LOWORD(lParam))
407         InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
408
409     return 0;
410 }
411
412 static LRESULT WINAPI
413 HOTKEY_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
414 {
415     HOTKEY_INFO *infoPtr = HOTKEY_GetInfoPtr (hwnd);
416     TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
417     if (!infoPtr && (uMsg != WM_CREATE))
418         return DefWindowProcW (hwnd, uMsg, wParam, lParam);
419     switch (uMsg)
420     {
421         case HKM_GETHOTKEY:
422             return HOTKEY_GetHotKey (infoPtr);
423         case HKM_SETHOTKEY:
424             HOTKEY_SetHotKey (infoPtr, wParam);
425             break;
426         case HKM_SETRULES:
427             HOTKEY_SetRules (infoPtr, wParam, lParam);
428             break;
429
430 /*      case WM_CHAR: */
431
432         case WM_CREATE:
433             return HOTKEY_Create (hwnd, wParam, lParam);
434
435         case WM_DESTROY:
436             return HOTKEY_Destroy (infoPtr, wParam, lParam);
437
438         case WM_ERASEBKGND:
439             return HOTKEY_EraseBackground (infoPtr, wParam, lParam);
440
441         case WM_GETDLGCODE:
442             return DLGC_WANTCHARS | DLGC_WANTARROWS;
443
444         case WM_GETFONT:
445             return HOTKEY_GetFont (infoPtr, wParam, lParam);
446
447         case WM_KEYDOWN:
448         case WM_SYSKEYDOWN:
449             return HOTKEY_KeyDown (infoPtr, wParam, lParam);
450
451         case WM_KEYUP:
452         case WM_SYSKEYUP:
453             return HOTKEY_KeyUp (infoPtr, wParam, lParam);
454
455         case WM_KILLFOCUS:
456             return HOTKEY_KillFocus (infoPtr, wParam, lParam);
457
458         case WM_LBUTTONDOWN:
459             return HOTKEY_LButtonDown (infoPtr, wParam, lParam);
460
461         case WM_NCCREATE:
462             return HOTKEY_NCCreate (infoPtr, wParam, lParam);
463
464         case WM_PAINT:
465             HOTKEY_Paint(infoPtr, (HDC)wParam);
466             return 0;
467
468         case WM_SETFOCUS:
469             return HOTKEY_SetFocus (infoPtr, wParam, lParam);
470
471         case WM_SETFONT:
472             return HOTKEY_SetFont (infoPtr, wParam, lParam);
473
474 /*      case WM_SYSCHAR: */
475
476         default:
477             if ((uMsg >= WM_USER) && (uMsg < WM_APP))
478                 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
479                      uMsg, wParam, lParam);
480             return DefWindowProcW (hwnd, uMsg, wParam, lParam);
481     }
482     return 0;
483 }
484
485
486 void
487 HOTKEY_Register (void)
488 {
489     WNDCLASSW wndClass;
490
491     ZeroMemory (&wndClass, sizeof(WNDCLASSW));
492     wndClass.style         = CS_GLOBALCLASS;
493     wndClass.lpfnWndProc   = (WNDPROC)HOTKEY_WindowProc;
494     wndClass.cbClsExtra    = 0;
495     wndClass.cbWndExtra    = sizeof(HOTKEY_INFO *);
496     wndClass.hCursor       = 0;
497     wndClass.hbrBackground = 0;
498     wndClass.lpszClassName = HOTKEY_CLASSW;
499
500     RegisterClassW (&wndClass);
501 }
502
503
504 void
505 HOTKEY_Unregister (void)
506 {
507     UnregisterClassW (HOTKEY_CLASSW, NULL);
508 }