menu: code cleanup, naming changes, simplify refcounting, make internal func take...
[reactos.git] / reactos / subsys / 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 static
73 PACCELERATOR_TABLE FASTCALL UserGetAccelObjectNoRef(HACCEL hAccel)
74 {
75 PACCELERATOR_TABLE Accel=NULL;
76 NTSTATUS Status = ObmReferenceObjectByHandle(gHandleTable,
77 hAccel,
78 otAcceleratorTable,
79 (PVOID*)&Accel);
80
81 if (!NT_SUCCESS(Status))
82 {
83 SetLastWin32Error(ERROR_INVALID_ACCEL_HANDLE);
84 return NULL;
85 }
86
87 ObmDereferenceObject(Accel);
88 return Accel;
89 }
90
91
92
93
94 int
95 STDCALL
96 NtUserCopyAcceleratorTable(
97 HACCEL hAccel,
98 LPACCEL Entries,
99 int EntriesCount)
100 {
101 PWINSTATION_OBJECT WindowStation;
102 PACCELERATOR_TABLE Accel;
103 NTSTATUS Status;
104 int Ret;
105 DECLARE_RETURN(int);
106
107 DPRINT("Enter NtUserCopyAcceleratorTable\n");
108 UserEnterShared();
109
110 Status = IntValidateWindowStationHandle(UserGetProcessWindowStation(),
111 UserMode,
112 0,
113 &WindowStation);
114
115 if (!NT_SUCCESS(Status))
116 {
117 SetLastNtError(STATUS_ACCESS_DENIED);
118 RETURN(0);
119 }
120
121 if (!(Accel = UserGetAccelObjectNoRef(hAccel)))
122 {
123 ObDereferenceObject(WindowStation);
124 RETURN(0);
125 }
126
127 if(Entries)
128 {
129 Ret = min(EntriesCount, Accel->Count);
130 Status = MmCopyToCaller(Entries, Accel->Table, Ret * sizeof(ACCEL));
131 if (!NT_SUCCESS(Status))
132 {
133 ObDereferenceObject(WindowStation);
134 SetLastNtError(Status);
135 RETURN(0);
136 }
137 }
138 else
139 {
140 Ret = Accel->Count;
141 }
142
143 ObDereferenceObject(WindowStation);
144
145 RETURN(Ret);
146
147 CLEANUP:
148 DPRINT("Leave NtUserCopyAcceleratorTable, ret=%i\n",_ret_);
149 UserLeave();
150 END_CLEANUP;
151 }
152
153 HACCEL
154 STDCALL
155 NtUserCreateAcceleratorTable(
156 LPACCEL Entries,
157 SIZE_T EntriesCount)
158 {
159 PWINSTATION_OBJECT WindowStation;
160 PACCELERATOR_TABLE Accel;
161 NTSTATUS Status;
162 HACCEL hAccel;
163 DECLARE_RETURN(HACCEL);
164
165 DPRINT("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d)\n",
166 Entries, EntriesCount);
167 UserEnterExclusive();
168
169 Status = IntValidateWindowStationHandle(UserGetProcessWindowStation(),
170 UserMode,
171 0,
172 &WindowStation);
173
174 if (!NT_SUCCESS(Status))
175 {
176 SetLastNtError(STATUS_ACCESS_DENIED);
177 RETURN( FALSE );
178 }
179
180 Accel = ObmCreateObject(
181 gHandleTable,
182 (PHANDLE)&hAccel,
183 otAcceleratorTable,
184 sizeof(ACCELERATOR_TABLE));
185
186 if (Accel == NULL)
187 {
188 ObDereferenceObject(WindowStation);
189 SetLastNtError(STATUS_NO_MEMORY);
190 RETURN( (HACCEL) 0 );
191 }
192
193 Accel->Count = EntriesCount;
194 if (Accel->Count > 0)
195 {
196 Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), TAG_ACCEL);
197 if (Accel->Table == NULL)
198 {
199 ObmCloseHandle(gHandleTable, hAccel);
200 ObDereferenceObject(WindowStation);
201 SetLastNtError(Status);
202 RETURN( (HACCEL) 0);
203 }
204
205 Status = MmCopyFromCaller(Accel->Table, Entries, EntriesCount * sizeof(ACCEL));
206 if (!NT_SUCCESS(Status))
207 {
208 ExFreePool(Accel->Table);
209 ObmCloseHandle(gHandleTable, hAccel);
210 ObDereferenceObject(WindowStation);
211 SetLastNtError(Status);
212 RETURN((HACCEL) 0);
213 }
214 }
215
216 ObDereferenceObject(WindowStation);
217
218 /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
219
220 RETURN(hAccel);
221
222 CLEANUP:
223 DPRINT("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d) = %x\n",
224 Entries, EntriesCount,_ret_);
225 UserLeave();
226 END_CLEANUP;
227 }
228
229
230
231 BOOLEAN
232 STDCALL
233 NtUserDestroyAcceleratorTable(
234 HACCEL hAccel)
235 {
236 PWINSTATION_OBJECT WindowStation;
237 PACCELERATOR_TABLE Accel;
238 NTSTATUS Status;
239 DECLARE_RETURN(BOOLEAN);
240
241 /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's
242 usage count (and return TRUE).
243 FIXME: Destroy only tables created using CreateAcceleratorTable.
244 */
245
246 DPRINT("NtUserDestroyAcceleratorTable(Table %x)\n", hAccel);
247 UserEnterExclusive();
248
249 Status = IntValidateWindowStationHandle(UserGetProcessWindowStation(),
250 UserMode,
251 0,
252 &WindowStation);
253
254 if (!NT_SUCCESS(Status))
255 {
256 SetLastNtError(STATUS_ACCESS_DENIED);
257 DPRINT1("E1\n");
258 RETURN( FALSE);
259 }
260
261 if (!(Accel = UserGetAccelObjectNoRef(hAccel)))
262 {
263 ObDereferenceObject(WindowStation);
264 RETURN( FALSE);
265 }
266
267 ObmCloseHandle(gHandleTable, hAccel);
268
269 if (Accel->Table != NULL)
270 {
271 ExFreePool(Accel->Table);
272 }
273
274 ObDereferenceObject(WindowStation);
275
276 RETURN( TRUE);
277
278 CLEANUP:
279 DPRINT("Leave NtUserDestroyAcceleratorTable(Table %x) = %i\n", hAccel,_ret_);
280 UserLeave();
281 END_CLEANUP;
282 }
283
284 static
285 BOOLEAN FASTCALL
286 co_IntTranslateAccelerator(
287 PWINDOW_OBJECT Window,
288 UINT message,
289 WPARAM wParam,
290 LPARAM lParam,
291 BYTE fVirt,
292 WORD key,
293 WORD cmd)
294 {
295 UINT mesg = 0;
296
297 ASSERT_REFS(Window);
298
299 DPRINT("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x)\n",
300 Window->hSelf, message, wParam, lParam, fVirt, key, cmd);
301
302 if (wParam != key)
303 {
304 DPRINT("T0\n");
305 return FALSE;
306 }
307
308 if (message == WM_CHAR)
309 {
310 if (!(fVirt & FALT) && !(fVirt & FVIRTKEY))
311 {
312 DPRINT("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
313 goto found;
314 }
315 }
316 else
317 {
318 if ((fVirt & FVIRTKEY) > 0)
319 {
320 INT mask = 0;
321 DPRINT("found accel for virt_key %04x (scan %04x)\n",
322 wParam, 0xff & HIWORD(lParam));
323
324 DPRINT("NtUserGetKeyState(VK_SHIFT) = 0x%x\n",
325 UserGetKeyState(VK_SHIFT));
326 DPRINT("NtUserGetKeyState(VK_CONTROL) = 0x%x\n",
327 UserGetKeyState(VK_CONTROL));
328 DPRINT("NtUserGetKeyState(VK_MENU) = 0x%x\n",
329 UserGetKeyState(VK_MENU));
330
331 if (UserGetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
332 if (UserGetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
333 if (UserGetKeyState(VK_MENU) & 0x8000) mask |= FALT;
334 if (mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
335 DPRINT("but incorrect SHIFT/CTRL/ALT-state\n");
336 }
337 else
338 {
339 if (!(lParam & 0x01000000)) /* no special_key */
340 {
341 if ((fVirt & FALT) && (lParam & 0x20000000))
342 { /* ^^ ALT pressed */
343 DPRINT("found accel for Alt-%c\n", wParam & 0xff);
344 goto found;
345 }
346 }
347 }
348 }
349
350 DPRINT("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = FALSE\n",
351 Window->hSelf, message, wParam, lParam, fVirt, key, cmd);
352
353 return FALSE;
354
355 found:
356 if (message == WM_KEYUP || message == WM_SYSKEYUP)
357 mesg = 1;
358 else if (IntGetCaptureWindow())
359 mesg = 2;
360 else if (!IntIsWindowVisible(Window->hSelf)) /* FIXME: WINE IsWindowEnabled == IntIsWindowVisible? */
361 mesg = 3;
362 else
363 {
364 #if 0
365 HMENU hMenu, hSubMenu, hSysMenu;
366 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
367
368 hMenu = (UserGetWindowLongW(hWnd, GWL_STYLE) & WS_CHILD) ? 0 : GetMenu(hWnd);
369 hSysMenu = get_win_sys_menu(hWnd);
370
371 /* find menu item and ask application to initialize it */
372 /* 1. in the system menu */
373 hSubMenu = hSysMenu;
374 nPos = cmd;
375 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
376 {
377 co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
378 if(hSubMenu != hSysMenu)
379 {
380 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
381 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
382 co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
383 }
384 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
385 }
386 else /* 2. in the window's menu */
387 {
388 hSubMenu = hMenu;
389 nPos = cmd;
390 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
391 {
392 co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
393 if(hSubMenu != hMenu)
394 {
395 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
396 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
397 co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
398 }
399 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
400 }
401 }
402
403 if (uSysStat != (UINT)-1)
404 {
405 if (uSysStat & (MF_DISABLED|MF_GRAYED))
406 mesg=4;
407 else
408 mesg=WM_SYSCOMMAND;
409 }
410 else
411 {
412 if (uStat != (UINT)-1)
413 {
414 if (IsIconic(hWnd))
415 mesg=5;
416 else
417 {
418 if (uStat & (MF_DISABLED|MF_GRAYED))
419 mesg=6;
420 else
421 mesg=WM_COMMAND;
422 }
423 }
424 else
425 {
426 mesg=WM_COMMAND;
427 }
428 }
429 #else
430 DPRINT1("menu search not implemented");
431 mesg = WM_COMMAND;
432 #endif
433 }
434
435 if (mesg == WM_COMMAND)
436 {
437 DPRINT(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
438 co_IntSendMessage(Window->hSelf, mesg, 0x10000 | cmd, 0L);
439 }
440 else if (mesg == WM_SYSCOMMAND)
441 {
442 DPRINT(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
443 co_IntSendMessage(Window->hSelf, mesg, cmd, 0x00010000L);
444 }
445 else
446 {
447 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
448 * #0: unknown (please report!)
449 * #1: for WM_KEYUP,WM_SYSKEYUP
450 * #2: mouse is captured
451 * #3: window is disabled
452 * #4: it's a disabled system menu option
453 * #5: it's a menu option, but window is iconic
454 * #6: it's a menu option, but disabled
455 */
456 DPRINT(", but won't send WM_{SYS}COMMAND, reason is #%d\n", mesg);
457 if (mesg == 0)
458 {
459 DPRINT1(" unknown reason - please report!");
460 }
461 }
462
463 DPRINT("IntTranslateAccelerator(hWnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = TRUE\n",
464 Window->hSelf, message, wParam, lParam, fVirt, key, cmd);
465
466 return TRUE;
467 }
468
469 int
470 STDCALL
471 NtUserTranslateAccelerator(
472 HWND hWnd,
473 HACCEL hAccel,
474 LPMSG Message)
475 {
476 PWINSTATION_OBJECT WindowStation = NULL;
477 PWINDOW_OBJECT Window = NULL;
478 PACCELERATOR_TABLE Accel = NULL;
479 NTSTATUS Status;
480 ULONG i;
481 DECLARE_RETURN(int);
482
483 DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p)\n",
484 hWnd, hAccel, Message);
485 UserEnterShared();
486
487 if (Message == NULL)
488 {
489 SetLastNtError(STATUS_INVALID_PARAMETER);
490 RETURN( 0);
491 }
492
493 if ((Message->message != WM_KEYDOWN) &&
494 (Message->message != WM_SYSKEYDOWN) &&
495 (Message->message != WM_SYSCHAR) &&
496 (Message->message != WM_CHAR))
497 {
498 RETURN( 0);
499 }
500
501 Status = IntValidateWindowStationHandle(UserGetProcessWindowStation(),
502 UserMode,
503 0,
504 &WindowStation);
505
506 if (!NT_SUCCESS(Status))
507 {
508 SetLastNtError(STATUS_ACCESS_DENIED);
509 RETURN( 0);
510 }
511
512 if (!(Accel = UserGetAccelObjectNoRef(hAccel)))
513 {
514 RETURN( 0);
515 }
516
517 UserReferenceAccelObjectCo(Accel);
518
519 if (!(Window = UserGetWindowObject(hWnd)))
520 {
521 RETURN( 0);
522 }
523
524 UserReferenceWindowObjectCo(Window);
525
526
527 /* FIXME: Associate AcceleratorTable with the current thread */
528
529 for (i = 0; i < Accel->Count; i++)
530 {
531 if (co_IntTranslateAccelerator(Window, Message->message, Message->wParam, Message->lParam,
532 Accel->Table[i].fVirt, Accel->Table[i].key,
533 Accel->Table[i].cmd))
534 {
535 DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
536 hWnd, hAccel, Message, 1);
537 RETURN( 1);
538 }
539 if (((Accel->Table[i].fVirt & 0x80) > 0))
540 {
541 break;
542 }
543 }
544
545 RETURN( 0);
546
547 CLEANUP:
548
549 if (Window) UserReferenceWindowObjectCo(Window);
550 if (Accel) UserReferenceAccelObjectCo(Accel);
551
552 if (WindowStation) ObDereferenceObject(WindowStation);
553
554 DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
555 hWnd, hAccel, Message, 0);
556 UserLeave();
557 END_CLEANUP;
558 }