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