sync with trunk r47346
[reactos.git] / subsystems / win32 / win32k / ntuser / accelerator.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window classes
24 * FILE: subsys/win32k/ntuser/class.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * REVISION HISTORY:
27 * 06-06-2001 CSH Created
28 */
29
30 /*
31 * Copyright 1993 Martin Ayotte
32 * Copyright 1994 Alexandre Julliard
33 * Copyright 1997 Morten Welinder
34 *
35 * This library is free software; you can redistribute it and/or
36 * modify it under the terms of the GNU Lesser General Public
37 * License as published by the Free Software Foundation; either
38 * version 2.1 of the License, or (at your option) any later version.
39 *
40 * This library is distributed in the hope that it will be useful,
41 * but WITHOUT ANY WARRANTY; without even the implied warranty of
42 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
43 * Lesser General Public License for more details.
44 *
45 * You should have received a copy of the GNU Lesser General Public
46 * License along with this library; if not, write to the Free Software
47 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
48 */
49
50 /* INCLUDES ******************************************************************/
51
52 #include <win32k.h>
53
54 #define NDEBUG
55 #include <debug.h>
56
57 /* FUNCTIONS *****************************************************************/
58
59 NTSTATUS FASTCALL
60 InitAcceleratorImpl(VOID)
61 {
62 return(STATUS_SUCCESS);
63 }
64
65 NTSTATUS FASTCALL
66 CleanupAcceleratorImpl(VOID)
67 {
68 return(STATUS_SUCCESS);
69 }
70
71
72 PACCELERATOR_TABLE FASTCALL UserGetAccelObject(HACCEL hAccel)
73 {
74 PACCELERATOR_TABLE Accel;
75
76 if (!hAccel)
77 {
78 SetLastWin32Error(ERROR_INVALID_ACCEL_HANDLE);
79 return NULL;
80 }
81
82 Accel= UserGetObject(gHandleTable, hAccel, otAccel);
83 if (!Accel)
84 {
85 SetLastWin32Error(ERROR_INVALID_ACCEL_HANDLE);
86 return NULL;
87 }
88
89 ASSERT(Accel->head.cLockObj >= 0);
90
91 return Accel;
92 }
93
94
95 static
96 BOOLEAN FASTCALL
97 co_IntTranslateAccelerator(
98 PWINDOW_OBJECT Window,
99 UINT message,
100 WPARAM wParam,
101 LPARAM lParam,
102 BYTE fVirt,
103 WORD key,
104 WORD cmd)
105 {
106 UINT mesg = 0;
107
108 ASSERT_REFS_CO(Window);
109
110 DPRINT("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x)\n",
111 Window->hSelf, message, wParam, lParam, fVirt, key, cmd);
112
113 if (wParam != key)
114 {
115 return FALSE;
116 }
117
118 if (message == WM_CHAR)
119 {
120 if (!(fVirt & FALT) && !(fVirt & FVIRTKEY))
121 {
122 DPRINT("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
123 goto found;
124 }
125 }
126 else
127 {
128 if ((fVirt & FVIRTKEY) > 0)
129 {
130 INT mask = 0;
131 DPRINT("found accel for virt_key %04x (scan %04x)\n",
132 wParam, 0xff & HIWORD(lParam));
133
134 DPRINT("NtUserGetKeyState(VK_SHIFT) = 0x%x\n",
135 UserGetKeyState(VK_SHIFT));
136 DPRINT("NtUserGetKeyState(VK_CONTROL) = 0x%x\n",
137 UserGetKeyState(VK_CONTROL));
138 DPRINT("NtUserGetKeyState(VK_MENU) = 0x%x\n",
139 UserGetKeyState(VK_MENU));
140
141 if (UserGetKeyState(VK_SHIFT) & 0x8000)
142 mask |= FSHIFT;
143 if (UserGetKeyState(VK_CONTROL) & 0x8000)
144 mask |= FCONTROL;
145 if (UserGetKeyState(VK_MENU) & 0x8000)
146 mask |= FALT;
147 if (mask == (fVirt & (FSHIFT | FCONTROL | FALT)))
148 goto found;
149 DPRINT("but incorrect SHIFT/CTRL/ALT-state\n");
150 }
151 else
152 {
153 if (!(lParam & 0x01000000)) /* no special_key */
154 {
155 if ((fVirt & FALT) && (lParam & 0x20000000))
156 { /* ^^ ALT pressed */
157 DPRINT("found accel for Alt-%c\n", wParam & 0xff);
158 goto found;
159 }
160 }
161 }
162 }
163
164 DPRINT("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = FALSE\n",
165 Window->hSelf, message, wParam, lParam, fVirt, key, cmd);
166
167 return FALSE;
168
169 found:
170 if (message == WM_KEYUP || message == WM_SYSKEYUP)
171 mesg = 1;
172 else if (IntGetCaptureWindow())
173 mesg = 2;
174 else if (!IntIsWindowVisible(Window)) /* FIXME: WINE IsWindowEnabled == IntIsWindowVisible? */
175 mesg = 3;
176 else
177 {
178 #if 0
179 HMENU hMenu, hSubMenu, hSysMenu;
180 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
181
182 hMenu = (UserGetWindowLongW(hWnd, GWL_STYLE) & WS_CHILD) ? 0 : GetMenu(hWnd);
183 hSysMenu = get_win_sys_menu(hWnd);
184
185 /* find menu item and ask application to initialize it */
186 /* 1. in the system menu */
187 hSubMenu = hSysMenu;
188 nPos = cmd;
189 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
190 {
191 co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
192 if(hSubMenu != hSysMenu)
193 {
194 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
195 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
196 co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
197 }
198 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
199 }
200 else /* 2. in the window's menu */
201 {
202 hSubMenu = hMenu;
203 nPos = cmd;
204 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
205 {
206 co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
207 if(hSubMenu != hMenu)
208 {
209 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
210 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
211 co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
212 }
213 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
214 }
215 }
216
217 if (uSysStat != (UINT)-1)
218 {
219 if (uSysStat & (MF_DISABLED|MF_GRAYED))
220 mesg=4;
221 else
222 mesg=WM_SYSCOMMAND;
223 }
224 else
225 {
226 if (uStat != (UINT)-1)
227 {
228 if (IsIconic(hWnd))
229 mesg=5;
230 else
231 {
232 if (uStat & (MF_DISABLED|MF_GRAYED))
233 mesg=6;
234 else
235 mesg=WM_COMMAND;
236 }
237 }
238 else
239 {
240 mesg=WM_COMMAND;
241 }
242 }
243 #else
244 DPRINT1("Menu search not implemented\n");
245 mesg = WM_COMMAND;
246 #endif
247
248 }
249
250 if (mesg == WM_COMMAND)
251 {
252 DPRINT(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
253 co_IntSendMessage(Window->hSelf, mesg, 0x10000 | cmd, 0L);
254 }
255 else if (mesg == WM_SYSCOMMAND)
256 {
257 DPRINT(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
258 co_IntSendMessage(Window->hSelf, mesg, cmd, 0x00010000L);
259 }
260 else
261 {
262 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
263 * #0: unknown (please report!)
264 * #1: for WM_KEYUP,WM_SYSKEYUP
265 * #2: mouse is captured
266 * #3: window is disabled
267 * #4: it's a disabled system menu option
268 * #5: it's a menu option, but window is iconic
269 * #6: it's a menu option, but disabled
270 */
271 DPRINT(", but won't send WM_{SYS}COMMAND, reason is #%d\n", mesg);
272 if (mesg == 0)
273 {
274 DPRINT1(" unknown reason - please report!");
275 }
276 }
277
278 DPRINT("IntTranslateAccelerator(hWnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = TRUE\n",
279 Window->hSelf, message, wParam, lParam, fVirt, key, cmd);
280
281 return TRUE;
282 }
283
284
285 /* SYSCALLS *****************************************************************/
286
287
288 int
289 APIENTRY
290 NtUserCopyAcceleratorTable(
291 HACCEL hAccel,
292 LPACCEL Entries,
293 int EntriesCount)
294 {
295 PACCELERATOR_TABLE Accel;
296 int Ret;
297 BOOL Done = FALSE;
298 DECLARE_RETURN(int);
299
300 DPRINT("Enter NtUserCopyAcceleratorTable\n");
301 UserEnterShared();
302
303 Accel = UserGetAccelObject(hAccel);
304
305 if ((Entries && (EntriesCount < 1)) || ((hAccel == NULL) || (!Accel)))
306 {
307 RETURN(0);
308 }
309
310 if (Accel->Count < EntriesCount)
311 EntriesCount = Accel->Count;
312
313 Ret = 0;
314
315 while (!Done)
316 {
317 if (Entries)
318 {
319 Entries[Ret].fVirt = Accel->Table[Ret].fVirt & 0x7f;
320 Entries[Ret].key = Accel->Table[Ret].key;
321 Entries[Ret].cmd = Accel->Table[Ret].cmd;
322
323 if(Ret + 1 == EntriesCount) Done = TRUE;
324 }
325
326 if((Accel->Table[Ret].fVirt & 0x80) != 0) Done = TRUE;
327
328 Ret++;
329 }
330
331 RETURN(Ret);
332
333 CLEANUP:
334 DPRINT("Leave NtUserCopyAcceleratorTable, ret=%i\n",_ret_);
335 UserLeave();
336 END_CLEANUP;
337 }
338
339 HACCEL
340 APIENTRY
341 NtUserCreateAcceleratorTable(
342 LPACCEL Entries,
343 SIZE_T EntriesCount)
344 {
345 PACCELERATOR_TABLE Accel;
346 HACCEL hAccel;
347 INT Index;
348 DECLARE_RETURN(HACCEL);
349
350 DPRINT("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d)\n",
351 Entries, EntriesCount);
352 UserEnterExclusive();
353
354 if (!Entries || EntriesCount < 1)
355 {
356 SetLastNtError(STATUS_INVALID_PARAMETER);
357 RETURN( (HACCEL) NULL );
358 }
359
360 Accel = UserCreateObject(gHandleTable, NULL, (PHANDLE)&hAccel, otAccel, sizeof(ACCELERATOR_TABLE));
361
362 if (Accel == NULL)
363 {
364 SetLastNtError(STATUS_NO_MEMORY);
365 RETURN( (HACCEL) NULL );
366 }
367
368 Accel->Count = EntriesCount;
369 if (Accel->Count > 0)
370 {
371 Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), TAG_ACCEL);
372 if (Accel->Table == NULL)
373 {
374 UserDereferenceObject(Accel);
375 UserDeleteObject(hAccel, otAccel);
376 SetLastNtError(STATUS_NO_MEMORY);
377 RETURN( (HACCEL) NULL);
378 }
379
380 for (Index = 0; Index < EntriesCount; Index++)
381 {
382 Accel->Table[Index].fVirt = Entries[Index].fVirt&0x7f;
383 if(Accel->Table[Index].fVirt & FVIRTKEY)
384 {
385 Accel->Table[Index].key = Entries[Index].key;
386 }
387 else
388 {
389 RtlMultiByteToUnicodeN(&Accel->Table[Index].key,
390 sizeof(WCHAR),
391 NULL,
392 (PCSTR)&Entries[Index].key,
393 sizeof(CHAR));
394 }
395
396 Accel->Table[Index].cmd = Entries[Index].cmd;
397 }
398
399 /* Set the end-of-table terminator. */
400 Accel->Table[EntriesCount - 1].fVirt |= 0x80;
401 }
402
403 /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
404
405 RETURN(hAccel);
406
407 CLEANUP:
408 DPRINT("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d) = %x\n",
409 Entries, EntriesCount,_ret_);
410 UserLeave();
411 END_CLEANUP;
412 }
413
414
415
416 BOOLEAN
417 APIENTRY
418 NtUserDestroyAcceleratorTable(
419 HACCEL hAccel)
420 {
421 PACCELERATOR_TABLE Accel;
422 DECLARE_RETURN(BOOLEAN);
423
424 /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's
425 usage count (and return TRUE).
426 FIXME: Destroy only tables created using CreateAcceleratorTable.
427 */
428
429 DPRINT("NtUserDestroyAcceleratorTable(Table %x)\n", hAccel);
430 UserEnterExclusive();
431
432 if (!(Accel = UserGetAccelObject(hAccel)))
433 {
434 RETURN( FALSE);
435 }
436
437 if (Accel->Table != NULL)
438 {
439 ExFreePool(Accel->Table);
440 Accel->Table = NULL;
441 }
442
443 UserDeleteObject(hAccel, otAccel);
444
445 RETURN( TRUE);
446
447 CLEANUP:
448 DPRINT("Leave NtUserDestroyAcceleratorTable(Table %x) = %i\n", hAccel,_ret_);
449 UserLeave();
450 END_CLEANUP;
451 }
452
453
454 int
455 APIENTRY
456 NtUserTranslateAccelerator(
457 HWND hWnd,
458 HACCEL hAccel,
459 LPMSG Message)
460 {
461 PWINDOW_OBJECT Window = NULL;
462 PACCELERATOR_TABLE Accel = NULL;
463 ULONG i;
464 USER_REFERENCE_ENTRY AccelRef, WindowRef;
465 DECLARE_RETURN(int);
466
467 DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p)\n",
468 hWnd, hAccel, Message);
469 UserEnterShared();
470
471 if (Message == NULL)
472 {
473 SetLastNtError(STATUS_INVALID_PARAMETER);
474 RETURN( 0);
475 }
476
477 if ((Message->message != WM_KEYDOWN) &&
478 (Message->message != WM_SYSKEYDOWN) &&
479 (Message->message != WM_SYSCHAR) &&
480 (Message->message != WM_CHAR))
481 {
482 RETURN( 0);
483 }
484
485 if (!(Accel = UserGetAccelObject(hAccel)))
486 {
487 RETURN( 0);
488 }
489
490 UserRefObjectCo(Accel, &AccelRef);
491
492 if (!(Window = UserGetWindowObject(hWnd)))
493 {
494 RETURN( 0);
495 }
496
497 UserRefObjectCo(Window, &WindowRef);
498
499
500 /* FIXME: Associate AcceleratorTable with the current thread */
501
502 for (i = 0; i < Accel->Count; i++)
503 {
504 if (co_IntTranslateAccelerator(Window, Message->message, Message->wParam, Message->lParam,
505 Accel->Table[i].fVirt, Accel->Table[i].key,
506 Accel->Table[i].cmd))
507 {
508 DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
509 hWnd, hAccel, Message, 1);
510 RETURN( 1);
511 }
512 if (((Accel->Table[i].fVirt & 0x80) > 0))
513 {
514 break;
515 }
516 }
517
518 RETURN( 0);
519
520 CLEANUP:
521 if (Window) UserDerefObjectCo(Window);
522 if (Accel) UserDerefObjectCo(Accel);
523
524 DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
525 hWnd, hAccel, Message, 0);
526 UserLeave();
527 END_CLEANUP;
528 }