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