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