[USER32][INPUT] Support various keyboard layouts (#4666)

- Fix IntLoadKeyboardLayout function to return the correct HKL value.
- Modify LAYOUT_LIST_NODE structure to add more information.
- Fix LayoutList_GetByHkl function to choose the IME HKLs correctly.
- Ignore DELETED entries correctly.
- Improve UI/UX.
CORE-11700, CORE-13244, CORE-18364
This commit is contained in:
Katayama Hirofumi MZ 2022-09-16 08:09:37 +09:00 committed by GitHub
parent 3f411c5da7
commit 596f04be6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 548 additions and 422 deletions

View file

@ -93,7 +93,7 @@ OnInitAddDialog(HWND hwndDlg)
iItemIndex = ComboBox_AddString(hwndLayoutCombo, pCurrentLayout->pszName); iItemIndex = ComboBox_AddString(hwndLayoutCombo, pCurrentLayout->pszName);
ComboBox_SetItemData(hwndLayoutCombo, iItemIndex, pCurrentLayout); ComboBox_SetItemData(hwndLayoutCombo, iItemIndex, pCurrentLayout);
if (pCurrentLayout->dwId == dwDefaultLayoutId) if (pCurrentLayout->dwKLID == dwDefaultLayoutId)
{ {
ComboBox_SetCurSel(hwndLayoutCombo, iItemIndex); ComboBox_SetCurSel(hwndLayoutCombo, iItemIndex);
} }
@ -159,7 +159,7 @@ OnCommandAddDialog(HWND hwndDlg, WPARAM wParam)
pCurrentLayout = (LAYOUT_LIST_NODE*)ComboBox_GetItemData(hwndLayoutCombo, iIndex); pCurrentLayout = (LAYOUT_LIST_NODE*)ComboBox_GetItemData(hwndLayoutCombo, iIndex);
if (pCurrentLayout != NULL && pCurrentLayout->dwId == dwLayoutId) if (pCurrentLayout != NULL && pCurrentLayout->dwKLID == dwLayoutId)
{ {
ComboBox_SetCurSel(hwndLayoutCombo, iIndex); ComboBox_SetCurSel(hwndLayoutCombo, iIndex);
break; break;

View file

@ -81,4 +81,17 @@ DWORDfromString(const WCHAR *pszString)
return wcstoul(pszString, &pszEnd, 16); return wcstoul(pszString, &pszEnd, 16);
} }
#define IME_MASK (0xE0000000UL)
#define SUBST_MASK (0xD0000000UL)
#define SPECIAL_MASK (0xF0000000UL)
#define IS_IME_HKL(hKL) ((((ULONG_PTR)(hKL)) & 0xF0000000) == IME_MASK)
#define IS_SPECIAL_HKL(hKL) ((((ULONG_PTR)(hKL)) & 0xF0000000) == SPECIAL_MASK)
#define SPECIALIDFROMHKL(hKL) ((WORD)(HIWORD(hKL) & 0x0FFF))
#define IS_IME_KLID(dwKLID) ((((ULONG)(dwKLID)) & 0xF0000000) == IME_MASK)
#define IS_SUBST_KLID(dwKLID) ((((ULONG)(dwKLID)) & 0xF0000000) == SUBST_MASK)
VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName);
#endif /* _INPUT_H */ #endif /* _INPUT_H */

View file

