xref: /reactos/dll/win32/imm32/candidate.c (revision 0bc6bd64)
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