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 ret = 0; 157 LPINPUTCONTEXT pIC; 158 PCLIENTIMC pClientImc; 159 LPCANDIDATEINFO pCI; 160 LPCANDIDATELIST pCL; 161 DWORD dwSize; 162 163 pClientImc = ImmLockClientImc(hIMC); 164 if (!pClientImc) 165 return 0; 166 167 pIC = ImmLockIMC(hIMC); 168 if (pIC == NULL) 169 { 170 ImmUnlockClientImc(pClientImc); 171 return 0; 172 } 173 174 pCI = ImmLockIMCC(pIC->hCandInfo); 175 if (pCI == NULL) 176 { 177 ImmUnlockIMC(hIMC); 178 ImmUnlockClientImc(pClientImc); 179 return 0; 180 } 181 182 if (pCI->dwSize < sizeof(CANDIDATEINFO) || pCI->dwCount <= dwIndex) 183 goto Quit; 184 185 /* get required size */ 186 pCL = (LPCANDIDATELIST)((LPBYTE)pCI + pCI->dwOffset[dwIndex]); 187 if (bAnsi) 188 { 189 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 190 dwSize = CandidateListAnsiToWide(pCL, NULL, 0, CP_ACP); 191 else 192 dwSize = pCL->dwSize; 193 } 194 else 195 { 196 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 197 dwSize = pCL->dwSize; 198 else 199 dwSize = CandidateListWideToAnsi(pCL, NULL, 0, CP_ACP); 200 } 201 202 if (dwBufLen != 0 && dwSize != 0) 203 { 204 if (lpCandList == NULL || dwBufLen < dwSize) 205 goto Quit; 206 207 /* store */ 208 if (bAnsi) 209 { 210 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 211 CandidateListAnsiToWide(pCL, lpCandList, dwSize, CP_ACP); 212 else 213 RtlCopyMemory(lpCandList, pCL, dwSize); 214 } 215 else 216 { 217 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 218 RtlCopyMemory(lpCandList, pCL, dwSize); 219 else 220 CandidateListWideToAnsi(pCL, lpCandList, dwSize, CP_ACP); 221 } 222 } 223 224 ret = dwSize; 225 226 Quit: 227 ImmUnlockIMCC(pIC->hCandInfo); 228 ImmUnlockIMC(hIMC); 229 ImmUnlockClientImc(pClientImc); 230 return ret; 231 } 232 233 // Win: ImmGetCandidateListCountWorker 234 DWORD APIENTRY 235 ImmGetCandidateListCountAW(HIMC hIMC, LPDWORD lpdwListCount, BOOL bAnsi) 236 { 237 DWORD ret = 0, cbGot, dwIndex; 238 PCLIENTIMC pClientImc; 239 LPINPUTCONTEXT pIC; 240 const CANDIDATEINFO *pCI; 241 const BYTE *pb; 242 const CANDIDATELIST *pCL; 243 const DWORD *pdwOffsets; 244 245 if (lpdwListCount == NULL) 246 return 0; 247 248 *lpdwListCount = 0; 249 250 pClientImc = ImmLockClientImc(hIMC); 251 if (pClientImc == NULL) 252 return 0; 253 254 pIC = ImmLockIMC(hIMC); 255 if (pIC == NULL) 256 { 257 ImmUnlockClientImc(pClientImc); 258 return 0; 259 } 260 261 pCI = ImmLockIMCC(pIC->hCandInfo); 262 if (pCI == NULL) 263 { 264 ImmUnlockIMC(hIMC); 265 ImmUnlockClientImc(pClientImc); 266 return 0; 267 } 268 269 if (pCI->dwSize < sizeof(CANDIDATEINFO)) 270 goto Quit; 271 272 *lpdwListCount = pCI->dwCount; /* the number of candidate lists */ 273 274 /* calculate total size of candidate lists */ 275 if (bAnsi) 276 { 277 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 278 { 279 ret = ROUNDUP4(pCI->dwPrivateSize); 280 pdwOffsets = pCI->dwOffset; 281 for (dwIndex = 0; dwIndex < pCI->dwCount; ++dwIndex) 282 { 283 pb = (const BYTE *)pCI + pdwOffsets[dwIndex]; 284 pCL = (const CANDIDATELIST *)pb; 285 cbGot = CandidateListWideToAnsi(pCL, NULL, 0, CP_ACP); 286 ret += cbGot; 287 } 288 } 289 else 290 { 291 ret = pCI->dwSize; 292 } 293 } 294 else 295 { 296 if (pClientImc->dwFlags & CLIENTIMC_WIDE) 297 { 298 ret = pCI->dwSize; 299 } 300 else 301 { 302 ret = ROUNDUP4(pCI->dwPrivateSize); 303 pdwOffsets = pCI->dwOffset; 304 for (dwIndex = 0; dwIndex < pCI->dwCount; ++dwIndex) 305 { 306 pb = (const BYTE *)pCI + pdwOffsets[dwIndex]; 307 pCL = (const CANDIDATELIST *)pb; 308 cbGot = CandidateListAnsiToWide(pCL, NULL, 0, CP_ACP); 309 ret += cbGot; 310 } 311 } 312 } 313 314 Quit: 315 ImmUnlockIMCC(pIC->hCandInfo); 316 ImmUnlockIMC(hIMC); 317 ImmUnlockClientImc(pClientImc); 318 return ret; 319 } 320 321 /*********************************************************************** 322 * ImmGetCandidateListA (IMM32.@) 323 */ 324 DWORD WINAPI 325 ImmGetCandidateListA(HIMC hIMC, DWORD dwIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen) 326 { 327 return ImmGetCandidateListAW(hIMC, dwIndex, lpCandList, dwBufLen, TRUE); 328 } 329 330 /*********************************************************************** 331 * ImmGetCandidateListCountA (IMM32.@) 332 */ 333 DWORD WINAPI ImmGetCandidateListCountA(HIMC hIMC, LPDWORD lpdwListCount) 334 { 335 return ImmGetCandidateListCountAW(hIMC, lpdwListCount, TRUE); 336 } 337 338 /*********************************************************************** 339 * ImmGetCandidateListCountW (IMM32.@) 340 */ 341 DWORD WINAPI ImmGetCandidateListCountW(HIMC hIMC, LPDWORD lpdwListCount) 342 { 343 return ImmGetCandidateListCountAW(hIMC, lpdwListCount, FALSE); 344 } 345 346 /*********************************************************************** 347 * ImmGetCandidateListW (IMM32.@) 348 */ 349 DWORD WINAPI 350 ImmGetCandidateListW(HIMC hIMC, DWORD dwIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen) 351 { 352 return ImmGetCandidateListAW(hIMC, dwIndex, lpCandList, dwBufLen, FALSE); 353 } 354 355 /*********************************************************************** 356 * ImmGetCandidateWindow (IMM32.@) 357 */ 358 BOOL WINAPI 359 ImmGetCandidateWindow(HIMC hIMC, DWORD dwIndex, LPCANDIDATEFORM lpCandidate) 360 { 361 BOOL ret = FALSE; 362 LPINPUTCONTEXT pIC; 363 LPCANDIDATEFORM pCF; 364 365 TRACE("(%p, %lu, %p)\n", hIMC, dwIndex, lpCandidate); 366 367 pIC = ImmLockIMC(hIMC); 368 if (pIC == NULL) 369 return FALSE; 370 371 pCF = &pIC->cfCandForm[dwIndex]; 372 if (pCF->dwIndex != IMM_INVALID_CANDFORM) 373 { 374 *lpCandidate = *pCF; 375 ret = TRUE; 376 } 377 378 ImmUnlockIMC(hIMC); 379 return ret; 380 } 381 382 /*********************************************************************** 383 * ImmSetCandidateWindow (IMM32.@) 384 */ 385 BOOL WINAPI ImmSetCandidateWindow(HIMC hIMC, LPCANDIDATEFORM lpCandidate) 386 { 387 HWND hWnd; 388 LPINPUTCONTEXT pIC; 389 390 TRACE("(%p, %p)\n", hIMC, lpCandidate); 391 392 if (lpCandidate->dwIndex >= MAX_CANDIDATEFORM) 393 return FALSE; 394 395 if (Imm32IsCrossThreadAccess(hIMC)) 396 return FALSE; 397 398 pIC = ImmLockIMC(hIMC); 399 if (pIC == NULL) 400 return FALSE; 401 402 hWnd = pIC->hWnd; 403 pIC->cfCandForm[lpCandidate->dwIndex] = *lpCandidate; 404 405 ImmUnlockIMC(hIMC); 406 407 Imm32MakeIMENotify(hIMC, hWnd, NI_CONTEXTUPDATED, 0, IMC_SETCANDIDATEPOS, 408 IMN_SETCANDIDATEPOS, (1 << (BYTE)lpCandidate->dwIndex)); 409 return TRUE; 410 } 411