Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689/* * PROJECT: ReactOS CTF * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Multi-language handling of Cicero * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
#include "precomp.h"
#include <shellapi.h>#include <imm.h>#include <imm32_undoc.h>#include <shlobj.h>#include <shlwapi.h>#include <shlwapi_undoc.h>#include <strsafe.h>#include <assert.h>
#include "mlng.h"
#include <wine/debug.h>WINE_DEFAULT_DEBUG_CHANNEL(msctf);
extern CRITICAL_SECTION g_cs;
CicArray<MLNGINFO> *g_pMlngInfo = NULL;INT CStaticIconList::s_cx = 0;INT CStaticIconList::s_cy = 0;CStaticIconList g_IconList;
// Cache for GetSpecialKLIDstatic HKL s_hCacheKL = NULL;static DWORD s_dwCacheKLID = 0;
/*********************************************************************** * The helper funtions */
/// @implementedDWORD GetSpecialKLID(_In_ HKL hKL){ assert(IS_SPECIAL_HKL(hKL));
if (s_hCacheKL == hKL && s_dwCacheKLID != 0) return s_dwCacheKLID;
s_dwCacheKLID = 0;
CicRegKey regKey1; LSTATUS error = regKey1.Open(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts"); if (error != ERROR_SUCCESS) return 0;
WCHAR szName[16], szLayoutId[16]; const DWORD dwSpecialId = SPECIALIDFROMHKL(hKL); for (DWORD dwIndex = 0; ; ++dwIndex) { error = ::RegEnumKeyW(regKey1, dwIndex, szName, _countof(szName)); szName[_countof(szName) - 1] = UNICODE_NULL; // Avoid buffer overrun if (error != ERROR_SUCCESS) break;
CicRegKey regKey2; error = regKey2.Open(regKey1, szName); if (error != ERROR_SUCCESS) break;
error = regKey2.QuerySz(L"Layout Id", szLayoutId, _countof(szLayoutId)); szLayoutId[_countof(szLayoutId) - 1] = UNICODE_NULL; // Avoid buffer overrun if (error == ERROR_SUCCESS) continue;
DWORD dwLayoutId = wcstoul(szLayoutId, NULL, 16); if (dwLayoutId == dwSpecialId) { s_hCacheKL = hKL; s_dwCacheKLID = wcstoul(szName, NULL, 16); break; } }
return s_dwCacheKLID;}
/// @implementedDWORD GetHKLSubstitute(_In_ HKL hKL){ if (IS_IME_HKL(hKL)) return HandleToUlong(hKL);
DWORD dwKLID; if (HIWORD(hKL) == LOWORD(hKL)) dwKLID = LOWORD(hKL); else if (IS_SPECIAL_HKL(hKL)) dwKLID = GetSpecialKLID(hKL); else dwKLID = HandleToUlong(hKL);
if (dwKLID == 0) return HandleToUlong(hKL);
CicRegKey regKey; LSTATUS error = regKey.Open(HKEY_CURRENT_USER, L"Keyboard Layout\\Substitutes"); if (error == ERROR_SUCCESS) { WCHAR szName[MAX_PATH], szValue[MAX_PATH]; DWORD dwIndex, dwValue; for (dwIndex = 0; ; ++dwIndex) { error = regKey.EnumValue(dwIndex, szName, _countof(szName)); szName[_countof(szName) - 1] = UNICODE_NULL; // Avoid buffer overrun if (error != ERROR_SUCCESS) break;
error = regKey.QuerySz(szName, szValue, _countof(szValue)); szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun if (error != ERROR_SUCCESS) break;
dwValue = wcstoul(szValue, NULL, 16); if ((dwKLID & ~SPECIAL_MASK) == dwValue) { dwKLID = wcstoul(szName, NULL, 16); break; } } }
return dwKLID;}
/// @implementedstatic BOOLGetKbdLayoutNameFromReg(_In_ HKL hKL, _Out_ LPWSTR pszDesc, _In_ UINT cchDesc){ const DWORD dwKLID = GetHKLSubstitute(hKL);
WCHAR szSubKey[MAX_PATH]; StringCchPrintfW(szSubKey, _countof(szSubKey), L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%08lX", dwKLID);
CicRegKey regKey; LSTATUS error = regKey.Open(HKEY_LOCAL_MACHINE, szSubKey); if (error != ERROR_SUCCESS) return FALSE;
if (SHLoadRegUIStringW(regKey, L"Layout Display Name", pszDesc, cchDesc) == S_OK) { pszDesc[cchDesc - 1] = UNICODE_NULL; // Avoid buffer overrun return TRUE; }
error = regKey.QuerySz(L"Layout Text", pszDesc, cchDesc); pszDesc[cchDesc - 1] = UNICODE_NULL; // Avoid buffer overrun return (error == ERROR_SUCCESS);}
/// @implementedstatic BOOLGetHKLName(_In_ HKL hKL, _Out_ LPWSTR pszDesc, _In_ UINT cchDesc){ if (::GetLocaleInfoW(LOWORD(hKL), LOCALE_SLANGUAGE, pszDesc, cchDesc)) return TRUE;
*pszDesc = UNICODE_NULL;
if (LOWORD(hKL) == HIWORD(hKL)) return FALSE;
return GetKbdLayoutNameFromReg(hKL, pszDesc, cchDesc);}
/// @implementedstatic BOOLGetHKLDesctription( _In_ HKL hKL, _Out_ LPWSTR pszDesc, _In_ UINT cchDesc, _Out_ LPWSTR pszImeFileName, _In_ UINT cchImeFileName){ pszDesc[0] = pszImeFileName[0] = UNICODE_NULL;
if (!IS_IME_HKL(hKL)) return GetHKLName(hKL, pszDesc, cchDesc);
if (GetKbdLayoutNameFromReg(hKL, pszDesc, cchDesc)) return TRUE;
if (!::ImmGetDescriptionW(hKL, pszDesc, cchDesc)) { *pszDesc = UNICODE_NULL; return GetHKLName(hKL, pszDesc, cchDesc); }
if (!::ImmGetIMEFileNameW(hKL, pszImeFileName, cchImeFileName)) *pszImeFileName = UNICODE_NULL;
return TRUE;}
/// @implementedHICON GetIconFromFile(_In_ INT cx, _In_ INT cy, _In_ LPCWSTR pszFileName, _In_ INT iIcon){ HICON hIcon;
if (cx <= GetSystemMetrics(SM_CXSMICON)) ::ExtractIconExW(pszFileName, iIcon, NULL, &hIcon, 1); else ::ExtractIconExW(pszFileName, iIcon, &hIcon, NULL, 1);
return hIcon;}
/// @implementedstatic BOOL EnsureIconImageList(VOID){ if (!CStaticIconList::s_cx) g_IconList.Init(::GetSystemMetrics(SM_CYSMICON), ::GetSystemMetrics(SM_CXSMICON));
return TRUE;}
/// @implementedstatic INT GetPhysicalFontHeight(LOGFONTW *plf){ HDC hDC = ::GetDC(NULL); HFONT hFont = ::CreateFontIndirectW(plf); HGDIOBJ hFontOld = ::SelectObject(hDC, hFont); TEXTMETRICW tm; ::GetTextMetricsW(hDC, &tm); INT ret = tm.tmExternalLeading + tm.tmHeight; ::SelectObject(hDC, hFontOld); ::DeleteObject(hFont); ::ReleaseDC(NULL, hDC); return ret;}
/*********************************************************************** * Inat helper functions */
/// @implementedINT InatAddIcon(_In_ HICON hIcon){ if (!EnsureIconImageList()) return -1; return g_IconList.AddIcon(hIcon);}
/// @implementedHICONInatCreateIconBySize( _In_ LANGID LangID, _In_ INT nWidth, _In_ INT nHeight, _In_ const LOGFONTW *plf){ WCHAR szText[64]; BOOL ret = ::GetLocaleInfoW(LangID, LOCALE_NOUSEROVERRIDE | LOCALE_SABBREVLANGNAME, szText, _countof(szText)); if (!ret) szText[0] = szText[1] = L'?';
szText[2] = UNICODE_NULL; CharUpperW(szText);
HFONT hFont = ::CreateFontIndirectW(plf); if (!hFont) return NULL;
HDC hDC = ::GetDC(NULL); HDC hMemDC = ::CreateCompatibleDC(hDC); HBITMAP hbmColor = ::CreateCompatibleBitmap(hDC, nWidth, nHeight); HBITMAP hbmMask = ::CreateBitmap(nWidth, nHeight, 1, 1, NULL); ::ReleaseDC(NULL, hDC);
HICON hIcon = NULL; HGDIOBJ hbmOld = ::SelectObject(hMemDC, hbmColor); HGDIOBJ hFontOld = ::SelectObject(hMemDC, hFont); if (hMemDC && hbmColor && hbmMask) { ::SetBkColor(hMemDC, ::GetSysColor(COLOR_HIGHLIGHT)); ::SetTextColor(hMemDC, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
RECT rc = { 0, 0, nWidth, nHeight }; ::ExtTextOutW(hMemDC, 0, 0, ETO_OPAQUE, &rc, L"", 0, NULL);
::DrawTextW(hMemDC, szText, 2, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER); ::SelectObject(hMemDC, hbmMask);
::PatBlt(hMemDC, 0, 0, nWidth, nHeight, BLACKNESS);
ICONINFO IconInfo = { TRUE, 0, 0, hbmMask, hbmColor }; hIcon = ::CreateIconIndirect(&IconInfo); } ::SelectObject(hMemDC, hFontOld); ::SelectObject(hMemDC, hbmOld);
::DeleteObject(hbmMask); ::DeleteObject(hbmColor); ::DeleteDC(hMemDC); ::DeleteObject(hFont); return hIcon;}
/// @implementedHICON InatCreateIcon(_In_ LANGID LangID){ INT cxSmIcon = ::GetSystemMetrics(SM_CXSMICON), cySmIcon = ::GetSystemMetrics(SM_CYSMICON);
LOGFONTW lf; if (!SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(LOGFONTW), &lf, 0)) return NULL;
if (cySmIcon < GetPhysicalFontHeight(&lf)) { lf.lfWidth = 0; lf.lfHeight = - (7 * cySmIcon) / 10; }
return InatCreateIconBySize(LangID, cxSmIcon, cySmIcon, &lf);}
/// @implementedBOOL InatGetIconSize(_Out_ INT *pcx, _Out_ INT *pcy){ g_IconList.GetIconSize(pcx, pcy); return TRUE;}
/// @implementedINT InatGetImageCount(VOID){ return g_IconList.GetImageCount();}
/// @implementedVOID InatRemoveAll(VOID){ if (CStaticIconList::s_cx) g_IconList.RemoveAll(FALSE);}
/// @implementedVOID UninitINAT(VOID){ g_IconList.RemoveAll(TRUE);
if (g_pMlngInfo) { delete g_pMlngInfo; g_pMlngInfo = NULL; }}
/*********************************************************************** * MLNGINFO */
/// @implementedvoid MLNGINFO::InitDesc(){ if (m_bInitDesc) return;
WCHAR szDesc[MAX_PATH], szImeFileName[MAX_PATH]; GetHKLDesctription(m_hKL, szDesc, (UINT)_countof(szDesc), szImeFileName, (UINT)_countof(szImeFileName)); SetDesc(szDesc); m_bInitDesc = TRUE;}
/// @implementedvoid MLNGINFO::InitIcon(){ if (m_bInitIcon) return;
WCHAR szDesc[MAX_PATH], szImeFileName[MAX_PATH]; GetHKLDesctription(m_hKL, szDesc, (UINT)_countof(szDesc), szImeFileName, (UINT)_countof(szImeFileName)); SetDesc(szDesc); m_bInitDesc = TRUE;
INT cxIcon, cyIcon; InatGetIconSize(&cxIcon, &cyIcon);
HICON hIcon = NULL; if (szImeFileName[0]) hIcon = GetIconFromFile(cxIcon, cyIcon, szImeFileName, 0);
if (!hIcon) hIcon = InatCreateIcon(LOWORD(m_hKL));
if (hIcon) { m_iIconIndex = InatAddIcon(hIcon); ::DestroyIcon(hIcon); }
m_bInitIcon = TRUE;}
/// @implementedLPCWSTR MLNGINFO::GetDesc(){ if (!m_bInitDesc) InitDesc();
return m_szDesc;}
/// @implementedvoid MLNGINFO::SetDesc(LPCWSTR pszDesc){ StringCchCopyW(m_szDesc, _countof(m_szDesc), pszDesc);}
/// @implementedINT MLNGINFO::GetIconIndex(){ if (!m_bInitIcon) InitIcon();
return m_iIconIndex;}
/*********************************************************************** * CStaticIconList */
/// @implementedvoid CStaticIconList::Init(INT cxIcon, INT cyIcon){ ::EnterCriticalSection(&g_cs); s_cx = cxIcon; s_cy = cyIcon; ::LeaveCriticalSection(&g_cs);}
/// @implementedINT CStaticIconList::AddIcon(HICON hIcon){ ::EnterCriticalSection(&g_cs);
INT iItem = -1; HICON hCopyIcon = ::CopyIcon(hIcon); if (hCopyIcon) { if (g_IconList.Add(hIcon)) iItem = INT(g_IconList.size() - 1); }
::LeaveCriticalSection(&g_cs); return iItem;}
/// @implementedHICON CStaticIconList::ExtractIcon(INT iIcon){ HICON hCopyIcon = NULL; ::EnterCriticalSection(&g_cs); if (iIcon <= (INT)g_IconList.size()) hCopyIcon = ::CopyIcon(g_IconList[iIcon]); ::LeaveCriticalSection(&g_cs); return hCopyIcon;}
/// @implementedvoid CStaticIconList::GetIconSize(INT *pcx, INT *pcy){ ::EnterCriticalSection(&g_cs); *pcx = s_cx; *pcy = s_cy; ::LeaveCriticalSection(&g_cs);}
/// @implementedINT CStaticIconList::GetImageCount(){ ::EnterCriticalSection(&g_cs); INT cItems = (INT)g_IconList.size(); ::LeaveCriticalSection(&g_cs); return cItems;}
/// @implementedvoid CStaticIconList::RemoveAll(BOOL bNoLock){ if (!bNoLock) ::EnterCriticalSection(&g_cs);
for (size_t iItem = 0; iItem < g_IconList.size(); ++iItem) { ::DestroyIcon(g_IconList[iItem]); }
clear();
if (!bNoLock) ::LeaveCriticalSection(&g_cs);}
/// @implementedstatic BOOL CheckMlngInfo(VOID){ if (!g_pMlngInfo) return TRUE; // Needs creation
INT cKLs = ::GetKeyboardLayoutList(0, NULL); if (cKLs != TF_MlngInfoCount()) return TRUE; // Needs refresh
if (!cKLs) return FALSE;
HKL *phKLs = (HKL*)cicMemAlloc(cKLs * sizeof(HKL)); if (!phKLs) return FALSE;
::GetKeyboardLayoutList(cKLs, phKLs);
assert(g_pMlngInfo);
BOOL ret = FALSE; for (INT iKL = 0; iKL < cKLs; ++iKL) { if ((*g_pMlngInfo)[iKL].m_hKL != phKLs[iKL]) { ret = TRUE; // Needs refresh break; } }
cicMemFree(phKLs); return ret;}
/// @implementedstatic VOID DestroyMlngInfo(VOID){ if (!g_pMlngInfo) return;
delete g_pMlngInfo; g_pMlngInfo = NULL;}
/// @implementedstatic VOID CreateMlngInfo(VOID){ if (!g_pMlngInfo) { g_pMlngInfo = new(cicNoThrow) CicArray<MLNGINFO>(); if (!g_pMlngInfo) return; }
if (!EnsureIconImageList()) return;
INT cKLs = ::GetKeyboardLayoutList(0, NULL); HKL *phKLs = (HKL*)cicMemAllocClear(cKLs * sizeof(HKL)); if (!phKLs) return;
::GetKeyboardLayoutList(cKLs, phKLs);
for (INT iKL = 0; iKL < cKLs; ++iKL) { MLNGINFO& info = (*g_pMlngInfo)[iKL]; info.m_hKL = phKLs[iKL]; info.m_bInitDesc = FALSE; info.m_bInitIcon = FALSE; }
cicMemFree(phKLs);}
/*********************************************************************** * TF_InitMlngInfo (MSCTF.@) * * @implemented */EXTERN_C VOID WINAPI TF_InitMlngInfo(VOID){ TRACE("()\n");
::EnterCriticalSection(&g_cs);
if (CheckMlngInfo()) { DestroyMlngInfo(); CreateMlngInfo(); }
::LeaveCriticalSection(&g_cs);}
/*********************************************************************** * TF_MlngInfoCount (MSCTF.@) * * @implemented */EXTERN_C INT WINAPI TF_MlngInfoCount(VOID){ TRACE("()\n");
if (!g_pMlngInfo) return 0;
return (INT)g_pMlngInfo->size();}
/*********************************************************************** * TF_InatExtractIcon (MSCTF.@) * * @implemented */EXTERN_C HICON WINAPI TF_InatExtractIcon(_In_ INT iKL){ TRACE("(%d)\n", iKL); return g_IconList.ExtractIcon(iKL);}
/*********************************************************************** * TF_GetMlngIconIndex (MSCTF.@) * * @implemented */EXTERN_C INT WINAPI TF_GetMlngIconIndex(_In_ INT iKL){ TRACE("(%d)\n", iKL);
INT iIcon = -1;
::EnterCriticalSection(&g_cs);
assert(g_pMlngInfo);
if (iKL < (INT)g_pMlngInfo->size()) iIcon = (*g_pMlngInfo)[iKL].GetIconIndex();
::LeaveCriticalSection(&g_cs);
return iIcon;}
/*********************************************************************** * TF_GetMlngHKL (MSCTF.@) * * @implemented */EXTERN_C BOOL WINAPITF_GetMlngHKL( _In_ INT iKL, _Out_opt_ HKL *phKL, _Out_opt_ LPWSTR pszDesc, _In_ INT cchDesc){ TRACE("(%d, %p, %p, %d)\n", iKL, phKL, pszDesc, cchDesc);
BOOL ret = FALSE;
::EnterCriticalSection(&g_cs);
assert(g_pMlngInfo);
if (iKL < (INT)g_pMlngInfo->size()) { MLNGINFO& info = (*g_pMlngInfo)[iKL];
if (phKL) *phKL = info.m_hKL;
if (pszDesc) StringCchCopyW(pszDesc, cchDesc, info.GetDesc());
ret = TRUE; }
::LeaveCriticalSection(&g_cs);
return ret;}