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