[ACPPAGE] Make use of the PCH.
[reactos.git] / dll / cpl / input / input_list.c
1 /*
2 * PROJECT: input.dll
3 * FILE: dll/cpl/input/input_list.c
4 * PURPOSE: input.dll
5 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org)
6 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
7 */
8
9 #include "input_list.h"
10
11 typedef struct
12 {
13 PWCHAR FontName;
14 PWCHAR SubFontName;
15 } MUI_SUBFONT;
16
17 #include "../../../base/setup/usetup/muifonts.h"
18
19 BOOL UpdateRegistryForFontSubstitutes(MUI_SUBFONT *pSubstitutes)
20 {
21 DWORD cbData;
22 HKEY hKey;
23 static const WCHAR pszKey[] =
24 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
25
26 hKey = NULL;
27 RegOpenKeyExW(HKEY_LOCAL_MACHINE, pszKey, 0, KEY_ALL_ACCESS, &hKey);
28 if (hKey == NULL)
29 return FALSE;
30
31 /* Overwrite only */
32 for (; pSubstitutes->FontName; ++pSubstitutes)
33 {
34 cbData = (lstrlenW(pSubstitutes->SubFontName) + 1) * sizeof(WCHAR);
35 RegSetValueExW(hKey, pSubstitutes->FontName, 0,
36 REG_SZ, (LPBYTE)pSubstitutes->SubFontName, cbData);
37 }
38
39 RegCloseKey(hKey);
40
41 return TRUE;
42 }
43
44 BOOL
45 InputList_SetFontSubstitutes(LCID dwLocaleId)
46 {
47 MUI_SUBFONT *pSubstitutes;
48 WORD wLangID, wPrimaryLangID, wSubLangID;
49
50 wLangID = LANGIDFROMLCID(dwLocaleId);
51 wPrimaryLangID = PRIMARYLANGID(wLangID);
52 wSubLangID = SUBLANGID(wLangID);
53
54 /* FIXME: Add more if necessary */
55 switch (wPrimaryLangID)
56 {
57 default:
58 pSubstitutes = LatinFonts;
59 break;
60 case LANG_AZERI:
61 case LANG_BELARUSIAN:
62 case LANG_BULGARIAN:
63 case LANG_KAZAK:
64 case LANG_RUSSIAN:
65 case LANG_SERBIAN:
66 case LANG_TATAR:
67 case LANG_UKRAINIAN:
68 case LANG_UZBEK:
69 pSubstitutes = CyrillicFonts;
70 break;
71 case LANG_GREEK:
72 pSubstitutes = GreekFonts;
73 break;
74 case LANG_HEBREW:
75 pSubstitutes = HebrewFonts;
76 break;
77 case LANG_CHINESE:
78 switch (wSubLangID)
79 {
80 case SUBLANG_CHINESE_SIMPLIFIED:
81 case SUBLANG_CHINESE_SINGAPORE:
82 case SUBLANG_CHINESE_MACAU:
83 pSubstitutes = ChineseSimplifiedFonts;
84 break;
85 case SUBLANG_CHINESE_TRADITIONAL:
86 case SUBLANG_CHINESE_HONGKONG:
87 pSubstitutes = ChineseTraditionalFonts;
88 break;
89 default:
90 pSubstitutes = NULL;
91 DebugBreak();
92 break;
93 }
94 break;
95 case LANG_JAPANESE:
96 pSubstitutes = JapaneseFonts;
97 break;
98 case LANG_KOREAN:
99 pSubstitutes = KoreanFonts;
100 break;
101 case LANG_ARABIC:
102 case LANG_ARMENIAN:
103 case LANG_BENGALI:
104 case LANG_FARSI:
105 case LANG_GEORGIAN:
106 case LANG_GUJARATI:
107 case LANG_HINDI:
108 case LANG_KONKANI:
109 case LANG_MARATHI:
110 case LANG_PUNJABI:
111 case LANG_SANSKRIT:
112 case LANG_TAMIL:
113 case LANG_TELUGU:
114 case LANG_THAI:
115 case LANG_URDU:
116 case LANG_VIETNAMESE:
117 pSubstitutes = UnicodeFonts;
118 break;
119 }
120
121 if (pSubstitutes)
122 {
123 UpdateRegistryForFontSubstitutes(pSubstitutes);
124 return TRUE;
125 }
126 return FALSE;
127 }
128
129 static INPUT_LIST_NODE *_InputList = NULL;
130
131
132 static INPUT_LIST_NODE*
133 InputList_AppendNode(VOID)
134 {
135 INPUT_LIST_NODE *pCurrent;
136 INPUT_LIST_NODE *pNew;
137
138 pCurrent = _InputList;
139
140 pNew = (INPUT_LIST_NODE*)malloc(sizeof(INPUT_LIST_NODE));
141 if (pNew == NULL)
142 return NULL;
143
144 ZeroMemory(pNew, sizeof(INPUT_LIST_NODE));
145
146 if (pCurrent == NULL)
147 {
148 _InputList = pNew;
149 }
150 else
151 {
152 while (pCurrent->pNext != NULL)
153 {
154 pCurrent = pCurrent->pNext;
155 }
156
157 pNew->pPrev = pCurrent;
158 pCurrent->pNext = pNew;
159 }
160
161 return pNew;
162 }
163
164
165 static VOID
166 InputList_RemoveNode(INPUT_LIST_NODE *pNode)
167 {
168 INPUT_LIST_NODE *pCurrent = pNode;
169
170 if (_InputList == NULL)
171 return;
172
173 if (pCurrent != NULL)
174 {
175 INPUT_LIST_NODE *pNext = pCurrent->pNext;
176 INPUT_LIST_NODE *pPrev = pCurrent->pPrev;
177
178 free(pCurrent->pszIndicator);
179 free(pCurrent);
180
181 if (pNext != NULL)
182 pNext->pPrev = pPrev;
183
184 if (pPrev != NULL)
185 pPrev->pNext = pNext;
186 else
187 _InputList = pNext;
188 }
189 }
190
191
192 VOID
193 InputList_Destroy(VOID)
194 {
195 INPUT_LIST_NODE *pCurrent;
196
197 if (_InputList == NULL)
198 return;
199
200 pCurrent = _InputList;
201
202 while (pCurrent != NULL)
203 {
204 INPUT_LIST_NODE *pNext = pCurrent->pNext;
205
206 free(pCurrent->pszIndicator);
207 free(pCurrent);
208
209 pCurrent = pNext;
210 }
211
212 _InputList = NULL;
213 }
214
215
216 static BOOL
217 InputList_PrepareUserRegistry(VOID)
218 {
219 BOOL bResult = FALSE;
220 HKEY hTempKey = NULL;
221 HKEY hKey = NULL;
222
223 if (RegOpenKeyExW(HKEY_CURRENT_USER,
224 L"Keyboard Layout",
225 0,
226 KEY_ALL_ACCESS,
227 &hKey) == ERROR_SUCCESS)
228 {
229 RegDeleteKeyW(hKey, L"Preload");
230 RegDeleteKeyW(hKey, L"Substitutes");
231
232 RegCloseKey(hKey);
233 }
234
235 if (RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout", &hKey) != ERROR_SUCCESS)
236 {
237 goto Cleanup;
238 }
239
240 if (RegCreateKeyW(hKey, L"Preload", &hTempKey) != ERROR_SUCCESS)
241 {
242 goto Cleanup;
243 }
244
245 RegCloseKey(hTempKey);
246
247 if (RegCreateKeyW(hKey, L"Substitutes", &hTempKey) != ERROR_SUCCESS)
248 {
249 goto Cleanup;
250 }
251
252 RegCloseKey(hTempKey);
253
254 bResult = TRUE;
255
256 Cleanup:
257 if (hTempKey != NULL)
258 RegCloseKey(hTempKey);
259 if (hKey != NULL)
260 RegCloseKey(hKey);
261
262 return bResult;
263 }
264
265
266 static VOID
267 InputList_AddInputMethodToUserRegistry(DWORD dwIndex, INPUT_LIST_NODE *pNode)
268 {
269 WCHAR szMethodIndex[MAX_PATH];
270 WCHAR szPreload[MAX_PATH];
271 BOOL bIsImeMethod = FALSE;
272 HKEY hKey;
273
274 StringCchPrintfW(szMethodIndex, ARRAYSIZE(szMethodIndex), L"%lu", dwIndex);
275
276 /* Check is IME method */
277 if ((HIWORD(pNode->pLayout->dwId) & 0xF000) == 0xE000)
278 {
279 StringCchPrintfW(szPreload, ARRAYSIZE(szPreload), L"%08X", pNode->pLayout->dwId);
280 bIsImeMethod = TRUE;
281 }
282 else
283 {
284 StringCchPrintfW(szPreload, ARRAYSIZE(szPreload), L"%08X", pNode->pLocale->dwId);
285 }
286
287 if (RegOpenKeyExW(HKEY_CURRENT_USER,
288 L"Keyboard Layout\\Preload",
289 0,
290 KEY_SET_VALUE,
291 &hKey) == ERROR_SUCCESS)
292 {
293 RegSetValueExW(hKey,
294 szMethodIndex,
295 0,
296 REG_SZ,
297 (LPBYTE)szPreload,
298 (wcslen(szPreload) + 1) * sizeof(WCHAR));
299
300 RegCloseKey(hKey);
301 }
302
303 if (pNode->pLocale->dwId != pNode->pLayout->dwId && bIsImeMethod == FALSE)
304 {
305 if (RegOpenKeyExW(HKEY_CURRENT_USER,
306 L"Keyboard Layout\\Substitutes",
307 0,
308 KEY_SET_VALUE,
309 &hKey) == ERROR_SUCCESS)
310 {
311 WCHAR szSubstitutes[MAX_PATH];
312
313 StringCchPrintfW(szSubstitutes, ARRAYSIZE(szSubstitutes), L"%08X", pNode->pLayout->dwId);
314
315 RegSetValueExW(hKey,
316 szPreload,
317 0,
318 REG_SZ,
319 (LPBYTE)szSubstitutes,
320 (wcslen(szSubstitutes) + 1) * sizeof(WCHAR));
321
322 RegCloseKey(hKey);
323 }
324 }
325
326 if ((pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) ||
327 (pNode->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
328 {
329 pNode->hkl = LoadKeyboardLayoutW(szPreload, KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL);
330 }
331 }
332
333
334 /*
335 * Writes any changes in input methods to the registry
336 */
337 BOOL
338 InputList_Process(VOID)
339 {
340 INPUT_LIST_NODE *pCurrent;
341 DWORD dwIndex;
342 BOOL bRet = FALSE;
343
344 /* Process deleted and edited input methods */
345 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
346 {
347 if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) ||
348 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
349 {
350 if (UnloadKeyboardLayout(pCurrent->hkl))
351 {
352 /* Only unload the edited input method, but does not delete it from the list */
353 if (!(pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
354 {
355 InputList_RemoveNode(pCurrent);
356 }
357 }
358 }
359 }
360
361 InputList_PrepareUserRegistry();
362
363 /* Find default input method */
364 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
365 {
366 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
367 {
368 bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId);
369 InputList_AddInputMethodToUserRegistry(1, pCurrent);
370 break;
371 }
372 }
373
374 if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG,
375 0,
376 (LPVOID)((LPDWORD)&pCurrent->hkl),
377 0))
378 {
379 DWORD dwRecipients;
380
381 dwRecipients = BSM_ALLCOMPONENTS;
382
383 BroadcastSystemMessageW(BSF_POSTMESSAGE,
384 &dwRecipients,
385 WM_INPUTLANGCHANGEREQUEST,
386 0,
387 (LPARAM)pCurrent->hkl);
388 }
389
390 /* Add methods to registry */
391 dwIndex = 2;
392
393 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
394 {
395 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
396 continue;
397
398 InputList_AddInputMethodToUserRegistry(dwIndex, pCurrent);
399
400 dwIndex++;
401 }
402
403 return bRet;
404 }
405
406
407 BOOL
408 InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout)
409 {
410 WCHAR szIndicator[MAX_STR_LEN];
411 INPUT_LIST_NODE *pInput;
412
413 if (pLocale == NULL || pLayout == NULL)
414 {
415 return FALSE;
416 }
417
418 for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext)
419 {
420 if (pInput->pLocale == pLocale && pInput->pLayout == pLayout)
421 {
422 return FALSE;
423 }
424 }
425
426 pInput = InputList_AppendNode();
427
428 pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED;
429
430 pInput->pLocale = pLocale;
431 pInput->pLayout = pLayout;
432
433 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
434 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
435 szIndicator,
436 ARRAYSIZE(szIndicator)))
437 {
438 size_t len = wcslen(szIndicator);
439
440 if (len > 0)
441 {
442 szIndicator[len - 1] = 0;
443 pInput->pszIndicator = _wcsdup(szIndicator);
444 }
445 }
446
447 return TRUE;
448 }
449
450
451 VOID
452 InputList_SetDefault(INPUT_LIST_NODE *pNode)
453 {
454 INPUT_LIST_NODE *pCurrent;
455
456 if (pNode == NULL)
457 return;
458
459 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
460 {
461 if (pCurrent == pNode)
462 {
463 pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
464 }
465 else
466 {
467 pCurrent->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT;
468 }
469 }
470 }
471
472
473 /*
474 * It marks the input method for deletion, but does not delete it directly.
475 * To apply the changes using InputList_Process()
476 */
477 VOID
478 InputList_Remove(INPUT_LIST_NODE *pNode)
479 {
480 BOOL bRemoveNode = FALSE;
481
482 if (pNode == NULL)
483 return;
484
485 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED)
486 {
487 /*
488 * If the input method has been added to the list, but not yet written
489 * in the registry, then simply remove it from the list
490 */
491 bRemoveNode = TRUE;
492 }
493 else
494 {
495 pNode->wFlags = INPUT_LIST_NODE_FLAG_DELETED;
496 }
497
498 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
499 {
500 if (pNode->pNext != NULL)
501 {
502 pNode->pNext->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
503 }
504 else if (pNode->pPrev != NULL)
505 {
506 pNode->pPrev->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
507 }
508 }
509
510 if (bRemoveNode != FALSE)
511 {
512 InputList_RemoveNode(pNode);
513 }
514 }
515
516
517 VOID
518 InputList_Create(VOID)
519 {
520 INT iLayoutCount;
521 HKL *pLayoutList;
522
523 iLayoutCount = GetKeyboardLayoutList(0, NULL);
524 pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL));
525
526 if (pLayoutList != NULL)
527 {
528 if (GetKeyboardLayoutList(iLayoutCount, pLayoutList) > 0)
529 {
530 INT iIndex;
531
532 for (iIndex = 0; iIndex < iLayoutCount; iIndex++)
533 {
534 LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(pLayoutList[iIndex]);
535 LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(pLayoutList[iIndex]);
536
537 if (pLocale != NULL && pLayout != NULL)
538 {
539 WCHAR szIndicator[MAX_STR_LEN] = { 0 };
540 INPUT_LIST_NODE *pInput;
541 HKL hklDefault;
542
543 pInput = InputList_AppendNode();
544
545 pInput->pLocale = pLocale;
546 pInput->pLayout = pLayout;
547 pInput->hkl = pLayoutList[iIndex];
548
549 if (SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG,
550 0,
551 (LPVOID)((LPDWORD)&hklDefault),
552 0) == FALSE)
553 {
554 hklDefault = GetKeyboardLayout(0);
555 }
556
557 if (pInput->hkl == hklDefault)
558 {
559 pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
560 }
561
562 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
563 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
564 szIndicator,
565 ARRAYSIZE(szIndicator)))
566 {
567 size_t len = wcslen(szIndicator);
568
569 if (len > 0)
570 {
571 szIndicator[len - 1] = 0;
572 pInput->pszIndicator = _wcsdup(szIndicator);
573 }
574 }
575 }
576 }
577 }
578
579 free(pLayoutList);
580 }
581 }
582
583
584 INPUT_LIST_NODE*
585 InputList_GetFirst(VOID)
586 {
587 return _InputList;
588 }