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