@ -23,9 +23,7 @@ BOOL UpdateRegistryForFontSubstitutes(MUI_SUBFONT *pSubstitutes)
static const WCHAR pszKey[] = static const WCHAR pszKey[] =
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes"; L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
hKey = NULL; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, pszKey, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
RegOpenKeyExW(HKEY_LOCAL_MACHINE, pszKey, 0, KEY_ALL_ACCESS, &hKey);
if (hKey == NULL)
return FALSE; return FALSE;
/* Overwrite only */ /* Overwrite only */
@ -37,10 +35,16 @@ BOOL UpdateRegistryForFontSubstitutes(MUI_SUBFONT *pSubstitutes)
} }
RegCloseKey(hKey); RegCloseKey(hKey);
return TRUE; return TRUE;
} }
VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName)
{
WCHAR szSysDir[MAX_PATH];
GetSystemDirectoryW(szSysDir, ARRAYSIZE(szSysDir));
StringCchPrintfW(pszPath, cchPath, L"%s\\%s", szSysDir, pszFileName);
}
BOOL BOOL
InputList_SetFontSubstitutes(LCID dwLocaleId) InputList_SetFontSubstitutes(LCID dwLocaleId)
{ {
@ -135,29 +139,28 @@ InputList_AppendNode(VOID)
INPUT_LIST_NODE *pCurrent; INPUT_LIST_NODE *pCurrent;
INPUT_LIST_NODE *pNew; INPUT_LIST_NODE *pNew;
pCurrent = _InputList;
pNew = (INPUT_LIST_NODE*)malloc(sizeof(INPUT_LIST_NODE)); pNew = (INPUT_LIST_NODE*)malloc(sizeof(INPUT_LIST_NODE));
if (pNew == NULL) if (pNew == NULL)
return NULL; return NULL;
ZeroMemory(pNew, sizeof(INPUT_LIST_NODE)); ZeroMemory(pNew, sizeof(INPUT_LIST_NODE));
if (pCurrent == NULL) if (_InputList == NULL) /* Empty? */
{ {
_InputList = pNew; _InputList = pNew;
return pNew;
} }
else
{
while (pCurrent->pNext != NULL)
{
pCurrent = pCurrent->pNext;
}
pNew->pPrev = pCurrent; /* Find last node */
pCurrent->pNext = pNew; for (pCurrent = _InputList; pCurrent->pNext; pCurrent = pCurrent->pNext)
{
;
} }
/* Add to the end */
pCurrent->pNext = pNew;
pNew->pPrev = pCurrent;
return pNew; return pNew;
} }
@ -193,20 +196,17 @@ VOID
InputList_Destroy(VOID) InputList_Destroy(VOID)
{ {
INPUT_LIST_NODE *pCurrent; INPUT_LIST_NODE *pCurrent;
INPUT_LIST_NODE *pNext;
if (_InputList == NULL) if (_InputList == NULL)
return; return;
pCurrent = _InputList; for (pCurrent = _InputList; pCurrent; pCurrent = pNext)
while (pCurrent != NULL)
{ {
INPUT_LIST_NODE *pNext = pCurrent->pNext; pNext = pCurrent->pNext;
free(pCurrent->pszIndicator); free(pCurrent->pszIndicator);
free(pCurrent); free(pCurrent);
pCurrent = pNext;
} }
_InputList = NULL; _InputList = NULL;
@ -214,11 +214,12 @@ InputList_Destroy(VOID)
static BOOL static BOOL
InputList_PrepareUserRegistry(VOID) InputList_PrepareUserRegistry(PHKEY phPreloadKey, PHKEY phSubstKey)
{ {
BOOL bResult = FALSE; BOOL bResult = FALSE;
HKEY hTempKey = NULL; HKEY hKey;
HKEY hKey = NULL;
*phPreloadKey = *phSubstKey = NULL;
if (RegOpenKeyExW(HKEY_CURRENT_USER, if (RegOpenKeyExW(HKEY_CURRENT_USER,
L"Keyboard Layout", L"Keyboard Layout",
@ -232,101 +233,143 @@ InputList_PrepareUserRegistry(VOID)
RegCloseKey(hKey); RegCloseKey(hKey);
} }
if (RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout", &hKey) != ERROR_SUCCESS) if (RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout", &hKey) == ERROR_SUCCESS &&
RegCreateKeyW(hKey, L"Preload", phPreloadKey) == ERROR_SUCCESS &&
RegCreateKeyW(hKey, L"Substitutes", phSubstKey) == ERROR_SUCCESS)
{ {
goto Cleanup; bResult = TRUE;
} }
if (RegCreateKeyW(hKey, L"Preload", &hTempKey) != ERROR_SUCCESS) if (hKey)
{
goto Cleanup;
}
RegCloseKey(hTempKey);
if (RegCreateKeyW(hKey, L"Substitutes", &hTempKey) != ERROR_SUCCESS)
{
goto Cleanup;
}
RegCloseKey(hTempKey);
bResult = TRUE;
Cleanup:
if (hTempKey != NULL)
RegCloseKey(hTempKey);
if (hKey != NULL)
RegCloseKey(hKey); RegCloseKey(hKey);
return bResult; return bResult;
} }
static BOOL
InputList_FindPreloadKLID(HKEY hPreloadKey, DWORD dwKLID)
{
DWORD dwNumber, dwType, cbValue;
WCHAR szNumber[16], szValue[KL_NAMELENGTH], szKLID[KL_NAMELENGTH];
StringCchPrintfW(szKLID, ARRAYSIZE(szKLID), L"%08x", dwKLID);
for (dwNumber = 1; dwNumber <= 1000; ++dwNumber)
{
StringCchPrintfW(szNumber, ARRAYSIZE(szNumber), L"%u", dwNumber);
cbValue = ARRAYSIZE(szValue) * sizeof(WCHAR);
if (RegQueryValueExW(hPreloadKey, szNumber, NULL, &dwType,
(LPBYTE)szValue, &cbValue) != ERROR_SUCCESS)
{
break;
}
if (dwType != REG_SZ)
continue;
szValue[ARRAYSIZE(szValue) - 1] = 0;
if (_wcsicmp(szKLID, szValue) == 0)
return TRUE;
}
return FALSE;
}
static BOOL
InputList_WriteSubst(HKEY hSubstKey, DWORD dwPhysicalKLID, DWORD dwLogicalKLID)
{
DWORD cbValue;
WCHAR szLogicalKLID[KL_NAMELENGTH], szPhysicalKLID[KL_NAMELENGTH];
StringCchPrintfW(szLogicalKLID, ARRAYSIZE(szLogicalKLID), L"%08x", dwLogicalKLID);
StringCchPrintfW(szPhysicalKLID, ARRAYSIZE(szPhysicalKLID), L"%08x", dwPhysicalKLID);
cbValue = (wcslen(szPhysicalKLID) + 1) * sizeof(WCHAR);
return RegSetValueExW(hSubstKey, szLogicalKLID, 0, REG_SZ, (LPBYTE)szPhysicalKLID,
cbValue) == ERROR_SUCCESS;
}
static DWORD
InputList_DoSubst(HKEY hPreloadKey, HKEY hSubstKey,
DWORD dwPhysicalKLID, DWORD dwLogicalKLID)
{
DWORD iTrial;
BOOL bSubstNeeded = (dwPhysicalKLID != dwLogicalKLID) || (HIWORD(dwPhysicalKLID) != 0);
for (iTrial = 1; iTrial <= 1000; ++iTrial)
{
if (!InputList_FindPreloadKLID(hPreloadKey, dwLogicalKLID)) /* Not found? */
{
if (bSubstNeeded)
{
/* Write now */
InputList_WriteSubst(hSubstKey, dwPhysicalKLID, dwLogicalKLID);
}
return dwLogicalKLID;
}
bSubstNeeded = TRUE;
/* Calculate the next logical KLID */
if (!IS_SUBST_KLID(dwLogicalKLID))
{
dwLogicalKLID |= SUBST_MASK;
}
else
{
WORD wLow = LOWORD(dwLogicalKLID);
WORD wHigh = HIWORD(dwLogicalKLID);
dwLogicalKLID = MAKELONG(wLow, wHigh + 1);
}
}
return 0;
}
static VOID static VOID
InputList_AddInputMethodToUserRegistry(DWORD dwIndex, INPUT_LIST_NODE *pNode) InputList_AddInputMethodToUserRegistry(
HKEY hPreloadKey,
HKEY hSubstKey,
DWORD dwNumber,
INPUT_LIST_NODE *pNode)
{ {
WCHAR szMethodIndex[MAX_PATH]; WCHAR szNumber[32], szLogicalKLID[KL_NAMELENGTH];
WCHAR szPreload[MAX_PATH]; DWORD dwPhysicalKLID, dwLogicalKLID, cbValue;
BOOL bIsImeMethod = FALSE; HKL hKL = pNode->hkl;
HKEY hKey;
StringCchPrintfW(szMethodIndex, ARRAYSIZE(szMethodIndex), L"%lu", dwIndex); if (IS_IME_HKL(hKL)) /* IME? */
/* Check is IME method */
if ((HIWORD(pNode->pLayout->dwId) & 0xF000) == 0xE000)
{ {
StringCchPrintfW(szPreload, ARRAYSIZE(szPreload), L"%08X", pNode->pLayout->dwId); /* Do not substitute the IME KLID */
bIsImeMethod = TRUE; dwLogicalKLID = dwPhysicalKLID = HandleToUlong(hKL);
} }
else else
{ {
StringCchPrintfW(szPreload, ARRAYSIZE(szPreload), L"%08X", pNode->pLocale->dwId); /* Substitute the KLID if necessary */
dwPhysicalKLID = pNode->pLayout->dwKLID;
dwLogicalKLID = pNode->pLocale->dwId;
dwLogicalKLID = InputList_DoSubst(hPreloadKey, hSubstKey, dwPhysicalKLID, dwLogicalKLID);
} }
if (RegOpenKeyExW(HKEY_CURRENT_USER, /* Write the Preload value (number |--> logical KLID) */
L"Keyboard Layout\\Preload", StringCchPrintfW(szNumber, ARRAYSIZE(szNumber), L"%lu", dwNumber);
0, StringCchPrintfW(szLogicalKLID, ARRAYSIZE(szLogicalKLID), L"%08x", dwLogicalKLID);
KEY_SET_VALUE, cbValue = (wcslen(szLogicalKLID) + 1) * sizeof(WCHAR);
&hKey) == ERROR_SUCCESS) RegSetValueExW(hPreloadKey,
{ szNumber,
RegSetValueExW(hKey, 0,
szMethodIndex, REG_SZ,
0, (LPBYTE)szLogicalKLID,
REG_SZ, cbValue);
(LPBYTE)szPreload,
(wcslen(szPreload) + 1) * sizeof(WCHAR));
RegCloseKey(hKey);
}
if (pNode->pLocale->dwId != pNode->pLayout->dwId && bIsImeMethod == FALSE)
{
if (RegOpenKeyExW(HKEY_CURRENT_USER,
L"Keyboard Layout\\Substitutes",
0,
KEY_SET_VALUE,
&hKey) == ERROR_SUCCESS)
{
WCHAR szSubstitutes[MAX_PATH];
StringCchPrintfW(szSubstitutes, ARRAYSIZE(szSubstitutes), L"%08X", pNode->pLayout->dwId);
RegSetValueExW(hKey,
szPreload,
0,
REG_SZ,
(LPBYTE)szSubstitutes,
(wcslen(szSubstitutes) + 1) * sizeof(WCHAR));
RegCloseKey(hKey);
}
}
if ((pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) || if ((pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) ||
(pNode->wFlags & INPUT_LIST_NODE_FLAG_EDITED)) (pNode->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
{ {
pNode->hkl = LoadKeyboardLayoutW(szPreload, KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL); UINT uFlags = KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL;
if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
uFlags |= KLF_REPLACELANG;
pNode->hkl = LoadKeyboardLayoutW(szLogicalKLID, uFlags);
} }
} }
@ -338,19 +381,30 @@ BOOL
InputList_Process(VOID) InputList_Process(VOID)
{ {
INPUT_LIST_NODE *pCurrent; INPUT_LIST_NODE *pCurrent;
DWORD dwIndex; DWORD dwNumber;
BOOL bRet = FALSE; BOOL bRet = FALSE;
HKEY hPreloadKey, hSubstKey;
/* Process deleted and edited input methods */ if (!InputList_PrepareUserRegistry(&hPreloadKey, &hSubstKey))
{
if (hPreloadKey)
RegCloseKey(hPreloadKey);
if (hSubstKey)
RegCloseKey(hSubstKey);
return FALSE;
}
/* Process DELETED and EDITED entries */
for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
{ {
if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) || if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) ||
(pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED)) (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
{ {
/* Only unload the DELETED and EDITED entries */
if (UnloadKeyboardLayout(pCurrent->hkl)) if (UnloadKeyboardLayout(pCurrent->hkl))
{ {
/* Only unload the edited input method, but does not delete it from the list */ /* But the EDITED entries are used later */
if (!(pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED)) if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
{ {
InputList_RemoveNode(pCurrent); InputList_RemoveNode(pCurrent);
} }
@ -358,27 +412,44 @@ InputList_Process(VOID)
} }
} }
InputList_PrepareUserRegistry(); /* Add the DEFAULT entry and set font substitutes */
/* Find default input method */
for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
{ {
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
continue;
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
{ {
bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId); bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId);
InputList_AddInputMethodToUserRegistry(1, pCurrent); InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, 1, pCurrent);
break; break;
} }
} }
if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG, /* Add entries except DEFAULT to registry */
0, dwNumber = 2;
(LPVOID)((LPDWORD)&pCurrent->hkl), for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
0))
{ {
DWORD dwRecipients; if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
continue;
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
continue;
dwRecipients = BSM_ALLCOMPONENTS; InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, dwNumber, pCurrent);
++dwNumber;
}
/* Remove ADDED and EDITED flags */
for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
{
pCurrent->wFlags &= ~(INPUT_LIST_NODE_FLAG_ADDED | INPUT_LIST_NODE_FLAG_EDITED);
}
/* Change the default keyboard language */
if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG, 0, &pCurrent->hkl, 0))
{
DWORD dwRecipients = BSM_ALLCOMPONENTS | BSM_ALLDESKTOPS;
BroadcastSystemMessageW(BSF_POSTMESSAGE, BroadcastSystemMessageW(BSF_POSTMESSAGE,
&dwRecipients, &dwRecipients,
@ -387,19 +458,18 @@ InputList_Process(VOID)
(LPARAM)pCurrent->hkl); (LPARAM)pCurrent->hkl);
} }
/* Add methods to registry */ /* Retry to delete (in case of failure to delete the default keyboard) */
dwIndex = 2;
for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
{ {
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
continue; {
UnloadKeyboardLayout(pCurrent->hkl);
InputList_AddInputMethodToUserRegistry(dwIndex, pCurrent); InputList_RemoveNode(pCurrent);
}
dwIndex++;
} }
RegCloseKey(hPreloadKey);
RegCloseKey(hSubstKey);
return bRet; return bRet;
} }
@ -408,7 +478,7 @@ BOOL
InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout) InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout)
{ {
WCHAR szIndicator[MAX_STR_LEN]; WCHAR szIndicator[MAX_STR_LEN];
INPUT_LIST_NODE *pInput; INPUT_LIST_NODE *pInput = NULL;
if (pLocale == NULL || pLayout == NULL) if (pLocale == NULL || pLayout == NULL)
{ {
@ -417,16 +487,17 @@ InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout)
for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext) for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext)
{ {
if (pInput->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
continue;
if (pInput->pLocale == pLocale && pInput->pLayout == pLayout) if (pInput->pLocale == pLocale && pInput->pLayout == pLayout)
{ {
return FALSE; return FALSE; /* Already exists */
} }
} }
pInput = InputList_AppendNode(); pInput = InputList_AppendNode();
pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED; pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED;
pInput->pLocale = pLocale; pInput->pLocale = pLocale;
pInput->pLayout = pLayout; pInput->pLayout = pLayout;
@ -469,6 +540,29 @@ InputList_SetDefault(INPUT_LIST_NODE *pNode)
} }
} }
INPUT_LIST_NODE *
InputList_FindNextDefault(INPUT_LIST_NODE *pNode)
{
INPUT_LIST_NODE *pCurrent;
for (pCurrent = pNode->pNext; pCurrent; pCurrent = pCurrent->pNext)
{
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
continue;
return pCurrent;
}
for (pCurrent = pNode->pPrev; pCurrent; pCurrent = pCurrent->pPrev)
{
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
continue;
return pCurrent;
}
return NULL;
}
/* /*
* It marks the input method for deletion, but does not delete it directly. * It marks the input method for deletion, but does not delete it directly.
@ -492,22 +586,19 @@ InputList_Remove(INPUT_LIST_NODE *pNode)
} }
else else
{ {
pNode->wFlags = INPUT_LIST_NODE_FLAG_DELETED; pNode->wFlags |= INPUT_LIST_NODE_FLAG_DELETED;
} }
if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
{ {
if (pNode->pNext != NULL) INPUT_LIST_NODE *pCurrent = InputList_FindNextDefault(pNode);
{ if (pCurrent)
pNode->pNext->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
}
else if (pNode->pPrev != NULL) pNode->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT;
{
pNode->pPrev->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
}
} }
if (bRemoveNode != FALSE) if (bRemoveNode)
{ {
InputList_RemoveNode(pNode); InputList_RemoveNode(pNode);
} }
@ -517,67 +608,58 @@ InputList_Remove(INPUT_LIST_NODE *pNode)
VOID VOID
InputList_Create(VOID) InputList_Create(VOID)
{ {
INT iLayoutCount; INT iLayoutCount, iIndex;
HKL *pLayoutList; WCHAR szIndicator[MAX_STR_LEN];
INPUT_LIST_NODE *pInput;
HKL *pLayoutList, hklDefault;
SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG, 0, &hklDefault, 0);
iLayoutCount = GetKeyboardLayoutList(0, NULL); iLayoutCount = GetKeyboardLayoutList(0, NULL);
pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL)); pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL));
if (pLayoutList != NULL) if (!pLayoutList || GetKeyboardLayoutList(iLayoutCount, pLayoutList) <= 0)
{ {
if (GetKeyboardLayoutList(iLayoutCount, pLayoutList) > 0) free(pLayoutList);
return;
}
for (iIndex = 0; iIndex < iLayoutCount; ++iIndex)
{
HKL hKL = pLayoutList[iIndex];
LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(hKL);
LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(hKL);
if (!pLocale || !pLayout)
continue;
pInput = InputList_AppendNode();
pInput->pLocale = pLocale;
pInput->pLayout = pLayout;
pInput->hkl = hKL;
if (pInput->hkl == hklDefault) /* Default HKL? */
{ {
INT iIndex; pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
hklDefault = NULL; /* No more default item */
for (iIndex = 0; iIndex < iLayoutCount; iIndex++)
{
LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(pLayoutList[iIndex]);
LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(pLayoutList[iIndex]);
if (pLocale != NULL && pLayout != NULL)
{
WCHAR szIndicator[MAX_STR_LEN] = { 0 };
INPUT_LIST_NODE *pInput;
HKL hklDefault;
pInput = InputList_AppendNode();
pInput->pLocale = pLocale;
pInput->pLayout = pLayout;
pInput->hkl = pLayoutList[iIndex];
if (SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG,
0,
(LPVOID)((LPDWORD)&hklDefault),
0) == FALSE)
{
hklDefault = GetKeyboardLayout(0);
}
if (pInput->hkl == hklDefault)
{
pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
}
if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
szIndicator,
ARRAYSIZE(szIndicator)))
{
size_t len = wcslen(szIndicator);
if (len > 0)
{
szIndicator[len - 1] = 0;
pInput->pszIndicator = _wcsdup(szIndicator);
}
}
}
}
} }
free(pLayoutList); /* Get abbrev language name */
szIndicator[0] = 0;
if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
szIndicator,
ARRAYSIZE(szIndicator)))
{
size_t len = wcslen(szIndicator);
if (len > 0)
{
szIndicator[len - 1] = 0;
pInput->pszIndicator = _wcsdup(szIndicator);
}
}
} }
free(pLayoutList);
} }

