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 HKL hDefaultKL = NULL; 389 390 if (!InputList_PrepareUserRegistry(&hPreloadKey, &hSubstKey)) 391 { 392 if (hPreloadKey) 393 RegCloseKey(hPreloadKey); 394 if (hSubstKey) 395 RegCloseKey(hSubstKey); 396 return FALSE; 397 } 398 399 /* Find change in the IME HKLs */ 400 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 401 { 402 if (!IS_IME_HKL(pCurrent->hkl)) 403 continue; 404 405 if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_ADDED) || 406 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED) || 407 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)) 408 { 409 bRet = TRUE; /* Reboot is needed */ 410 break; 411 } 412 } 413 414 /* Process DELETED and EDITED entries */ 415 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 416 { 417 if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) || 418 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED)) 419 { 420 421 /* Only unload the DELETED and EDITED entries */ 422 if (UnloadKeyboardLayout(pCurrent->hkl)) 423 { 424 /* But the EDITED entries are used later */ 425 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 426 { 427 InputList_RemoveNode(pCurrent); 428 } 429 } 430 } 431 } 432 433 /* Add the DEFAULT entry and set font substitutes */ 434 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 435 { 436 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 437 continue; 438 439 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 440 { 441 bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId); 442 InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, 1, pCurrent); 443 444 /* Activate the DEFAULT entry */ 445 ActivateKeyboardLayout(pCurrent->hkl, KLF_RESET); 446 447 /* Save it */ 448 hDefaultKL = pCurrent->hkl; 449 break; 450 } 451 } 452 453 /* Add entries except DEFAULT to registry */ 454 dwNumber = 2; 455 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 456 { 457 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 458 continue; 459 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 460 continue; 461 462 InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, dwNumber, pCurrent); 463 464 ++dwNumber; 465 } 466 467 /* Remove ADDED and EDITED flags */ 468 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 469 { 470 pCurrent->wFlags &= ~(INPUT_LIST_NODE_FLAG_ADDED | INPUT_LIST_NODE_FLAG_EDITED); 471 } 472 473 /* Change the default keyboard language */ 474 if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG, 0, &hDefaultKL, 0)) 475 { 476 DWORD dwRecipients = BSM_ALLDESKTOPS | BSM_APPLICATIONS; 477 478 BroadcastSystemMessageW(BSF_POSTMESSAGE, 479 &dwRecipients, 480 WM_INPUTLANGCHANGEREQUEST, 481 INPUTLANGCHANGE_SYSCHARSET, 482 (LPARAM)hDefaultKL); 483 } 484 485 /* Retry to delete (in case of failure to delete the default keyboard) */ 486 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 487 { 488 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 489 { 490 UnloadKeyboardLayout(pCurrent->hkl); 491 InputList_RemoveNode(pCurrent); 492 } 493 } 494 495 RegCloseKey(hPreloadKey); 496 RegCloseKey(hSubstKey); 497 return bRet; 498 } 499 500 501 BOOL 502 InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout) 503 { 504 WCHAR szIndicator[MAX_STR_LEN]; 505 INPUT_LIST_NODE *pInput = NULL; 506 507 if (pLocale == NULL || pLayout == NULL) 508 { 509 return FALSE; 510 } 511 512 for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext) 513 { 514 if (pInput->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 515 continue; 516 517 if (pInput->pLocale == pLocale && pInput->pLayout == pLayout) 518 { 519 return FALSE; /* Already exists */ 520 } 521 } 522 523 pInput = InputList_AppendNode(); 524 pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED; 525 pInput->pLocale = pLocale; 526 pInput->pLayout = pLayout; 527 528 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId), 529 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE, 530 szIndicator, 531 ARRAYSIZE(szIndicator))) 532 { 533 size_t len = wcslen(szIndicator); 534 535 if (len > 0) 536 { 537 szIndicator[len - 1] = 0; 538 pInput->pszIndicator = _wcsdup(szIndicator); 539 } 540 } 541 542 return TRUE; 543 } 544 545 546 VOID 547 InputList_SetDefault(INPUT_LIST_NODE *pNode) 548 { 549 INPUT_LIST_NODE *pCurrent; 550 551 if (pNode == NULL) 552 return; 553 554 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 555 { 556 if (pCurrent == pNode) 557 { 558 pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; 559 } 560 else 561 { 562 pCurrent->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT; 563 } 564 } 565 } 566 567 INPUT_LIST_NODE * 568 InputList_FindNextDefault(INPUT_LIST_NODE *pNode) 569 { 570 INPUT_LIST_NODE *pCurrent; 571 572 for (pCurrent = pNode->pNext; pCurrent; pCurrent = pCurrent->pNext) 573 { 574 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 575 continue; 576 577 return pCurrent; 578 } 579 580 for (pCurrent = pNode->pPrev; pCurrent; pCurrent = pCurrent->pPrev) 581 { 582 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 583 continue; 584 585 return pCurrent; 586 } 587 588 return NULL; 589 } 590 591 /* 592 * It marks the input method for deletion, but does not delete it directly. 593 * To apply the changes using InputList_Process() 594 */ 595 BOOL 596 InputList_Remove(INPUT_LIST_NODE *pNode) 597 { 598 BOOL ret = FALSE; 599 BOOL bRemoveNode = FALSE; 600 601 if (pNode == NULL) 602 return FALSE; 603 604 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) 605 { 606 /* 607 * If the input method has been added to the list, but not yet written 608 * in the registry, then simply remove it from the list 609 */ 610 bRemoveNode = TRUE; 611 } 612 else 613 { 614 pNode->wFlags |= INPUT_LIST_NODE_FLAG_DELETED; 615 } 616 617 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 618 { 619 INPUT_LIST_NODE *pCurrent = InputList_FindNextDefault(pNode); 620 if (pCurrent) 621 pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; 622 623 pNode->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT; 624 ret = TRUE; /* default input is changed */ 625 } 626 627 if (bRemoveNode) 628 { 629 InputList_RemoveNode(pNode); 630 } 631 632 return ret; 633 } 634 635 BOOL 636 InputList_RemoveByLang(LANGID wLangId) 637 { 638 BOOL ret = FALSE; 639 INPUT_LIST_NODE *pCurrent; 640 641 Retry: 642 for (pCurrent = _InputList; pCurrent; pCurrent = pCurrent->pNext) 643 { 644 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 645 continue; 646 647 if (LOWORD(pCurrent->pLocale->dwId) == wLangId) 648 { 649 if (InputList_Remove(pCurrent)) 650 ret = TRUE; /* default input is changed */ 651 goto Retry; 652 } 653 } 654 655 return ret; 656 } 657 658 VOID 659 InputList_Create(VOID) 660 { 661 INT iLayoutCount, iIndex; 662 WCHAR szIndicator[MAX_STR_LEN]; 663 INPUT_LIST_NODE *pInput; 664 HKL *pLayoutList, hklDefault; 665 666 SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG, 0, &hklDefault, 0); 667 668 iLayoutCount = GetKeyboardLayoutList(0, NULL); 669 pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL)); 670 671 if (!pLayoutList || GetKeyboardLayoutList(iLayoutCount, pLayoutList) <= 0) 672 { 673 free(pLayoutList); 674 return; 675 } 676 677 for (iIndex = 0; iIndex < iLayoutCount; ++iIndex) 678 { 679 HKL hKL = pLayoutList[iIndex]; 680 LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(hKL); 681 LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(hKL); 682 if (!pLocale || !pLayout) 683 continue; 684 685 pInput = InputList_AppendNode(); 686 pInput->pLocale = pLocale; 687 pInput->pLayout = pLayout; 688 pInput->hkl = hKL; 689 690 if (pInput->hkl == hklDefault) /* Default HKL? */ 691 { 692 pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; 693 hklDefault = NULL; /* No more default item */ 694 } 695 696 /* Get abbrev language name */ 697 szIndicator[0] = 0; 698 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId), 699 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE, 700 szIndicator, 701 ARRAYSIZE(szIndicator))) 702 { 703 size_t len = wcslen(szIndicator); 704 if (len > 0) 705 { 706 szIndicator[len - 1] = 0; 707 pInput->pszIndicator = _wcsdup(szIndicator); 708 } 709 } 710 } 711 712 free(pLayoutList); 713 } 714 715 static INT InputList_Compare(INPUT_LIST_NODE *pNode1, INPUT_LIST_NODE *pNode2) 716 { 717 INT nCompare = _wcsicmp(pNode1->pszIndicator, pNode2->pszIndicator); 718 if (nCompare != 0) 719 return nCompare; 720 721 return _wcsicmp(pNode1->pLayout->pszName, pNode2->pLayout->pszName); 722 } 723 724 VOID InputList_Sort(VOID) 725 { 726 INPUT_LIST_NODE *pList = _InputList; 727 INPUT_LIST_NODE *pNext, *pPrev; 728 INPUT_LIST_NODE *pMinimum, *pNode; 729 730 _InputList = NULL; 731 732 while (pList) 733 { 734 /* Find the minimum node */ 735 pMinimum = NULL; 736 for (pNode = pList; pNode; pNode = pNext) 737 { 738 pNext = pNode->pNext; 739 740 if (pMinimum == NULL) 741 { 742 pMinimum = pNode; 743 } 744 else if (InputList_Compare(pNode, pMinimum) < 0) 745 { 746 pMinimum = pNode; 747 } 748 } 749 750 // Remove pMinimum from pList 751 pNext = pMinimum->pNext; 752 pPrev = pMinimum->pPrev; 753 if (pNext) 754 pNext->pPrev = pPrev; 755 if (pPrev) 756 pPrev->pNext = pNext; 757 else 758 pList = pNext; 759 760 // Append pMinimum to _InputList 761 if (!_InputList) 762 { 763 pMinimum->pPrev = pMinimum->pNext = NULL; 764 _InputList = pMinimum; 765 } 766 else 767 { 768 /* Find last node */ 769 for (pNode = _InputList; pNode->pNext; pNode = pNode->pNext) 770 { 771 NOTHING; 772 } 773 774 /* Add to the end */ 775 pNode->pNext = pMinimum; 776 pMinimum->pPrev = pNode; 777 pMinimum->pNext = NULL; 778 } 779 } 780 } 781 782 INT 783 InputList_GetAliveCount(VOID) 784 { 785 INPUT_LIST_NODE *pNode; 786 INT nCount = 0; 787 788 for (pNode = _InputList; pNode; pNode = pNode->pNext) 789 { 790 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 791 continue; 792 793 ++nCount; 794 } 795 796 return nCount; 797 } 798 799 INPUT_LIST_NODE* 800 InputList_GetFirst(VOID) 801 { 802 return _InputList; 803 } 804