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