The real, definitive, Visual C++ support branch. Accept no substitutes
[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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
48 */
49
50 /* INCLUDES ******************************************************************/
51
52 #include <w32k.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(USER_BODY_TO_HEADER(Accel)->RefCount >= 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 STDCALL
290 NtUserCopyAcceleratorTable(
291 HACCEL hAccel,
292 LPACCEL Entries,
293 int EntriesCount)
294 {
295 PACCELERATOR_TABLE Accel;
296 NTSTATUS Status;
297 int Ret;
298 DECLARE_RETURN(int);
299
300 DPRINT("Enter NtUserCopyAcceleratorTable\n");
301 UserEnterShared();
302
303 if (!(Accel = UserGetAccelObject(hAccel)))
304 {
305 RETURN(0);
306 }
307
308 if(Entries)
309 {
310 Ret = min(EntriesCount, Accel->Count);
311 Status = MmCopyToCaller(Entries, Accel->Table, Ret * sizeof(ACCEL));
312 if (!NT_SUCCESS(Status))
313 {
314 SetLastNtError(Status);
315 RETURN(0);
316 }
317 }
318 else
319 {
320 Ret = Accel->Count;
321 }
322
323 RETURN(Ret);
324
325 CLEANUP:
326 DPRINT("Leave NtUserCopyAcceleratorTable, ret=%i\n",_ret_);
327 UserLeave();
328 END_CLEANUP;
329 }
330
331 HACCEL
332 STDCALL
333 NtUserCreateAcceleratorTable(
334 LPACCEL Entries,
335 SIZE_T EntriesCount)
336 {
337 PACCELERATOR_TABLE Accel;
338 NTSTATUS Status;
339 HACCEL hAccel;
340 DECLARE_RETURN(HACCEL);
341
342 DPRINT("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d)\n",
343 Entries, EntriesCount);
344 UserEnterExclusive();
345
346 if (!Entries || !EntriesCount)
347 {
348 RETURN( (HACCEL) 0 );
349 }
350
351 Accel = UserCreateObject(gHandleTable, (PHANDLE)&hAccel, otAccel, sizeof(ACCELERATOR_TABLE));
352
353 if (Accel == NULL)
354 {
355 SetLastNtError(STATUS_NO_MEMORY);
356 RETURN( (HACCEL) 0 );
357 }
358
359 Accel->Count = EntriesCount;
360 if (Accel->Count > 0)
361 {
362 Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), TAG_ACCEL);
363 if (Accel->Table == NULL)
364 {
365 UserDereferenceObject(Accel);
366 UserDeleteObject(hAccel, otAccel);
367 SetLastNtError(STATUS_NO_MEMORY);
368 RETURN( (HACCEL) 0);
369 }
370
371 Status = MmCopyFromCaller(Accel->Table, Entries, EntriesCount * sizeof(ACCEL));
372 if (!NT_SUCCESS(Status))
373 {
374 ExFreePool(Accel->Table);
375 UserDereferenceObject(Accel);
376 UserDeleteObject(hAccel, otAccel);
377 SetLastNtError(Status);
378 RETURN((HACCEL) 0);
379 }
380 }
381
382 /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
383
384 RETURN(hAccel);
385
386 CLEANUP:
387 DPRINT("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d) = %x\n",
388 Entries, EntriesCount,_ret_);
389 UserLeave();
390 END_CLEANUP;
391 }
392
393
394
395 BOOLEAN
396 STDCALL
397 NtUserDestroyAcceleratorTable(
398 HACCEL hAccel)
399 {
400 PACCELERATOR_TABLE Accel;
401 DECLARE_RETURN(BOOLEAN);
402
403 /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's
404 usage count (and return TRUE).
405 FIXME: Destroy only tables created using CreateAcceleratorTable.
406 */
407
408 DPRINT("NtUserDestroyAcceleratorTable(Table %x)\n", hAccel);
409 UserEnterExclusive();
410
411 if (!(Accel = UserGetAccelObject(hAccel)))
412 {
413 RETURN( FALSE);
414 }
415
416 if (Accel->Table != NULL)
417 {
418 ExFreePool(Accel->Table);
419 Accel->Table = NULL;
420 }
421
422 UserDeleteObject(hAccel, otAccel);
423
424 RETURN( TRUE);
425
426 CLEANUP:
427 DPRINT("Leave NtUserDestroyAcceleratorTable(Table %x) = %i\n", hAccel,_ret_);
428 UserLeave();
429 END_CLEANUP;
430 }
431
432
433 int
434 STDCALL
435 NtUserTranslateAccelerator(
436 HWND hWnd,
437 HACCEL hAccel,
438 LPMSG Message)
439 {
440 PWINDOW_OBJECT Window = NULL;
441 PACCELERATOR_TABLE Accel = NULL;
442 ULONG i;
443 USER_REFERENCE_ENTRY AccelRef, WindowRef;
444 DECLARE_RETURN(int);
445
446 DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p)\n",
447 hWnd, hAccel, Message);
448 UserEnterShared();
449
450 if (Message == NULL)
451 {
452 SetLastNtError(STATUS_INVALID_PARAMETER);
453 RETURN( 0);
454 }
455
456 if ((Message->message != WM_KEYDOWN) &&
457 (Message->message != WM_SYSKEYDOWN) &&
458 (Message->message != WM_SYSCHAR) &&
459 (Message->message != WM_CHAR))
460 {
461 RETURN( 0);
462 }
463
464 if (!(Accel = UserGetAccelObject(hAccel)))
465 {
466 RETURN( 0);
467 }
468
469 UserRefObjectCo(Accel, &AccelRef);
470
471 if (!(Window = UserGetWindowObject(hWnd)))
472 {
473 RETURN( 0);
474 }
475
476 UserRefObjectCo(Window, &WindowRef);
477
478
479 /* FIXME: Associate AcceleratorTable with the current thread */
480
481 for (i = 0; i < Accel->Count; i++)
482 {
483 if (co_IntTranslateAccelerator(Window, Message->message, Message->wParam, Message->lParam,
484 Accel->Table[i].fVirt, Accel->Table[i].key,
485 Accel->Table[i].cmd))
486 {
487 DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
488 hWnd, hAccel, Message, 1);
489 RETURN( 1);
490 }
491 if (((Accel->Table[i].fVirt & 0x80) > 0))
492 {
493 break;
494 }
495 }
496
497 RETURN( 0);
498
499 CLEANUP:
500 if (Window) UserDerefObjectCo(Window);
501 if (Accel) UserDerefObjectCo(Accel);
502
503 DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
504 hWnd, hAccel, Message, 0);
505 UserLeave();
506 END_CLEANUP;
507 }