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