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
CandidateListWideToAnsi(const CANDIDATELIST * pWideCL,LPCANDIDATELIST pAnsiCL,DWORD dwBufLen,UINT uCodePage)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
CandidateListAnsiToWide(const CANDIDATELIST * pAnsiCL,LPCANDIDATELIST pWideCL,DWORD dwBufLen,UINT uCodePage)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
ImmGetCandidateListAW(HIMC hIMC,DWORD dwIndex,LPCANDIDATELIST lpCandList,DWORD dwBufLen,BOOL bAnsi)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
ImmGetCandidateListCountAW(HIMC hIMC,LPDWORD lpdwListCount,BOOL bAnsi)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
ImmGetCandidateListA(HIMC hIMC,DWORD dwIndex,LPCANDIDATELIST lpCandList,DWORD dwBufLen)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 */
ImmGetCandidateListCountA(HIMC hIMC,LPDWORD lpdwListCount)351 DWORD WINAPI ImmGetCandidateListCountA(HIMC hIMC, LPDWORD lpdwListCount)
352 {
353 return ImmGetCandidateListCountAW(hIMC, lpdwListCount, TRUE);
354 }
355
356 /***********************************************************************
357 * ImmGetCandidateListCountW (IMM32.@)
358 */
ImmGetCandidateListCountW(HIMC hIMC,LPDWORD lpdwListCount)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
ImmGetCandidateListW(HIMC hIMC,DWORD dwIndex,LPCANDIDATELIST lpCandList,DWORD dwBufLen)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
ImmGetCandidateWindow(HIMC hIMC,DWORD dwIndex,LPCANDIDATEFORM lpCandidate)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
402 ImmUnlockIMC(hIMC);
403 TRACE("ret: %d\n", ret);
404 return ret;
405 }
406
407 /***********************************************************************
408 * ImmSetCandidateWindow (IMM32.@)
409 */
ImmSetCandidateWindow(HIMC hIMC,LPCANDIDATEFORM lpCandidate)410 BOOL WINAPI ImmSetCandidateWindow(HIMC hIMC, LPCANDIDATEFORM lpCandidate)
411 {
412 HWND hWnd;
413 LPINPUTCONTEXT pIC;
414
415 TRACE("(%p, %p)\n", hIMC, lpCandidate);
416
417 if (lpCandidate->dwIndex >= MAX_CANDIDATEFORM)
418 {
419 ERR("Out of boundary\n");
420 return FALSE;
421 }
422
423 if (IS_CROSS_THREAD_HIMC(hIMC))
424 return FALSE;
425
426 pIC = ImmLockIMC(hIMC);
427 if (IS_NULL_UNEXPECTEDLY(pIC))
428 return FALSE;
429
430 hWnd = pIC->hWnd;
431 pIC->cfCandForm[lpCandidate->dwIndex] = *lpCandidate;
432
433 ImmUnlockIMC(hIMC);
434
435 Imm32MakeIMENotify(hIMC, hWnd, NI_CONTEXTUPDATED, 0, IMC_SETCANDIDATEPOS,
436 IMN_SETCANDIDATEPOS, (1 << (BYTE)lpCandidate->dwIndex));
437 return TRUE;
438 }
439