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