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/lib/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 } 589