update for HEAD-2003091401
[reactos.git] / lib / user32 / windows / accel.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001 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  * PROJECT:         ReactOS user32.dll
22  * FILE:            lib/user32/windows/input.c
23  * PURPOSE:         Accelerator tables
24  * PROGRAMMER:      KJK::Hyperion <noog@libero.it>
25  * UPDATE HISTORY:
26  *      09/05/2001  CSH  Created
27  *      08/07/2003  KJK  Fully implemented
28  */
29
30 /* INCLUDES ******************************************************************/
31 #include <windows.h>
32 #include <user32/accel.h>
33 #include <win32k/ntuser.h>
34
35 /* FUNCTIONS *****************************************************************/
36
37 /* Lock guarding the cache */
38 CRITICAL_SECTION U32AccelCacheLock;
39
40 /* Cache */
41 U32_ACCEL_CACHE_ENTRY * U32AccelCache = NULL;
42
43 /* Look up a handle or resource address in the cache */
44 U32_ACCEL_CACHE_ENTRY ** WINAPI U32AccelCacheFind(HANDLE Object, HGLOBAL Data)
45 {
46  /*
47   to avoid using a double-link list and still allow elements to be removed,
48   return a pointer to the list link that points to the desired entry
49  */
50  U32_ACCEL_CACHE_ENTRY ** ppEntry = &U32AccelCache;
51  
52  for(; *ppEntry; ppEntry = &((*ppEntry)->Next))
53   if((*ppEntry)->Object == Object || (*ppEntry)->Data == Data) break;
54
55  return ppEntry;
56 }
57
58 /* Allocate an entry and insert it into the cache */
59 void WINAPI U32AccelCacheAdd(HACCEL Object, HGLOBAL Data)
60 {
61  U32_ACCEL_CACHE_ENTRY * pEntry =
62   LocalAlloc(LMEM_FIXED, sizeof(U32_ACCEL_CACHE_ENTRY));
63
64  /* failed to allocate an entry - not critical */
65  if(pEntry == NULL) return;
66
67  /* initialize the entry */
68  pEntry->Usage = 1;
69  pEntry->Object = Object;
70  pEntry->Data = Data;
71
72  /* insert the entry into the cache */
73  pEntry->Next = U32AccelCache;
74  U32AccelCache = pEntry;
75 }
76
77 /* Create an accelerator table from a loaded resource */
78 HACCEL WINAPI U32LoadAccelerators(HINSTANCE hInstance, HRSRC hTableRes)
79 {
80  HGLOBAL hAccTableData;
81  HACCEL hAccTable = NULL;
82  U32_ACCEL_CACHE_ENTRY * pEntry;
83  RES_ACCEL * pAccTableResData;
84  RES_ACCEL * p;
85  SIZE_T i = 0;
86  SIZE_T j = 0;
87  ACCEL * pAccTableData;
88
89  /* load the accelerator table */
90  hAccTableData = LoadResource(hInstance, hTableRes);
91
92  /* failure */
93  if(hAccTableData == NULL) return NULL;
94
95  RtlEnterCriticalSection(&U32AccelCacheLock);
96
97  /* see if this accelerator table has already been loaded */
98  pEntry = *U32AccelCacheFind(NULL, hAccTableData);
99
100  /* accelerator table already loaded */
101  if(pEntry)
102  {
103   /* increment the reference count */
104   ++ pEntry->Usage;
105
106   /* return the existing object */
107   hAccTable = pEntry->Object;
108
109   /* success */
110   goto l_Leave;
111  }
112
113  /* count the number of entries in the table */
114  p = pAccTableResData = (RES_ACCEL *)hAccTableData;
115
116  while(1)
117  {
118   /* FIXME??? unknown flag 0x60 stops the scan */
119   if(p->fVirt & 0x60) break;
120
121   ++ i;
122   ++ p;
123
124   /* flag 0x80 marks the last entry of the table */
125   if(p->fVirt & 0x80) break;
126  }
127
128  /* allocate the buffer for the table to be passed to Win32K */
129  pAccTableData = LocalAlloc(LMEM_FIXED, i * sizeof(ACCEL));
130
131  /* failure */
132  if(pAccTableData == NULL) goto l_Leave;
133
134  /* copy the table */
135  for(j = 0; j < i; ++ j)
136  {
137   pAccTableData[j].fVirt = pAccTableResData[j].fVirt;
138   pAccTableData[j].key = pAccTableResData[j].key;
139   pAccTableData[j].cmd = pAccTableResData[j].cmd;
140  }
141
142  /* create a new accelerator table object */
143  hAccTable = NtUserCreateAcceleratorTable(pAccTableData, i);
144
145  /* free the buffer */
146  LocalFree(pAccTableData);
147  
148  /* failure */
149  if(hAccTable == NULL) goto l_Leave;
150
151  /* success - cache the object */
152  U32AccelCacheAdd(hAccTable, pAccTableResData);
153
154 l_Leave:
155  RtlLeaveCriticalSection(&U32AccelCacheLock);
156  return hAccTable;
157 }
158
159 /* Checks if a message can be translated through an accelerator table */
160 BOOL WINAPI U32IsValidAccelMessage(UINT uMsg)
161 {
162  switch(uMsg)
163  {
164   case WM_KEYDOWN:
165   case WM_CHAR:
166   case WM_SYSKEYDOWN:
167   case WM_SYSCHAR:
168    return TRUE;
169
170   default:
171    return FALSE;
172  }
173 }
174
175 /* WIN32 FUNCTIONS ***********************************************************/
176
177 /*
178  * Dereference the specified accelerator table, removing it from the cache and
179  * deleting the associated NtUser object as appropriate
180  *
181  * @implemented
182  */
183 BOOL WINAPI DestroyAcceleratorTable(HACCEL hAccel)
184 {
185  U32_ACCEL_CACHE_ENTRY ** ppEntry;
186  ULONG_PTR nUsage = 0;
187
188  RtlEnterCriticalSection(&U32AccelCacheLock);
189
190  /* see if this accelerator table has been cached */
191  ppEntry = U32AccelCacheFind(hAccel, NULL);
192
193  /* accelerator table cached */
194  if(*ppEntry)
195  {
196   U32_ACCEL_CACHE_ENTRY * pEntry = *ppEntry;
197
198   /* decrement the reference count */
199   nUsage = pEntry->Usage = pEntry->Usage - 1;
200
201   /* reference count now zero: destroy the cache entry */
202   if(nUsage == 0)
203   {
204    /* unlink the cache entry */
205    *ppEntry = pEntry->Next;
206
207    /* free the cache entry */
208    LocalFree(pEntry);
209   }
210  }
211
212  RtlLeaveCriticalSection(&U32AccelCacheLock);
213
214  if(nUsage > 0) return FALSE;
215
216  /* destroy the object */
217  return NtUserDestroyAcceleratorTable(hAccel);
218 }
219
220
221 /*
222  * Create an accelerator table from a named resource
223  *
224  * @implemented
225  */
226 HACCEL WINAPI LoadAcceleratorsW(HINSTANCE hInstance, LPCWSTR lpTableName)
227 {
228  return U32LoadAccelerators
229  (
230   hInstance, 
231   FindResourceExW(hInstance, MAKEINTRESOURCEW(RT_ACCELERATOR), lpTableName, 0)
232  );
233 }
234
235
236 /*
237  * @implemented
238  */
239 HACCEL WINAPI LoadAcceleratorsA(HINSTANCE hInstance, LPCSTR lpTableName)
240 {
241   HRSRC Accel;
242
243   Accel = FindResourceExA(hInstance, MAKEINTRESOURCEA(RT_ACCELERATOR), lpTableName, 0);
244   if (NULL == Accel)
245     {
246       return NULL;
247     }
248
249   return U32LoadAccelerators(hInstance, Accel);
250 }
251
252 /*
253  * Translate a key press into a WM_COMMAND message
254  *
255  * @implemented
256  */
257 int WINAPI TranslateAcceleratorW(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg)
258 {
259  if(!U32IsValidAccelMessage(lpMsg->message)) return 0;
260
261  return NtUserTranslateAccelerator(hWnd, hAccTable, lpMsg);
262 }
263
264 /*
265  * @implemented
266  */
267 int WINAPI CopyAcceleratorTableW
268 (
269  HACCEL hAccelSrc,
270  LPACCEL lpAccelDst,
271  int cAccelEntries
272 )
273 {
274  return NtUserCopyAcceleratorTable(hAccelSrc, lpAccelDst, cAccelEntries);
275 }
276
277 /*
278  * @implemented
279  */
280 HACCEL WINAPI CreateAcceleratorTableW(LPACCEL lpaccl, int cEntries)
281 {
282  return NtUserCreateAcceleratorTable(lpaccl, cEntries);
283 }
284
285
286 /*
287  * @implemented
288  */
289 int WINAPI CopyAcceleratorTableA
290 (
291  HACCEL hAccelSrc,
292  LPACCEL lpAccelDst,
293  int cAccelEntries
294 )
295 {
296  int i;
297
298  cAccelEntries = CopyAcceleratorTableW(hAccelSrc, lpAccelDst, cAccelEntries);
299  
300  if(cAccelEntries == 0) return 0;
301
302  for(i = 0; i < cAccelEntries; ++ i)
303   if(!(lpAccelDst[i].fVirt & FVIRTKEY))
304   {
305    NTSTATUS nErrCode = RtlUnicodeToMultiByteN
306    (
307     (PCHAR)&lpAccelDst[i].key,
308     sizeof(lpAccelDst[i].key),
309     NULL,
310     (PWCHAR)&lpAccelDst[i].key,
311     sizeof(lpAccelDst[i].key)
312    );
313
314    if(!NT_SUCCESS(nErrCode)) lpAccelDst[i].key = 0;
315   }
316
317  return cAccelEntries;
318 }
319
320
321 /*
322  * @implemented
323  */
324 HACCEL WINAPI CreateAcceleratorTableA(LPACCEL lpaccl, int cEntries)
325 {
326  int i;
327
328  for(i = 0; i < cEntries; ++ i)
329   if(!lpaccl[i].fVirt)
330   {
331    NTSTATUS nErrCode = RtlMultiByteToUnicodeN
332    (
333     (PWCHAR)&lpaccl[i].key,
334     sizeof(lpaccl[i].key),
335     NULL,
336     (PCHAR)&lpaccl[i].key,
337     sizeof(lpaccl[i].key)
338    );
339
340    if(!NT_SUCCESS(nErrCode)) lpaccl[i].key = -1;
341   }
342
343  return CreateAcceleratorTableW(lpaccl, cEntries);
344 }
345
346
347 /*
348  * @implemented
349  */
350 int WINAPI TranslateAcceleratorA(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg)
351 {
352  MSG mCopy = *lpMsg;
353  CHAR cChar;
354  WCHAR wChar;
355  NTSTATUS nErrCode;
356
357  if(!U32IsValidAccelMessage(lpMsg->message)) return 0;
358
359  nErrCode =
360   RtlMultiByteToUnicodeN(&wChar, sizeof(wChar), NULL, &cChar, sizeof(cChar));
361
362  if(!nErrCode)
363  {
364   SetLastError(RtlNtStatusToDosError(nErrCode));
365   return 0;
366  }
367
368  return TranslateAcceleratorW(hWnd, hAccTable, &mCopy);
369 }
370
371 /* EOF */