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 #define NOTHING 11 12 typedef struct 13 { 14 PWCHAR FontName; 15 PWCHAR SubFontName; 16 } MUI_SUBFONT; 17 18 #include "../../../base/setup/lib/muifonts.h" 19 20 BOOL UpdateRegistryForFontSubstitutes(MUI_SUBFONT *pSubstitutes) 21 { 22 DWORD cbData; 23 HKEY hKey; 24 static const WCHAR pszKey[] = 25 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes"; 26 27 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, pszKey, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) 28 return FALSE; 29 30 /* Overwrite only */ 31 for (; pSubstitutes->FontName; ++pSubstitutes) 32 { 33 cbData = (lstrlenW(pSubstitutes->SubFontName) + 1) * sizeof(WCHAR); 34 RegSetValueExW(hKey, pSubstitutes->FontName, 0, 35 REG_SZ, (LPBYTE)pSubstitutes->SubFontName, cbData); 36 } 37 38 RegCloseKey(hKey); 39 return TRUE; 40 } 41 42 VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName) 43 { 44 WCHAR szSysDir[MAX_PATH]; 45 GetSystemDirectoryW(szSysDir, ARRAYSIZE(szSysDir)); 46 StringCchPrintfW(pszPath, cchPath, L"%s\\%s", szSysDir, pszFileName); 47 } 48 49 BOOL 50 InputList_SetFontSubstitutes(LCID dwLocaleId) 51 { 52 MUI_SUBFONT *pSubstitutes; 53 WORD wLangID, wPrimaryLangID, wSubLangID; 54 55 wLangID = LANGIDFROMLCID(dwLocaleId); 56 wPrimaryLangID = PRIMARYLANGID(wLangID); 57 wSubLangID = SUBLANGID(wLangID); 58 59 /* FIXME: Add more if necessary */ 60 switch (wPrimaryLangID) 61 { 62 default: 63 pSubstitutes = LatinFonts; 64 break; 65 case LANG_AZERI: 66 case LANG_BELARUSIAN: 67 case LANG_BULGARIAN: 68 case LANG_KAZAK: 69 case LANG_RUSSIAN: 70 case LANG_SERBIAN: 71 case LANG_TATAR: 72 case LANG_UKRAINIAN: 73 case LANG_UZBEK: 74 pSubstitutes = CyrillicFonts; 75 break; 76 case LANG_GREEK: 77 pSubstitutes = GreekFonts; 78 break; 79 case LANG_HEBREW: 80 pSubstitutes = HebrewFonts; 81 break; 82 case LANG_CHINESE: 83 switch (wSubLangID) 84 { 85 case SUBLANG_CHINESE_SIMPLIFIED: 86 case SUBLANG_CHINESE_SINGAPORE: 87 pSubstitutes = ChineseSimplifiedFonts; 88 break; 89 case SUBLANG_CHINESE_TRADITIONAL: 90 case SUBLANG_CHINESE_HONGKONG: 91 case SUBLANG_CHINESE_MACAU: 92 pSubstitutes = ChineseTraditionalFonts; 93 break; 94 default: 95 pSubstitutes = NULL; 96 DebugBreak(); 97 break; 98 } 99 break; 100 case LANG_JAPANESE: 101 pSubstitutes = JapaneseFonts; 102 break; 103 case LANG_KOREAN: 104 pSubstitutes = KoreanFonts; 105 break; 106 case LANG_ARABIC: 107 case LANG_ARMENIAN: 108 case LANG_BENGALI: 109 case LANG_FARSI: 110 case LANG_GEORGIAN: 111 case LANG_GUJARATI: 112 case LANG_HINDI: 113 case LANG_KONKANI: 114 case LANG_MARATHI: 115 case LANG_PUNJABI: 116 case LANG_SANSKRIT: 117 case LANG_TAMIL: 118 case LANG_TELUGU: 119 case LANG_THAI: 120 case LANG_URDU: 121 case LANG_VIETNAMESE: 122 pSubstitutes = UnicodeFonts; 123 break; 124 } 125 126 if (pSubstitutes) 127 { 128 UpdateRegistryForFontSubstitutes(pSubstitutes); 129 return TRUE; 130 } 131 return FALSE; 132 } 133 134 static INPUT_LIST_NODE *_InputList = NULL; 135 136 137 static INPUT_LIST_NODE* 138 InputList_AppendNode(VOID) 139 { 140 INPUT_LIST_NODE *pCurrent; 141 INPUT_LIST_NODE *pNew; 142 143 pNew = (INPUT_LIST_NODE*)malloc(sizeof(INPUT_LIST_NODE)); 144 if (pNew == NULL) 145 return NULL; 146 147 ZeroMemory(pNew, sizeof(INPUT_LIST_NODE)); 148 149 if (_InputList == NULL) /* Empty? */ 150 { 151 _InputList = pNew; 152 return pNew; 153 } 154 155 /* Find last node */ 156 for (pCurrent = _InputList; pCurrent->pNext; pCurrent = pCurrent->pNext) 157 { 158 NOTHING; 159 } 160 161 /* Add to the end */ 162 pCurrent->pNext = pNew; 163 pNew->pPrev = pCurrent; 164 165 return pNew; 166 } 167 168 169 static VOID 170 InputList_RemoveNode(INPUT_LIST_NODE *pNode) 171 { 172 INPUT_LIST_NODE *pCurrent = pNode; 173 174 if (_InputList == NULL) 175 return; 176 177 if (pCurrent != NULL) 178 { 179 INPUT_LIST_NODE *pNext = pCurrent->pNext; 180 INPUT_LIST_NODE *pPrev = pCurrent->pPrev; 181 182 free(pCurrent->pszIndicator); 183 free(pCurrent); 184 185 if (pNext != NULL) 186 pNext->pPrev = pPrev; 187 188 if (pPrev != NULL) 189 pPrev->pNext = pNext; 190 else 191 _InputList = pNext; 192 } 193 } 194 195 196 VOID 197 InputList_Destroy(VOID) 198 { 199 INPUT_LIST_NODE *pCurrent; 200 INPUT_LIST_NODE *pNext; 201 202 if (_InputList == NULL) 203 return; 204 205 for (pCurrent = _InputList; pCurrent; pCurrent = pNext) 206 { 207 pNext = pCurrent->pNext; 208 209 free(pCurrent->pszIndicator); 210 free(pCurrent); 211 } 212 213 _InputList = NULL; 214 } 215 216 217 static BOOL 218 InputList_PrepareUserRegistry(PHKEY phPreloadKey, PHKEY phSubstKey) 219 { 220 BOOL bResult = FALSE; 221 HKEY hKey; 222 223 *phPreloadKey = *phSubstKey = NULL; 224 225 if (RegOpenKeyExW(HKEY_CURRENT_USER, 226 L"Keyboard Layout", 227 0, 228 KEY_ALL_ACCESS, 229 &hKey) == ERROR_SUCCESS) 230 { 231 RegDeleteKeyW(hKey, L"Preload"); 232 RegDeleteKeyW(hKey, L"Substitutes"); 233 234 RegCloseKey(hKey); 235 } 236 237 if (RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout", &hKey) == ERROR_SUCCESS && 238 RegCreateKeyW(hKey, L"Preload", phPreloadKey) == ERROR_SUCCESS && 239 RegCreateKeyW(hKey, L"Substitutes", phSubstKey) == ERROR_SUCCESS) 240 { 241 bResult = TRUE; 242 } 243 244 if (hKey) 245 RegCloseKey(hKey); 246 247 return bResult; 248 } 249 250 static BOOL 251 InputList_FindPreloadKLID(HKEY hPreloadKey, DWORD dwKLID) 252 { 253 DWORD dwNumber, dwType, cbValue; 254 WCHAR szNumber[16], szValue[KL_NAMELENGTH], szKLID[KL_NAMELENGTH]; 255 256 StringCchPrintfW(szKLID, ARRAYSIZE(szKLID), L"%08x", dwKLID); 257 258 for (dwNumber = 1; dwNumber <= 1000; ++dwNumber) 259 { 260 StringCchPrintfW(szNumber, ARRAYSIZE(szNumber), L"%u", dwNumber); 261 262 cbValue = ARRAYSIZE(szValue) * sizeof(WCHAR); 263 if (RegQueryValueExW(hPreloadKey, szNumber, NULL, &dwType, 264 (LPBYTE)szValue, &cbValue) != ERROR_SUCCESS) 265 { 266 break; 267 } 268 269 if (dwType != REG_SZ) 270 continue; 271 272 szValue[ARRAYSIZE(szValue) - 1] = 0; 273 if (_wcsicmp(szKLID, szValue) == 0) 274 return TRUE; 275 } 276 277 return FALSE; 278 } 279 280 static BOOL 281 InputList_WriteSubst(HKEY hSubstKey, DWORD dwPhysicalKLID, DWORD dwLogicalKLID) 282 { 283 DWORD cbValue; 284 WCHAR szLogicalKLID[KL_NAMELENGTH], szPhysicalKLID[KL_NAMELENGTH]; 285 286 StringCchPrintfW(szLogicalKLID, ARRAYSIZE(szLogicalKLID), L"%08x", dwLogicalKLID); 287 StringCchPrintfW(szPhysicalKLID, ARRAYSIZE(szPhysicalKLID), L"%08x", dwPhysicalKLID); 288 289 cbValue = (wcslen(szPhysicalKLID) + 1) * sizeof(WCHAR); 290 return RegSetValueExW(hSubstKey, szLogicalKLID, 0, REG_SZ, (LPBYTE)szPhysicalKLID, 291 cbValue) == ERROR_SUCCESS; 292 } 293 294 static DWORD 295 InputList_DoSubst(HKEY hPreloadKey, HKEY hSubstKey, 296 DWORD dwPhysicalKLID, DWORD dwLogicalKLID) 297 { 298 DWORD iTrial; 299 BOOL bSubstNeeded = (dwPhysicalKLID != dwLogicalKLID) || (HIWORD(dwPhysicalKLID) != 0); 300 301 for (iTrial = 1; iTrial <= 1000; ++iTrial) 302 { 303 if (!InputList_FindPreloadKLID(hPreloadKey, dwLogicalKLID)) /* Not found? */ 304 { 305 if (bSubstNeeded) 306 { 307 /* Write now */ 308 InputList_WriteSubst(hSubstKey, dwPhysicalKLID, dwLogicalKLID); 309 } 310 return dwLogicalKLID; 311 } 312 313 bSubstNeeded = TRUE; 314 315 /* Calculate the next logical KLID */ 316 if (!IS_SUBST_KLID(dwLogicalKLID)) 317 { 318 dwLogicalKLID |= SUBST_MASK; 319 } 320 else 321 { 322 WORD wLow = LOWORD(dwLogicalKLID); 323 WORD wHigh = HIWORD(dwLogicalKLID); 324 dwLogicalKLID = MAKELONG(wLow, wHigh + 1); 325 } 326 } 327 328 return 0; 329 } 330 331 static VOID 332 InputList_AddInputMethodToUserRegistry( 333 HKEY hPreloadKey, 334 HKEY hSubstKey, 335 DWORD dwNumber, 336 INPUT_LIST_NODE *pNode) 337 { 338 WCHAR szNumber[32], szLogicalKLID[KL_NAMELENGTH]; 339 DWORD dwPhysicalKLID, dwLogicalKLID, cbValue; 340 HKL hKL = pNode->hkl; 341 342 if (IS_IME_HKL(hKL)) /* IME? */ 343 { 344 /* Do not substitute the IME KLID */ 345 dwLogicalKLID = dwPhysicalKLID = HandleToUlong(hKL); 346 } 347 else 348 { 349 /* Substitute the KLID if necessary */ 350 dwPhysicalKLID = pNode->pLayout->dwKLID; 351 dwLogicalKLID = pNode->pLocale->dwId; 352 dwLogicalKLID = InputList_DoSubst(hPreloadKey, hSubstKey, dwPhysicalKLID, dwLogicalKLID); 353 } 354 355 /* Write the Preload value (number |--> logical KLID) */ 356 StringCchPrintfW(szNumber, ARRAYSIZE(szNumber), L"%lu", dwNumber); 357 StringCchPrintfW(szLogicalKLID, ARRAYSIZE(szLogicalKLID), L"%08x", dwLogicalKLID); 358 cbValue = (wcslen(szLogicalKLID) + 1) * sizeof(WCHAR); 359 RegSetValueExW(hPreloadKey, 360 szNumber, 361 0, 362 REG_SZ, 363 (LPBYTE)szLogicalKLID, 364 cbValue); 365 366 if ((pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) || 367 (pNode->wFlags & INPUT_LIST_NODE_FLAG_EDITED)) 368 { 369 UINT uFlags = KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL; 370 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 371 uFlags |= KLF_REPLACELANG; 372 373 pNode->hkl = LoadKeyboardLayoutW(szLogicalKLID, uFlags); 374 } 375 } 376 377 378 /* 379 * Writes any changes in input methods to the registry 380 */ 381 BOOL 382 InputList_Process(VOID) 383 { 384 INPUT_LIST_NODE *pCurrent; 385 DWORD dwNumber; 386 BOOL bRet = FALSE; 387 HKEY hPreloadKey, hSubstKey; 388 389 if (!InputList_PrepareUserRegistry(&hPreloadKey, &hSubstKey)) 390 { 391 if (hPreloadKey) 392 RegCloseKey(hPreloadKey); 393 if (hSubstKey) 394 RegCloseKey(hSubstKey); 395 return FALSE; 396 } 397 398 /* Find change in the IME HKLs */ 399 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 400 { 401 if (!IS_IME_HKL(pCurrent->hkl)) 402 continue; 403 404 if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_ADDED) || 405 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED) || 406 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)) 407 { 408 bRet = TRUE; /* Reboot is needed */ 409 break; 410 } 411 } 412 413 /* Process DELETED and EDITED entries */ 414 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 415 { 416 if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) || 417 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED)) 418 { 419 420 /* Only unload the DELETED and EDITED entries */ 421 if (UnloadKeyboardLayout(pCurrent->hkl)) 422 { 423 /* But the EDITED entries are used later */ 424 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 425 { 426 InputList_RemoveNode(pCurrent); 427 } 428 } 429 } 430 } 431 432 /* Add the DEFAULT entry and set font substitutes */ 433 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 434 { 435 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 436 continue; 437 438 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 439 { 440 bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId); 441 InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, 1, pCurrent); 442 443 /* Activate the DEFAULT entry */ 444 ActivateKeyboardLayout(pCurrent->hkl, KLF_RESET); 445 break; 446 } 447 } 448 449 /* Add entries except DEFAULT to registry */ 450 dwNumber = 2; 451 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 452 { 453 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 454 continue; 455 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 456 continue; 457 458 InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, dwNumber, pCurrent); 459 460 ++dwNumber; 461 } 462 463 /* Remove ADDED and EDITED flags */ 464 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 465 { 466 pCurrent->wFlags &= ~(INPUT_LIST_NODE_FLAG_ADDED | INPUT_LIST_NODE_FLAG_EDITED); 467 } 468 469 /* Change the default keyboard language */ 470 if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG, 0, &pCurrent->hkl, 0)) 471 { 472 DWORD dwRecipients = BSM_ALLDESKTOPS | BSM_APPLICATIONS; 473 474 BroadcastSystemMessageW(BSF_POSTMESSAGE, 475 &dwRecipients, 476 WM_INPUTLANGCHANGEREQUEST, 477 INPUTLANGCHANGE_SYSCHARSET, 478 (LPARAM)pCurrent->hkl); 479 } 480 481 /* Retry to delete (in case of failure to delete the default keyboard) */ 482 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 483 { 484 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 485 { 486 UnloadKeyboardLayout(pCurrent->hkl); 487 InputList_RemoveNode(pCurrent); 488 } 489 } 490 491 RegCloseKey(hPreloadKey); 492 RegCloseKey(hSubstKey); 493 return bRet; 494 } 495 496 497 BOOL 498 InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout) 499 { 500 WCHAR szIndicator[MAX_STR_LEN]; 501 INPUT_LIST_NODE *pInput = NULL; 502 503 if (pLocale == NULL || pLayout == NULL) 504 { 505 return FALSE; 506 } 507 508 for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext) 509 { 510 if (pInput->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 511 continue; 512 513 if (pInput->pLocale == pLocale && pInput->pLayout == pLayout) 514 { 515 return FALSE; /* Already exists */ 516 } 517 } 518 519 pInput = InputList_AppendNode(); 520 pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED; 521 pInput->pLocale = pLocale; 522 pInput->pLayout = pLayout; 523 524 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId), 525 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE, 526 szIndicator, 527 ARRAYSIZE(szIndicator))) 528 { 529 size_t len = wcslen(szIndicator); 530 531 if (len > 0) 532 { 533 szIndicator[len - 1] = 0; 534 pInput->pszIndicator = _wcsdup(szIndicator); 535 } 536 } 537 538 return TRUE; 539 } 540 541 542 VOID 543 InputList_SetDefault(INPUT_LIST_NODE *pNode) 544 { 545 INPUT_LIST_NODE *pCurrent; 546 547 if (pNode == NULL) 548 return; 549 550 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 551 { 552 if (pCurrent == pNode) 553 { 554 pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; 555 } 556 else 557 { 558 pCurrent->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT; 559 } 560 } 561 } 562 563 INPUT_LIST_NODE * 564 InputList_FindNextDefault(INPUT_LIST_NODE *pNode) 565 { 566 INPUT_LIST_NODE *pCurrent; 567 568 for (pCurrent = pNode->pNext; pCurrent; pCurrent = pCurrent->pNext) 569 { 570 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 571 continue; 572 573 return pCurrent; 574 } 575 576 for (pCurrent = pNode->pPrev; pCurrent; pCurrent = pCurrent->pPrev) 577 { 578 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 579 continue; 580 581 return pCurrent; 582 } 583 584 return NULL; 585 } 586 587 /* 588 * It marks the input method for deletion, but does not delete it directly. 589 * To apply the changes using InputList_Process() 590 */ 591 BOOL 592 InputList_Remove(INPUT_LIST_NODE *pNode) 593 { 594 BOOL ret = FALSE; 595 BOOL bRemoveNode = FALSE; 596 597 if (pNode == NULL) 598 return FALSE; 599 600 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) 601 { 602 /* 603 * If the input method has been added to the list, but not yet written 604 * in the registry, then simply remove it from the list 605 */ 606 bRemoveNode = TRUE; 607 } 608 else 609 { 610 pNode->wFlags |= INPUT_LIST_NODE_FLAG_DELETED; 611 } 612 613 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 614 { 615 INPUT_LIST_NODE *pCurrent = InputList_FindNextDefault(pNode); 616 if (pCurrent) 617 pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; 618 619 pNode->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT; 620 ret = TRUE; /* default input is changed */ 621 } 622 623 if (bRemoveNode) 624 { 625 InputList_RemoveNode(pNode); 626 } 627 628 return ret; 629 } 630 631 BOOL 632 InputList_RemoveByLang(LANGID wLangId) 633 { 634 BOOL ret = FALSE; 635 INPUT_LIST_NODE *pCurrent; 636 637 Retry: 638 for (pCurrent = _InputList; pCurrent; pCurrent = pCurrent->pNext) 639 { 640 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 641 continue; 642 643 if (LOWORD(pCurrent->pLocale->dwId) == wLangId) 644 { 645 if (InputList_Remove(pCurrent)) 646 ret = TRUE; /* default input is changed */ 647 goto Retry; 648 } 649 } 650 651 return ret; 652 } 653 654 VOID 655 InputList_Create(VOID) 656 { 657 INT iLayoutCount, iIndex; 658 WCHAR szIndicator[MAX_STR_LEN]; 659 INPUT_LIST_NODE *pInput; 660 HKL *pLayoutList, hklDefault; 661 662 SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG, 0, &hklDefault, 0); 663 664 iLayoutCount = GetKeyboardLayoutList(0, NULL); 665 pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL)); 666 667 if (!pLayoutList || GetKeyboardLayoutList(iLayoutCount, pLayoutList) <= 0) 668 { 669 free(pLayoutList); 670 return; 671 } 672 673 for (iIndex = 0; iIndex < iLayoutCount; ++iIndex) 674 { 675 HKL hKL = pLayoutList[iIndex]; 676 LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(hKL); 677 LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(hKL); 678 if (!pLocale || !pLayout) 679 continue; 680 681 pInput = InputList_AppendNode(); 682 pInput->pLocale = pLocale; 683 pInput->pLayout = pLayout; 684 pInput->hkl = hKL; 685 686 if (pInput->hkl == hklDefault) /* Default HKL? */ 687 { 688 pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; 689 hklDefault = NULL; /* No more default item */ 690 } 691 692 /* Get abbrev language name */ 693 szIndicator[0] = 0; 694 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId), 695 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE, 696 szIndicator, 697 ARRAYSIZE(szIndicator))) 698 { 699 size_t len = wcslen(szIndicator); 700 if (len > 0) 701 { 702 szIndicator[len - 1] = 0; 703 pInput->pszIndicator = _wcsdup(szIndicator); 704 } 705 } 706 } 707 708 free(pLayoutList); 709 } 710 711 static INT InputList_Compare(INPUT_LIST_NODE *pNode1, INPUT_LIST_NODE *pNode2) 712 { 713 INT nCompare = _wcsicmp(pNode1->pszIndicator, pNode2->pszIndicator); 714 if (nCompare != 0) 715 return nCompare; 716 717 return _wcsicmp(pNode1->pLayout->pszName, pNode2->pLayout->pszName); 718 } 719 720 VOID InputList_Sort(VOID) 721 { 722 INPUT_LIST_NODE *pList = _InputList; 723 INPUT_LIST_NODE *pNext, *pPrev; 724 INPUT_LIST_NODE *pMinimum, *pNode; 725 726 _InputList = NULL; 727 728 while (pList) 729 { 730 /* Find the minimum node */ 731 pMinimum = NULL; 732 for (pNode = pList; pNode; pNode = pNext) 733 { 734 pNext = pNode->pNext; 735 736 if (pMinimum == NULL) 737 { 738 pMinimum = pNode; 739 } 740 else if (InputList_Compare(pNode, pMinimum) < 0) 741 { 742 pMinimum = pNode; 743 } 744 } 745 746 // Remove pMinimum from pList 747 pNext = pMinimum->pNext; 748 pPrev = pMinimum->pPrev; 749 if (pNext) 750 pNext->pPrev = pPrev; 751 if (pPrev) 752 pPrev->pNext = pNext; 753 else 754 pList = pNext; 755 756 // Append pMinimum to _InputList 757 if (!_InputList) 758 { 759 pMinimum->pPrev = pMinimum->pNext = NULL; 760 _InputList = pMinimum; 761 } 762 else 763 { 764 /* Find last node */ 765 for (pNode = _InputList; pNode->pNext; pNode = pNode->pNext) 766 { 767 NOTHING; 768 } 769 770 /* Add to the end */ 771 pNode->pNext = pMinimum; 772 pMinimum->pPrev = pNode; 773 pMinimum->pNext = NULL; 774 } 775 } 776 } 777 778 INT 779 InputList_GetAliveCount(VOID) 780 { 781 INPUT_LIST_NODE *pNode; 782 INT nCount = 0; 783 784 for (pNode = _InputList; pNode; pNode = pNode->pNext) 785 { 786 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 787 continue; 788 789 ++nCount; 790 } 791 792 return nCount; 793 } 794 795 INPUT_LIST_NODE* 796 InputList_GetFirst(VOID) 797 { 798 return _InputList; 799 } 800