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