View file

@ -4,12 +4,30 @@
#include "locale_list.h" #include "locale_list.h"
#include "layout_list.h" #include "layout_list.h"
/*
* INPUT_LIST_NODE_FLAG_EDITED
* --- The modification flag. Since previous time, this entry is modified.
*/
#define INPUT_LIST_NODE_FLAG_EDITED 0x0001 #define INPUT_LIST_NODE_FLAG_EDITED 0x0001
#define INPUT_LIST_NODE_FLAG_ADDED 0x0002
#define INPUT_LIST_NODE_FLAG_DELETED 0x0004
#define INPUT_LIST_NODE_FLAG_DEFAULT 0x0008
/*
* INPUT_LIST_NODE_FLAG_ADDED
* --- The addition flag. Since previous time, this entry is newly added.
*/
#define INPUT_LIST_NODE_FLAG_ADDED 0x0002
/*
* INPUT_LIST_NODE_FLAG_DELETED
* --- The deletion flag.
* The application should ignore the entry with this flag if necessary.
*/
#define INPUT_LIST_NODE_FLAG_DELETED 0x0004
/*
* INPUT_LIST_NODE_FLAG_DEFAULT
* --- The default flag. The entry with this flag should be single in the list.
*/
#define INPUT_LIST_NODE_FLAG_DEFAULT 0x0008
typedef struct _INPUT_LIST_NODE typedef struct _INPUT_LIST_NODE
{ {
@ -20,7 +38,7 @@ typedef struct _INPUT_LIST_NODE
HKL hkl; /* Only for loaded input methods */ HKL hkl; /* Only for loaded input methods */
WCHAR *pszIndicator; LPWSTR pszIndicator;
struct _INPUT_LIST_NODE *pPrev; struct _INPUT_LIST_NODE *pPrev;
struct _INPUT_LIST_NODE *pNext; struct _INPUT_LIST_NODE *pNext;

View file

@ -3,16 +3,16 @@
* FILE: dll/cpl/input/layout_list.c * FILE: dll/cpl/input/layout_list.c
* PURPOSE: input.dll * PURPOSE: input.dll
* PROGRAMMER: Dmitry Chapyshev (dmitry@reactos.org) * PROGRAMMER: Dmitry Chapyshev (dmitry@reactos.org)
* Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
*/ */
#include "layout_list.h" #include "layout_list.h"
static LAYOUT_LIST_NODE *_LayoutList = NULL; static LAYOUT_LIST_NODE *_LayoutList = NULL;
static LAYOUT_LIST_NODE* static LAYOUT_LIST_NODE*
LayoutList_AppendNode(DWORD dwId, DWORD dwSpecialId, const WCHAR *pszName) LayoutList_AppendNode(DWORD dwKLID, WORD wSpecialId, LPCWSTR pszFile, LPCWSTR pszName,
LPCWSTR pszImeFile)
{ {
LAYOUT_LIST_NODE *pCurrent; LAYOUT_LIST_NODE *pCurrent;
LAYOUT_LIST_NODE *pNew; LAYOUT_LIST_NODE *pNew;
@ -28,16 +28,22 @@ LayoutList_AppendNode(DWORD dwId, DWORD dwSpecialId, const WCHAR *pszName)
ZeroMemory(pNew, sizeof(LAYOUT_LIST_NODE)); ZeroMemory(pNew, sizeof(LAYOUT_LIST_NODE));
pNew->dwKLID = dwKLID;
pNew->wSpecialId = wSpecialId;
pNew->pszName = _wcsdup(pszName); pNew->pszName = _wcsdup(pszName);
if (pNew->pszName == NULL) pNew->pszFile = _wcsdup(pszFile);
pNew->pszImeFile = _wcsdup(pszImeFile);
if (pNew->pszName == NULL || pNew->pszFile == NULL ||
(pszImeFile && pNew->pszImeFile == NULL))
{ {
free(pNew->pszName);
free(pNew->pszFile);
free(pNew->pszImeFile);
free(pNew); free(pNew);
return NULL; return NULL;
} }
pNew->dwId = dwId;
pNew->dwSpecialId = dwSpecialId;
if (pCurrent == NULL) if (pCurrent == NULL)
{ {
_LayoutList = pNew; _LayoutList = pNew;
@ -61,90 +67,136 @@ VOID
LayoutList_Destroy(VOID) LayoutList_Destroy(VOID)
{ {
LAYOUT_LIST_NODE *pCurrent; LAYOUT_LIST_NODE *pCurrent;
LAYOUT_LIST_NODE *pNext;
if (_LayoutList == NULL) if (_LayoutList == NULL)
return; return;
pCurrent = _LayoutList; for (pCurrent = _LayoutList; pCurrent; pCurrent = pNext)
while (pCurrent != NULL)
{ {
LAYOUT_LIST_NODE *pNext = pCurrent->pNext; pNext = pCurrent->pNext;
free(pCurrent->pszName); free(pCurrent->pszName);
free(pCurrent->pszFile);
free(pCurrent->pszImeFile);
free(pCurrent); free(pCurrent);
pCurrent = pNext;
} }
_LayoutList = NULL; _LayoutList = NULL;
} }
static BOOL typedef HRESULT (WINAPI *FN_SHLoadRegUIStringW)(HKEY, LPCWSTR, LPWSTR, DWORD);
LayoutList_ReadLayout(HKEY hLayoutKey, LPCWSTR szLayoutId, LPCWSTR szSystemDirectory)
{
WCHAR szBuffer[MAX_PATH], szFilePath[MAX_PATH], szDllPath[MAX_PATH];
INT iIndex, iLength = 0;
DWORD dwSize, dwSpecialId, dwLayoutId = DWORDfromString(szLayoutId);
HINSTANCE hDllInst;
dwSize = sizeof(szBuffer); /* FIXME: Use shlwapi!SHLoadRegUIStringW instead when it is fully implemented */
HRESULT FakeSHLoadRegUIStringW(HKEY hkey, LPCWSTR value, LPWSTR buf, DWORD size)
{
#if 1
PWCHAR pBuffer, pIndex;
WCHAR szDllPath[MAX_PATH];
DWORD dwSize;
HINSTANCE hDllInst;
INT iIndex, iLength;
dwSize = size * sizeof(WCHAR);
if (RegQueryValueExW(hkey, value, NULL, NULL, (LPBYTE)buf, &dwSize) != ERROR_SUCCESS)
return E_FAIL;
if (buf[0] != L'@')
return S_OK;
/* Move to the position after the character "@" */
pBuffer = buf + 1;
/* Get a pointer to the beginning ",-" */
pIndex = wcsstr(pBuffer, L",-");
if (!pIndex)
return E_FAIL;
/* Convert the number in the string after the ",-" */
iIndex = _wtoi(pIndex + 2);
*pIndex = 0; /* Cut the string */
if (ExpandEnvironmentStringsW(pBuffer, szDllPath, ARRAYSIZE(szDllPath)) == 0)
return E_FAIL;
hDllInst = LoadLibraryW(szDllPath);
if (!hDllInst)
return E_FAIL;
iLength = LoadStringW(hDllInst, iIndex, buf, size);
FreeLibrary(hDllInst);
if (iLength <= 0)
return E_FAIL;
return S_OK;
#else
HRESULT hr = E_FAIL;
HINSTANCE hSHLWAPI = LoadLibraryW(L"shlwapi");
FN_SHLoadRegUIStringW fn;
fn = (FN_SHLoadRegUIStringW)GetProcAddress(hSHLWAPI, (LPCSTR)(INT_PTR)439);
if (fn)
hr = fn(hkey, value, buf, size);
FreeLibrary(hSHLWAPI);
return hr;
#endif
}
static BOOL
LayoutList_ReadLayout(HKEY hLayoutKey, LPCWSTR szKLID, LPCWSTR szSystemDirectory)
{
WCHAR szFile[80], szImeFile[80], szBuffer[MAX_PATH], szFilePath[MAX_PATH];
DWORD dwSize, dwKLID = DWORDfromString(szKLID);
WORD wSpecialId = 0;
LPWSTR pszImeFile = NULL;
dwSize = sizeof(szFile);
if (RegQueryValueExW(hLayoutKey, L"Layout File", NULL, NULL, if (RegQueryValueExW(hLayoutKey, L"Layout File", NULL, NULL,
(LPBYTE)szBuffer, &dwSize) != ERROR_SUCCESS) (LPBYTE)szFile, &dwSize) != ERROR_SUCCESS)
{ {
return FALSE; /* No "Layout File" value */ return FALSE; /* No "Layout File" value */
} }
if (IS_IME_KLID(dwKLID))
{
WCHAR szPath[MAX_PATH];
dwSize = sizeof(szImeFile);
if (RegQueryValueExW(hLayoutKey, L"IME File", NULL, NULL,
(LPBYTE)szImeFile, &dwSize) != ERROR_SUCCESS)
{
return FALSE; /* No "IME File" value */
}
if (wcschr(szImeFile, L'\\') != NULL)
return FALSE; /* Invalid character */
GetSystemLibraryPath(szPath, ARRAYSIZE(szPath), szImeFile);
if (GetFileAttributesW(szPath) == INVALID_FILE_ATTRIBUTES)
return FALSE; /* Does not exist */
pszImeFile = szImeFile;
}
/* Build the "Layout File" full path and check existence */ /* Build the "Layout File" full path and check existence */
StringCchPrintfW(szFilePath, ARRAYSIZE(szFilePath), L"%s\\%s", szSystemDirectory, szBuffer); StringCchPrintfW(szFilePath, ARRAYSIZE(szFilePath), L"%s\\%s", szSystemDirectory, szFile);
if (GetFileAttributesW(szFilePath) == INVALID_FILE_ATTRIBUTES) if (GetFileAttributesW(szFilePath) == INVALID_FILE_ATTRIBUTES)
return FALSE; /* No layout file found */ return FALSE; /* No layout file found */
/* Get the special ID */ /* Get the special ID */
dwSpecialId = 0;
dwSize = sizeof(szBuffer); dwSize = sizeof(szBuffer);
if (RegQueryValueExW(hLayoutKey, L"Layout Id", NULL, NULL, if (RegQueryValueExW(hLayoutKey, L"Layout Id", NULL, NULL,
(LPBYTE)szBuffer, &dwSize) == ERROR_SUCCESS) (LPBYTE)szBuffer, &dwSize) == ERROR_SUCCESS)
{ {
dwSpecialId = DWORDfromString(szBuffer); wSpecialId = LOWORD(DWORDfromString(szBuffer));
} }
/* If there is a valid "Layout Display Name", then use it as the entry name */ /* If there is a valid "Layout Display Name", then use it as the entry name */
dwSize = sizeof(szBuffer); if (FakeSHLoadRegUIStringW(hLayoutKey, L"Layout Display Name",
if (RegQueryValueExW(hLayoutKey, L"Layout Display Name", NULL, NULL, szBuffer, ARRAYSIZE(szBuffer)) == S_OK)
(LPBYTE)szBuffer, &dwSize) == ERROR_SUCCESS && szBuffer[0] == L'@')
{ {
/* FIXME: Use shlwapi!SHLoadRegUIStringW instead if it had fully implemented */ LayoutList_AppendNode(dwKLID, wSpecialId, szFile, szBuffer, pszImeFile);
return TRUE;
/* Move to the position after the character "@" */
WCHAR *pBuffer = szBuffer + 1;
/* Get a pointer to the beginning ",-" */
WCHAR *pIndex = wcsstr(pBuffer, L",-");
if (pIndex)
{
/* Convert the number in the string after the ",-" */
iIndex = _wtoi(pIndex + 2);
*pIndex = 0; /* Cut the string */
if (ExpandEnvironmentStringsW(pBuffer, szDllPath, ARRAYSIZE(szDllPath)) != 0)
{
hDllInst = LoadLibraryW(szDllPath);
if (hDllInst)
{
iLength = LoadStringW(hDllInst, iIndex, szBuffer, ARRAYSIZE(szBuffer));
FreeLibrary(hDllInst);
if (iLength > 0)
{
LayoutList_AppendNode(dwLayoutId, dwSpecialId, szBuffer);
return TRUE;
}
}
}
}
} }
/* Otherwise, use "Layout Text" value as the entry name */ /* Otherwise, use "Layout Text" value as the entry name */
@ -152,7 +204,7 @@ LayoutList_ReadLayout(HKEY hLayoutKey, LPCWSTR szLayoutId, LPCWSTR szSystemDirec
if (RegQueryValueExW(hLayoutKey, L"Layout Text", NULL, NULL, if (RegQueryValueExW(hLayoutKey, L"Layout Text", NULL, NULL,
(LPBYTE)szBuffer, &dwSize) == ERROR_SUCCESS) (LPBYTE)szBuffer, &dwSize) == ERROR_SUCCESS)
{ {
LayoutList_AppendNode(dwLayoutId, dwSpecialId, szBuffer); LayoutList_AppendNode(dwKLID, wSpecialId, szFile, szBuffer, pszImeFile);
return TRUE; return TRUE;
} }
@ -162,7 +214,7 @@ LayoutList_ReadLayout(HKEY hLayoutKey, LPCWSTR szLayoutId, LPCWSTR szSystemDirec
VOID VOID
LayoutList_Create(VOID) LayoutList_Create(VOID)
{ {
WCHAR szSystemDirectory[MAX_PATH], szLayoutId[MAX_PATH]; WCHAR szSystemDirectory[MAX_PATH], szKLID[KL_NAMELENGTH];
DWORD dwSize, dwIndex; DWORD dwSize, dwIndex;
HKEY hKey, hLayoutKey; HKEY hKey, hLayoutKey;
@ -170,23 +222,23 @@ LayoutList_Create(VOID)
return; return;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts",
0, KEY_ENUMERATE_SUB_KEYS, &hKey) != ERROR_SUCCESS) 0, KEY_READ, &hKey) != ERROR_SUCCESS)
{ {
return; return;
} }
for (dwIndex = 0; ; ++dwIndex) for (dwIndex = 0; ; ++dwIndex)
{ {
dwSize = ARRAYSIZE(szLayoutId); dwSize = ARRAYSIZE(szKLID);
if (RegEnumKeyExW(hKey, dwIndex, szLayoutId, &dwSize, NULL, NULL, if (RegEnumKeyExW(hKey, dwIndex, szKLID, &dwSize, NULL, NULL,
NULL, NULL) != ERROR_SUCCESS) NULL, NULL) != ERROR_SUCCESS)
{ {
break; break;
} }
if (RegOpenKeyExW(hKey, szLayoutId, 0, KEY_QUERY_VALUE, &hLayoutKey) == ERROR_SUCCESS) if (RegOpenKeyExW(hKey, szKLID, 0, KEY_QUERY_VALUE, &hLayoutKey) == ERROR_SUCCESS)
{ {
LayoutList_ReadLayout(hLayoutKey, szLayoutId, szSystemDirectory); LayoutList_ReadLayout(hLayoutKey, szKLID, szSystemDirectory);
RegCloseKey(hLayoutKey); RegCloseKey(hLayoutKey);
} }
} }
@ -200,13 +252,23 @@ LayoutList_GetByHkl(HKL hkl)
{ {
LAYOUT_LIST_NODE *pCurrent; LAYOUT_LIST_NODE *pCurrent;
if ((HIWORD(hkl) & 0xF000) == 0xF000) if (IS_SPECIAL_HKL(hkl))
{ {
DWORD dwSpecialId = (HIWORD(hkl) & 0x0FFF); WORD wSpecialId = SPECIALIDFROMHKL(hkl);
for (pCurrent = _LayoutList; pCurrent != NULL; pCurrent = pCurrent->pNext) for (pCurrent = _LayoutList; pCurrent != NULL; pCurrent = pCurrent->pNext)
{ {
if (dwSpecialId == pCurrent->dwSpecialId) if (wSpecialId == pCurrent->wSpecialId)
{
return pCurrent;
}
}
}
else if (IS_IME_HKL(hkl))
{
for (pCurrent = _LayoutList; pCurrent != NULL; pCurrent = pCurrent->pNext)
{
if (hkl == UlongToHandle(pCurrent->dwKLID))
{ {
return pCurrent; return pCurrent;
} }
@ -216,7 +278,7 @@ LayoutList_GetByHkl(HKL hkl)
{ {
for (pCurrent = _LayoutList; pCurrent != NULL; pCurrent = pCurrent->pNext) for (pCurrent = _LayoutList; pCurrent != NULL; pCurrent = pCurrent->pNext)
{ {
if (HIWORD(hkl) == LOWORD(pCurrent->dwId)) if (HIWORD(hkl) == LOWORD(pCurrent->dwKLID))
{ {
return pCurrent; return pCurrent;
} }

View file

@ -4,10 +4,11 @@
typedef struct _LAYOUT_LIST_NODE typedef struct _LAYOUT_LIST_NODE
{ {
WCHAR *pszName; DWORD dwKLID; /* The physical KLID */
WORD wSpecialId; /* The special ID */
DWORD dwId; LPWSTR pszName; /* The layout text */
DWORD dwSpecialId; LPWSTR pszFile; /* The layout file */
LPWSTR pszImeFile; /* The IME file */
struct _LAYOUT_LIST_NODE *pPrev; struct _LAYOUT_LIST_NODE *pPrev;
struct _LAYOUT_LIST_NODE *pNext; struct _LAYOUT_LIST_NODE *pNext;

View file

@ -11,7 +11,6 @@
#include "locale_list.h" #include "locale_list.h"
#include "input_list.h" #include "input_list.h"
static HICON static HICON
CreateLayoutIcon(LPWSTR szLayout, BOOL bIsDefault) CreateLayoutIcon(LPWSTR szLayout, BOOL bIsDefault)
{ {
@ -112,23 +111,37 @@ CreateLayoutIcon(LPWSTR szLayout, BOOL bIsDefault)
static VOID static VOID
SetControlsState(HWND hwndDlg, BOOL bIsEnabled) SetControlsState(HWND hwndDlg)
{ {
EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE_BUTTON), bIsEnabled); HWND hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
EnableWindow(GetDlgItem(hwndDlg, IDC_PROP_BUTTON), bIsEnabled); INT iSelected = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
EnableWindow(GetDlgItem(hwndDlg, IDC_SET_DEFAULT), bIsEnabled); INT nCount = ListView_GetItemCount(hwndList);
} BOOL bCanRemove = (iSelected != -1) && (nCount >= 2);
BOOL bCanDefault = (iSelected != -1) && (nCount >= 2);
BOOL bCanProp = (iSelected != -1);
LV_ITEM item = { LVIF_PARAM, iSelected };
if (ListView_GetItem(hwndList, &item))
{
INPUT_LIST_NODE *pInput = (INPUT_LIST_NODE*)item.lParam;
if (pInput && (pInput->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT))
{
bCanDefault = FALSE;
}
}
EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE_BUTTON), bCanRemove);
EnableWindow(GetDlgItem(hwndDlg, IDC_PROP_BUTTON), bCanProp);
EnableWindow(GetDlgItem(hwndDlg, IDC_SET_DEFAULT), bCanDefault);
}
static VOID static VOID
AddToInputListView(HWND hwndList, INPUT_LIST_NODE *pInputNode) AddToInputListView(HWND hwndList, INPUT_LIST_NODE *pInputNode)
{ {
INT ItemIndex = -1; INT ItemIndex, ImageIndex = -1;
INT ImageIndex = -1;
LV_ITEM item; LV_ITEM item;
HIMAGELIST hImageList; HIMAGELIST hImageList = ListView_GetImageList(hwndList, LVSIL_SMALL);
hImageList = ListView_GetImageList(hwndList, LVSIL_SMALL);
if (hImageList != NULL) if (hImageList != NULL)
{ {
@ -136,7 +149,6 @@ AddToInputListView(HWND hwndList, INPUT_LIST_NODE *pInputNode)
hLayoutIcon = CreateLayoutIcon(pInputNode->pszIndicator, hLayoutIcon = CreateLayoutIcon(pInputNode->pszIndicator,
(pInputNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)); (pInputNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT));
if (hLayoutIcon != NULL) if (hLayoutIcon != NULL)
{ {
ImageIndex = ImageList_AddIcon(hImageList, hLayoutIcon); ImageIndex = ImageList_AddIcon(hImageList, hLayoutIcon);
@ -145,13 +157,11 @@ AddToInputListView(HWND hwndList, INPUT_LIST_NODE *pInputNode)
} }
ZeroMemory(&item, sizeof(item)); ZeroMemory(&item, sizeof(item));
item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE; item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
item.pszText = pInputNode->pLocale->pszName; item.pszText = pInputNode->pLocale->pszName;
item.iItem = ListView_GetItemCount(hwndList) + 1; item.iItem = ListView_GetItemCount(hwndList);
item.lParam = (LPARAM)pInputNode; item.lParam = (LPARAM)pInputNode;
item.iImage = ImageIndex; item.iImage = ImageIndex;
ItemIndex = ListView_InsertItem(hwndList, &item); ItemIndex = ListView_InsertItem(hwndList, &item);
ListView_SetItemText(hwndList, ItemIndex, 1, pInputNode->pLayout->pszName); ListView_SetItemText(hwndList, ItemIndex, 1, pInputNode->pLayout->pszName);
@ -161,52 +171,55 @@ AddToInputListView(HWND hwndList, INPUT_LIST_NODE *pInputNode)
static VOID static VOID
UpdateInputListView(HWND hwndList) UpdateInputListView(HWND hwndList)
{ {
INPUT_LIST_NODE *pCurrentInputNode; INPUT_LIST_NODE *pNode;
HIMAGELIST hImageList; HIMAGELIST hImageList = ListView_GetImageList(hwndList, LVSIL_SMALL);
INT iSelected = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
hImageList = ListView_GetImageList(hwndList, LVSIL_SMALL); if (hImageList)
if (hImageList != NULL)
{ {
ImageList_RemoveAll(hImageList); ImageList_RemoveAll(hImageList);
} }
ListView_DeleteAllItems(hwndList); ListView_DeleteAllItems(hwndList);
for (pCurrentInputNode = InputList_GetFirst(); for (pNode = InputList_GetFirst(); pNode != NULL; pNode = pNode->pNext)
pCurrentInputNode != NULL;
pCurrentInputNode = pCurrentInputNode->pNext)
{ {
if (!(pCurrentInputNode->wFlags & INPUT_LIST_NODE_FLAG_DELETED)) if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
{ continue;
AddToInputListView(hwndList, pCurrentInputNode);
} AddToInputListView(hwndList, pNode);
} }
if (iSelected != -1)
{
INT nCount = ListView_GetItemCount(hwndList);
LV_ITEM item = { LVIF_STATE };
item.state = item.stateMask = LVIS_SELECTED;
item.iItem = ((nCount == iSelected) ? nCount - 1 : iSelected);
ListView_SetItem(hwndList, &item);
}
InvalidateRect(hwndList, NULL, TRUE);
} }
static VOID static VOID
OnInitSettingsPage(HWND hwndDlg) OnInitSettingsPage(HWND hwndDlg)
{ {
HWND hwndInputList; HWND hwndInputList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
LayoutList_Create(); LayoutList_Create();
LocaleList_Create(); LocaleList_Create();
InputList_Create(); InputList_Create();
hwndInputList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
if (hwndInputList != NULL) if (hwndInputList != NULL)
{ {
WCHAR szBuffer[MAX_STR_LEN]; WCHAR szBuffer[MAX_STR_LEN];
HIMAGELIST hLayoutImageList; HIMAGELIST hLayoutImageList;
LV_COLUMN column; LV_COLUMN column = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM };
ListView_SetExtendedListViewStyle(hwndInputList, LVS_EX_FULLROWSELECT); ListView_SetExtendedListViewStyle(hwndInputList, LVS_EX_FULLROWSELECT);
ZeroMemory(&column, sizeof(column));
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
LoadStringW(hApplet, IDS_LANGUAGE, szBuffer, ARRAYSIZE(szBuffer)); LoadStringW(hApplet, IDS_LANGUAGE, szBuffer, ARRAYSIZE(szBuffer));
column.fmt = LVCFMT_LEFT; column.fmt = LVCFMT_LEFT;
column.iSubItem = 0; column.iSubItem = 0;
@ -233,7 +246,7 @@ OnInitSettingsPage(HWND hwndDlg)
UpdateInputListView(hwndInputList); UpdateInputListView(hwndInputList);
} }
SetControlsState(hwndDlg, FALSE); SetControlsState(hwndDlg);
} }
@ -259,6 +272,7 @@ OnCommandSettingsPage(HWND hwndDlg, WPARAM wParam)
AddDialogProc) == IDOK) AddDialogProc) == IDOK)
{ {
UpdateInputListView(GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST)); UpdateInputListView(GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST));
SetControlsState(hwndDlg);
PropSheet_Changed(GetParent(hwndDlg), hwndDlg); PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
} }
} }
@ -266,21 +280,17 @@ OnCommandSettingsPage(HWND hwndDlg, WPARAM wParam)
case IDC_REMOVE_BUTTON: case IDC_REMOVE_BUTTON:
{ {
HWND hwndList; HWND hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
if (hwndList)
hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
if (hwndList != NULL)
{ {
LVITEM item = { 0 }; LVITEM item = { LVIF_PARAM };
item.mask = LVIF_PARAM;
item.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED); item.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
if (ListView_GetItem(hwndList, &item) != FALSE) if (ListView_GetItem(hwndList, &item))
{ {
InputList_Remove((INPUT_LIST_NODE*) item.lParam); InputList_Remove((INPUT_LIST_NODE*) item.lParam);
UpdateInputListView(hwndList); UpdateInputListView(hwndList);
SetControlsState(hwndDlg);
PropSheet_Changed(GetParent(hwndDlg), hwndDlg); PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
} }
} }
@ -289,18 +299,13 @@ OnCommandSettingsPage(HWND hwndDlg, WPARAM wParam)
case IDC_PROP_BUTTON: case IDC_PROP_BUTTON:
{ {
HWND hwndList; HWND hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
if (hwndList)
hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
if (hwndList != NULL)
{ {
LVITEM item = { 0 }; LVITEM item = { LVIF_PARAM };
item.mask = LVIF_PARAM;
item.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED); item.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
if (ListView_GetItem(hwndList, &item) != FALSE) if (ListView_GetItem(hwndList, &item))
{ {
if (DialogBoxParamW(hApplet, if (DialogBoxParamW(hApplet,
MAKEINTRESOURCEW(IDD_INPUT_LANG_PROP), MAKEINTRESOURCEW(IDD_INPUT_LANG_PROP),
@ -309,6 +314,7 @@ OnCommandSettingsPage(HWND hwndDlg, WPARAM wParam)
item.lParam) == IDOK) item.lParam) == IDOK)
{ {
UpdateInputListView(hwndList); UpdateInputListView(hwndList);
SetControlsState(hwndDlg);
PropSheet_Changed(GetParent(hwndDlg), hwndDlg); PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
} }
} }
@ -318,21 +324,17 @@ OnCommandSettingsPage(HWND hwndDlg, WPARAM wParam)
case IDC_SET_DEFAULT: case IDC_SET_DEFAULT:
{ {
HWND hwndList; HWND hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
if (hwndList)
hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
if (hwndList != NULL)
{ {
LVITEM item = { 0 }; LVITEM item = { LVIF_PARAM };
item.mask = LVIF_PARAM;
item.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED); item.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
if (ListView_GetItem(hwndList, &item) != FALSE) if (ListView_GetItem(hwndList, &item))
{ {
InputList_SetDefault((INPUT_LIST_NODE*) item.lParam); InputList_SetDefault((INPUT_LIST_NODE*) item.lParam);
UpdateInputListView(hwndList); UpdateInputListView(hwndList);
SetControlsState(hwndDlg);
PropSheet_Changed(GetParent(hwndDlg), hwndDlg); PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
} }
} }
@ -350,73 +352,18 @@ OnCommandSettingsPage(HWND hwndDlg, WPARAM wParam)
} }
} }
BOOL EnableProcessPrivileges(LPCWSTR lpPrivilegeName, BOOL bEnable)
{
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tokenPrivileges;
BOOL Ret;
Ret = OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken);
if (!Ret)
return Ret; // failure
Ret = LookupPrivilegeValueW(NULL, lpPrivilegeName, &luid);
if (Ret)
{
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Privileges[0].Luid = luid;
tokenPrivileges.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
Ret = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, 0, 0);
}
CloseHandle(hToken);
return Ret;
}
static VOID static VOID
OnNotifySettingsPage(HWND hwndDlg, LPARAM lParam) OnNotifySettingsPage(HWND hwndDlg, LPARAM lParam)
{ {
LPNMHDR header; LPNMHDR header = (LPNMHDR)lParam;
header = (LPNMHDR)lParam;
switch (header->code) switch (header->code)
{ {
case NM_CLICK: case LVN_ITEMCHANGED:
{ {
if (header->idFrom == IDC_KEYLAYOUT_LIST) if (header->idFrom == IDC_KEYLAYOUT_LIST)
{ {
INT iSelected = ListView_GetNextItem(header->hwndFrom, -1, LVNI_SELECTED); SetControlsState(hwndDlg);
if (iSelected != -1)
{
LVITEM item = { 0 };
SetControlsState(hwndDlg, TRUE);
item.mask = LVIF_PARAM;
item.iItem = iSelected;
if (ListView_GetItem(header->hwndFrom, &item) != FALSE)
{
INPUT_LIST_NODE *pInput;
pInput = (INPUT_LIST_NODE*) item.lParam;
if (pInput != NULL && pInput->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
{
EnableWindow(GetDlgItem(hwndDlg, IDC_SET_DEFAULT), FALSE);
}
}
}
else
{
SetControlsState(hwndDlg, FALSE);
}
} }
} }
break; break;
@ -424,20 +371,7 @@ OnNotifySettingsPage(HWND hwndDlg, LPARAM lParam)
case PSN_APPLY: case PSN_APPLY:
{ {
/* Write Input Methods list to registry */ /* Write Input Methods list to registry */
if (InputList_Process()) InputList_Process();
{
/* Needs reboot */
WCHAR szNeedsReboot[128], szLanguage[64];
LoadStringW(hApplet, IDS_REBOOT_NOW, szNeedsReboot, _countof(szNeedsReboot));
LoadStringW(hApplet, IDS_LANGUAGE, szLanguage, _countof(szLanguage));
if (MessageBoxW(hwndDlg, szNeedsReboot, szLanguage,
MB_ICONINFORMATION | MB_YESNOCANCEL) == IDYES)
{
EnableProcessPrivileges(SE_SHUTDOWN_NAME, TRUE);
ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);
}
}
} }
break; break;
} }

View file

@ -706,6 +706,13 @@ inline BOOL IsValidKLID(_In_ LPCWSTR pwszKLID)
return (pwszKLID != NULL) && (wcsspn(pwszKLID, L"0123456789ABCDEFabcdef") == (KL_NAMELENGTH - 1)); return (pwszKLID != NULL) && (wcsspn(pwszKLID, L"0123456789ABCDEFabcdef") == (KL_NAMELENGTH - 1));
} }
VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName)
{
WCHAR szSysDir[MAX_PATH];
GetSystemDirectoryW(szSysDir, _countof(szSysDir));
StringCchPrintfW(pszPath, cchPath, L"%s\\%s", szSysDir, pszFileName);
}
#define ENGLISH_US MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) #define ENGLISH_US MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
/* /*
@ -722,15 +729,15 @@ IntLoadKeyboardLayout(
_In_ UINT Flags, _In_ UINT Flags,
_In_ BOOL unknown5) _In_ BOOL unknown5)
{ {
DWORD dwhkl, dwType, dwSize; DWORD dwKLID, dwHKL, dwType, dwSize;
UNICODE_STRING ustrKbdName; UNICODE_STRING ustrKbdName;
UNICODE_STRING ustrKLID; UNICODE_STRING ustrKLID;
WCHAR wszRegKey[256] = L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"; WCHAR wszRegKey[256] = L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
WCHAR wszLayoutId[10], wszNewKLID[KL_NAMELENGTH], szImeFileName[80]; WCHAR wszLayoutId[10], wszNewKLID[KL_NAMELENGTH], szImeFileName[80];
PWCHAR endptr;
HKL hNewKL; HKL hNewKL;
HKEY hKey; HKEY hKey;
BOOL bIsIME; BOOL bIsIME;
WORD wLow, wHigh;
if (!IsValidKLID(pwszKLID)) if (!IsValidKLID(pwszKLID))
{ {
@ -738,13 +745,11 @@ IntLoadKeyboardLayout(
return UlongToHandle(MAKELONG(ENGLISH_US, ENGLISH_US)); return UlongToHandle(MAKELONG(ENGLISH_US, ENGLISH_US));
} }
dwhkl = wcstoul(pwszKLID, &endptr, 16); dwKLID = wcstoul(pwszKLID, NULL, 16);
bIsIME = IS_IME_HKL(UlongToHandle(dwKLID));
bIsIME = IS_IME_HKL(UlongToHandle(dwhkl)); wLow = LOWORD(dwKLID);
if (!bIsIME) /* Not IME? */ wHigh = HIWORD(dwKLID);
{
dwhkl = LOWORD(dwhkl); /* LOWORD of dwhkl is language identifier */
}
if (Flags & KLF_SUBSTITUTE_OK) if (Flags & KLF_SUBSTITUTE_OK)
{ {
@ -753,10 +758,14 @@ IntLoadKeyboardLayout(
KEY_READ, &hKey) == ERROR_SUCCESS) KEY_READ, &hKey) == ERROR_SUCCESS)
{ {
dwSize = sizeof(wszNewKLID); dwSize = sizeof(wszNewKLID);
if (RegQueryValueExW(hKey, pwszKLID, NULL, &dwType, (LPBYTE)wszNewKLID, &dwSize) == ERROR_SUCCESS) if (RegQueryValueExW(hKey, pwszKLID, NULL, &dwType, (LPBYTE)wszNewKLID,
&dwSize) == ERROR_SUCCESS &&
dwType == REG_SZ)
{ {
/* Use new KLID value */ /* Use new KLID value */
pwszKLID = wszNewKLID; pwszKLID = wszNewKLID;
dwKLID = wcstoul(pwszKLID, NULL, 16);
wHigh = LOWORD(dwKLID);
} }
/* Close the key now */ /* Close the key now */
@ -771,14 +780,11 @@ IntLoadKeyboardLayout(
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS) if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{ {
dwSize = sizeof(wszLayoutId); dwSize = sizeof(wszLayoutId);
if (RegQueryValueExW(hKey, L"Layout Id", NULL, &dwType, (LPBYTE)wszLayoutId, &dwSize) == ERROR_SUCCESS) if (RegQueryValueExW(hKey, L"Layout Id", NULL, &dwType, (LPBYTE)wszLayoutId,
&dwSize) == ERROR_SUCCESS && dwType == REG_SZ)
{ {
/* If Layout Id is specified, use this value | f000 as HIWORD */ /* If Layout Id is specified, use this value | f000 as HIWORD */
/* FIXME: Microsoft Office expects this value to be something specific wHigh = (0xF000 | wcstoul(wszLayoutId, NULL, 16));
* for Japanese and Korean Windows with an IME the value is 0xe001
*/
if (!bIsIME)
dwhkl |= (0xf000 | wcstol(wszLayoutId, NULL, 16)) << 16;
} }
if (bIsIME) if (bIsIME)
@ -788,9 +794,18 @@ IntLoadKeyboardLayout(
if (RegQueryValueExW(hKey, L"IME File", NULL, &dwType, (LPBYTE)szImeFileName, if (RegQueryValueExW(hKey, L"IME File", NULL, &dwType, (LPBYTE)szImeFileName,
&dwSize) != ERROR_SUCCESS) &dwSize) != ERROR_SUCCESS)
{ {
FIXME("Check IME file existence in system32\n");
bIsIME = FALSE; bIsIME = FALSE;
dwhkl = LOWORD(dwhkl); wHigh = 0;
}
else
{
WCHAR szPath[MAX_PATH];
GetSystemLibraryPath(szPath, _countof(szPath), szImeFileName);
if (GetFileAttributesW(szPath) == INVALID_FILE_ATTRIBUTES) /* Does not exist? */
{
bIsIME = FALSE;
wHigh = 0;
}
} }
} }
@ -803,13 +818,14 @@ IntLoadKeyboardLayout(
return NULL; return NULL;
} }
/* If Layout Id is not given HIWORD == LOWORD (for dwhkl) */ if (wHigh == 0)
if (!HIWORD(dwhkl)) wHigh = wLow;
dwhkl |= dwhkl << 16;
dwHKL = MAKELONG(wLow, wHigh);
ZeroMemory(&ustrKbdName, sizeof(ustrKbdName)); ZeroMemory(&ustrKbdName, sizeof(ustrKbdName));
RtlInitUnicodeString(&ustrKLID, pwszKLID); RtlInitUnicodeString(&ustrKLID, pwszKLID);
hNewKL = NtUserLoadKeyboardLayoutEx(NULL, 0, &ustrKbdName, NULL, &ustrKLID, dwhkl, Flags); hNewKL = NtUserLoadKeyboardLayoutEx(NULL, 0, &ustrKbdName, NULL, &ustrKLID, dwHKL, Flags);
CliImmInitializeHotKeys(SETIMEHOTKEY_ADD, hNewKL); CliImmInitializeHotKeys(SETIMEHOTKEY_ADD, hNewKL);
return hNewKL; return hNewKL;
} }