1 /* 2 * PROJECT: ReactOS IMM32 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: Implementing IMM32 candidate lists 5 * COPYRIGHT: Copyright 2020-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8 #include "precomp.h" 9 10 WINE_DEFAULT_DEBUG_CHANNEL(imm); 11 12 DWORD APIENTRY 13 CandidateListWideToAnsi(const CANDIDATELIST *pWideCL, LPCANDIDATELIST pAnsiCL, DWORD dwBufLen, 14 UINT uCodePage) 15 { 16 BOOL bUsedDefault; 17 DWORD dwSize, dwIndex, cbGot, cbLeft; 18 const BYTE *pbWide; 19 LPBYTE pbAnsi; 20 LPDWORD pibOffsets; 21 22 /* calculate total ansi size */ 23 if (pWideCL->dwCount > 0) 24 { 25 dwSize = sizeof(CANDIDATELIST) + ((pWideCL->dwCount - 1) * sizeof(DWORD)); 26 for (dwIndex = 0; dwIndex < pWideCL->dwCount; ++dwIndex) 27 { 28 pbWide = (const BYTE *)pWideCL + pWideCL->dwOffset[dwIndex]; 29 cbGot = WideCharToMultiByte(uCodePage, 0, (LPCWSTR)pbWide, -1, NULL, 0, 30 NULL, &bUsedDefault); 31 dwSize += cbGot; 32 } 33 } 34 else 35 { 36 dwSize = sizeof(CANDIDATELIST); 37 } 38 39 dwSize = ROUNDUP4(dwSize); 40 if (dwBufLen == 0) 41 return dwSize; 42 if (dwBufLen < dwSize) 43 return 0; 44 45 /* store to ansi */ 46 pAnsiCL->dwSize = dwBufLen; 47 pAnsiCL->dwStyle = pWideCL->dwStyle; 48 pAnsiCL->dwCount = pWideCL->dwCount; 49 pAnsiCL->dwSelection = pWideCL->dwSelection; 50 pAnsiCL->dwPageStart = pWideCL->dwPageStart; 51 pAnsiCL->dwPageSize = pWideCL->dwPageSize; 52 53 pibOffsets = pAnsiCL->dwOffset; 54 if (pWideCL->dwCount > 0) 55 { 56 pibOffsets[0] = sizeof(CANDIDATELIST) + ((pWideCL->dwCount - 1) * sizeof(DWORD)); 57 cbLeft = dwBufLen - pibOffsets[0]; 58 59 for (dwIndex = 0; dwIndex < pWideCL->dwCount; ++dwIndex) 60 { 61 pbWide = (const BYTE *)pWideCL + pWideCL->dwOffset[dwIndex]; 62 pbAnsi = (LPBYTE)pAnsiCL + pibOffsets[dwIndex]; 63 64 /* convert to ansi */ 65 cbGot = WideCharToMultiByte(uCodePage, 0, (LPCWSTR)pbWide, -1, 66 (LPSTR)pbAnsi, cbLeft, NULL, &bUsedDefault); 67 cbLeft -= cbGot; 68 69 if (dwIndex < pWideCL->dwCount - 1) 70 pibOffsets[dwIndex + 1] = pibOffsets[dwIndex] + cbGot; 71 } 72 } 73 else 74 { 75 pibOffsets[0] = sizeof(CANDIDATELIST); 76 } 77 78 return dwBufLen; 79 } 80 81 DWORD APIENTRY 82 CandidateListAnsiToWide(const CANDIDATELIST *pAnsiCL, LPCANDIDATELIST pWideCL, DWORD dwBufLen, 83 UINT uCodePage) 84 { 85 DWORD dwSize, dwIndex, cchGot, cbGot, cbLeft; 86 const BYTE *pbAnsi; 87 LPBYTE pbWide; 88 LPDWORD pibOffsets; 89 90 /* calculate total wide size */ 91 if (pAnsiCL->dwCount > 0) 92 { 93 dwSize = sizeof(CANDIDATELIST) + ((pAnsiCL->dwCount - 1) * sizeof(DWORD)); 94 for (dwIndex = 0; dwIndex < pAnsiCL->dwCount; ++dwIndex) 95 { 96 pbAnsi = (const BYTE *)pAnsiCL + pAnsiCL->dwOffset[dwIndex]; 97 cchGot = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, (LPCSTR)pbAnsi, -1, NULL, 0); 98 dwSize += cchGot * sizeof(WCHAR); 99 } 100 } 101 else 102 { 103 dwSize = sizeof(CANDIDATELIST); 104 } 105 106 dwSize = ROUNDUP4(dwSize); 107 if (dwBufLen == 0) 108 return dwSize; 109 if (dwBufLen < dwSize) 110 return 0; 111 112 /* store to wide */ 113 pWideCL->dwSize = dwBufLen; 114 pWideCL->dwStyle = pAnsiCL->dwStyle; 115 pWideCL->dwCount = pAnsiCL->dwCount; 116 pWideCL->dwSelection = pAnsiCL->dwSelection; 117 pWideCL->dwPageStart = pAnsiCL->dwPageStart; 118 pWideCL->dwPageSize = pAnsiCL->dwPageSize; 119 120 pibOffsets = pWideCL->dwOffset; 121 if (pAnsiCL->dwCount > 0) 122 { 123 pibOffsets[0] = sizeof(CANDIDATELIST) + ((pWideCL->dwCount - 1) * sizeof(DWORD)); 124 cbLeft = dwBufLen - pibOffsets[0]; 125 126 for (dwIndex = 0; dwIndex < pAnsiCL->dwCount; ++dwIndex) 127 { 128 pbAnsi = (const BYTE *)pAnsiCL + pAnsiCL->dwOffset[dwIndex]; 129 pbWide = (LPBYTE)pWideCL + pibOffsets[dwIndex]; 130 131 /* convert to wide */ 132 cchGot = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, (LPCSTR)pbAnsi, -1, 133 (LPWSTR)pbWide, cbLeft / sizeof(WCHAR)); 134 cbGot = cchGot * sizeof(WCHAR); 135 cbLeft -= cbGot; 136 137 if (dwIndex + 1 < pAnsiCL->dwCount) 138 pibOffsets[dwIndex + 1] = pibOffsets[dwIndex] + cbGot; 139 } 140 } 141 else 142 { 143 pibOffsets[0] = sizeof(CANDIDATELIST); 144 } 145 146 return dwBufLen; 147 } 148 149 static DWORD APIENTRY 150 ImmGetCandidateListAW(HIMC hIMC, DWORD dwIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen, 151 BOOL bAnsi) 152 { 153 DWORD ret = 0; 154 LPINPUTCONTEXT pIC; 155 PCLIENTIMC pClientImc; 156 LPCANDIDATEINFO pCI; 157 LPCANDIDATELIST pCL; 158 DWORD dwSize; 159 160 pClientImc = ImmLockClientImc(hIMC); 161 if (!pClientImc) 162 return 0; 163 164 pIC = ImmLockIMC(hIMC); 165 if (pIC == NULL) 166 { 167 ImmUnlockClientImc(pClientImc); 168 return 0; 169 } 170 171 pCI = ImmLockIMCC(pIC->hCandInfo); 172 if (pCI == NULL) 173 { 174 ImmUnlockIMC(hIMC); 175 ImmUnlockClientImc(pClientImc); 176 return 0; 177 } 178 179 if (pCI->dwSize < sizeof(CANDIDATEINFO) || pCI->dwCount <= dwIndex) 180 goto Quit; 181 182 /* get required size */ 183 pCL = (LPCANDIDATELIST)((LPBYTE)pCI + pCI->dwOffset[dwIndex]); 184 if (bAnsi) 185 { 186 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 187 dwSize = CandidateListAnsiToWide(pCL, NULL, 0, CP_ACP); 188 else 189 dwSize = pCL->dwSize; 190 } 191 else 192 { 193 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 194 dwSize = pCL->dwSize; 195 else 196 dwSize = CandidateListWideToAnsi(pCL, NULL, 0, CP_ACP); 197 } 198 199 if (dwBufLen != 0 && dwSize != 0) 200 { 201 if (lpCandList == NULL || dwBufLen < dwSize) 202 goto Quit; 203 204 /* store */ 205 if (bAnsi) 206 { 207 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 208 CandidateListAnsiToWide(pCL, lpCandList, dwSize, CP_ACP); 209 else 210 RtlCopyMemory(lpCandList, pCL, dwSize); 211 } 212 else 213 { 214 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 215 RtlCopyMemory(lpCandList, pCL, dwSize); 216 else 217 CandidateListWideToAnsi(pCL, lpCandList, dwSize, CP_ACP); 218 } 219 } 220 221 ret = dwSize; 222 223 Quit: 224 ImmUnlockIMCC(pIC->hCandInfo); 225 ImmUnlockIMC(hIMC); 226 ImmUnlockClientImc(pClientImc); 227 return ret; 228 } 229 230 DWORD APIENTRY 231 ImmGetCandidateListCountAW(HIMC hIMC, LPDWORD lpdwListCount, BOOL bAnsi) 232 { 233 DWORD ret = 0, cbGot, dwIndex; 234 PCLIENTIMC pClientImc; 235 LPINPUTCONTEXT pIC; 236 const CANDIDATEINFO *pCI; 237 const BYTE *pb; 238 const CANDIDATELIST *pCL; 239 const DWORD *pdwOffsets; 240 241 if (lpdwListCount == NULL) 242 return 0; 243 244 *lpdwListCount = 0; 245 246 pClientImc = ImmLockClientImc(hIMC); 247 if (pClientImc == NULL) 248 return 0; 249 250 pIC = ImmLockIMC(hIMC); 251 if (pIC == NULL) 252 { 253 ImmUnlockClientImc(pClientImc); 254 return 0; 255 } 256 257 pCI = ImmLockIMCC(pIC->hCandInfo); 258 if (pCI == NULL) 259 { 260 ImmUnlockIMC(hIMC); 261 ImmUnlockClientImc(pClientImc); 262 return 0; 263 } 264 265 if (pCI->dwSize < sizeof(CANDIDATEINFO)) 266 goto Quit; 267 268 *lpdwListCount = pCI->dwCount; /* the number of candidate lists */ 269 270 /* calculate total size of candidate lists */ 271 if (bAnsi) 272 { 273 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 274 { 275 ret = ROUNDUP4(pCI->dwPrivateSize); 276 pdwOffsets = pCI->dwOffset; 277 for (dwIndex = 0; dwIndex < pCI->dwCount; ++dwIndex) 278 { 279 pb = (const BYTE *)pCI + pdwOffsets[dwIndex]; 280 pCL = (const CANDIDATELIST *)pb; 281 cbGot = CandidateListWideToAnsi(pCL, NULL, 0, CP_ACP); 282 ret += cbGot; 283 } 284 } 285 else 286 { 287 ret = pCI->dwSize; 288 } 289 } 290 else 291 { 292 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 293 { 294 ret = pCI->dwSize; 295 } 296 else 297 { 298 ret = ROUNDUP4(pCI->dwPrivateSize); 299 pdwOffsets = pCI->dwOffset; 300 for (dwIndex = 0; dwIndex < pCI->dwCount; ++dwIndex) 301 { 302 pb = (const BYTE *)pCI + pdwOffsets[dwIndex]; 303 pCL = (const CANDIDATELIST *)pb; 304 cbGot = CandidateListAnsiToWide(pCL, NULL, 0, CP_ACP); 305 ret += cbGot; 306 } 307 } 308 } 309 310 Quit: 311 ImmUnlockIMCC(pIC->hCandInfo); 312 ImmUnlockIMC(hIMC); 313 ImmUnlockClientImc(pClientImc); 314 return ret; 315 } 316 317 /*********************************************************************** 318 * ImmGetCandidateListA (IMM32.@) 319 */ 320 DWORD WINAPI 321 ImmGetCandidateListA(HIMC hIMC, DWORD dwIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen) 322 { 323 return ImmGetCandidateListAW(hIMC, dwIndex, lpCandList, dwBufLen, TRUE); 324 } 325 326 /*********************************************************************** 327 * ImmGetCandidateListCountA (IMM32.@) 328 */ 329 DWORD WINAPI ImmGetCandidateListCountA(HIMC hIMC, LPDWORD lpdwListCount) 330 { 331 return ImmGetCandidateListCountAW(hIMC, lpdwListCount, TRUE); 332 } 333 334 /*********************************************************************** 335 * ImmGetCandidateListCountW (IMM32.@) 336 */ 337 DWORD WINAPI ImmGetCandidateListCountW(HIMC hIMC, LPDWORD lpdwListCount) 338 { 339 return ImmGetCandidateListCountAW(hIMC, lpdwListCount, FALSE); 340 } 341 342 /*********************************************************************** 343 * ImmGetCandidateListW (IMM32.@) 344 */ 345 DWORD WINAPI 346 ImmGetCandidateListW(HIMC hIMC, DWORD dwIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen) 347 { 348 return ImmGetCandidateListAW(hIMC, dwIndex, lpCandList, dwBufLen, FALSE); 349 } 350 351 /*********************************************************************** 352 * ImmGetCandidateWindow (IMM32.@) 353 */ 354 BOOL WINAPI 355 ImmGetCandidateWindow(HIMC hIMC, DWORD dwIndex, LPCANDIDATEFORM lpCandidate) 356 { 357 BOOL ret = FALSE; 358 LPINPUTCONTEXT pIC; 359 LPCANDIDATEFORM pCF; 360 361 TRACE("(%p, %lu, %p)\n", hIMC, dwIndex, lpCandidate); 362 363 pIC = ImmLockIMC(hIMC); 364 if (pIC == NULL) 365 return FALSE; 366 367 pCF = &pIC->cfCandForm[dwIndex]; 368 if (pCF->dwIndex != IMM_INVALID_CANDFORM) 369 { 370 *lpCandidate = *pCF; 371 ret = TRUE; 372 } 373 374 ImmUnlockIMC(hIMC); 375 return ret; 376 } 377 378 /*********************************************************************** 379 * ImmSetCandidateWindow (IMM32.@) 380 */ 381 BOOL WINAPI ImmSetCandidateWindow(HIMC hIMC, LPCANDIDATEFORM lpCandidate) 382 { 383 HWND hWnd; 384 LPINPUTCONTEXT pIC; 385 386 TRACE("(%p, %p)\n", hIMC, lpCandidate); 387 388 if (lpCandidate->dwIndex >= MAX_CANDIDATEFORM) 389 return FALSE; 390 391 if (Imm32IsCrossThreadAccess(hIMC)) 392 return FALSE; 393 394 pIC = ImmLockIMC(hIMC); 395 if (pIC == NULL) 396 return FALSE; 397 398 hWnd = pIC->hWnd; 399 pIC->cfCandForm[lpCandidate->dwIndex] = *lpCandidate; 400 401 ImmUnlockIMC(hIMC); 402 403 Imm32NotifyAction(hIMC, hWnd, NI_CONTEXTUPDATED, 0, IMC_SETCANDIDATEPOS, 404 IMN_SETCANDIDATEPOS, (1 << (BYTE)lpCandidate->dwIndex)); 405 return TRUE; 406 } 407