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