1 /*
2 * PROJECT: ReactOS msctfime.ime
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: Input Context of msctfime.ime
5 * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6 */
7
8 #include "msctfime.h"
9
10 WINE_DEFAULT_DEBUG_CHANNEL(msctfime);
11
12 /***********************************************************************
13 * CInputContextOwner
14 */
15
16 /// @unimplemented
CInputContextOwner(FN_IC_OWNER_CALLBACK fnCallback,LPVOID pCallbackPV)17 CInputContextOwner::CInputContextOwner(FN_IC_OWNER_CALLBACK fnCallback, LPVOID pCallbackPV)
18 {
19 m_dwCookie = -1;
20 m_fnCallback = fnCallback;
21 m_cRefs = 1;
22 m_pCallbackPV = pCallbackPV;
23 }
24
25 /// @implemented
~CInputContextOwner()26 CInputContextOwner::~CInputContextOwner()
27 {
28 }
29
30 /// @implemented
_Advise(IUnknown * pContext)31 HRESULT CInputContextOwner::_Advise(IUnknown *pContext)
32 {
33 ITfSource *pSource = NULL;
34
35 m_pContext = NULL;
36
37 HRESULT hr = E_FAIL;
38 if (SUCCEEDED(m_pContext->QueryInterface(IID_ITfSource, (LPVOID*)&pSource)) &&
39 SUCCEEDED(pSource->AdviseSink(IID_ITfContextOwner,
40 static_cast<ITfContextOwner*>(this), &m_dwCookie)))
41 {
42 m_pContext = pContext;
43 m_pContext->AddRef();
44 hr = S_OK;
45 }
46
47 if (pSource)
48 pSource->Release();
49
50 return hr;
51 }
52
53 /// @implemented
_Unadvise()54 HRESULT CInputContextOwner::_Unadvise()
55 {
56 ITfSource *pSource = NULL;
57
58 HRESULT hr = E_FAIL;
59 if (m_pContext)
60 {
61 if (SUCCEEDED(m_pContext->QueryInterface(IID_ITfSource, (LPVOID*)&pSource)) &&
62 SUCCEEDED(pSource->UnadviseSink(m_dwCookie)))
63 {
64 hr = S_OK;
65 }
66 }
67
68 if (m_pContext)
69 {
70 m_pContext->Release();
71 m_pContext = NULL;
72 }
73
74 if (pSource)
75 pSource->Release();
76
77 return hr;
78 }
79
80 /// @implemented
QueryInterface(REFIID riid,LPVOID * ppvObj)81 STDMETHODIMP CInputContextOwner::QueryInterface(REFIID riid, LPVOID* ppvObj)
82 {
83 static const QITAB c_tab[] =
84 {
85 QITABENT(CInputContextOwner, ITfContextOwner),
86 QITABENT(CInputContextOwner, ITfMouseTrackerACP),
87 { NULL }
88 };
89 return ::QISearch(this, c_tab, riid, ppvObj);
90 }
91
92 /// @implemented
STDMETHODIMP_(ULONG)93 STDMETHODIMP_(ULONG) CInputContextOwner::AddRef()
94 {
95 return ++m_cRefs;
96 }
97
98 /// @implemented
STDMETHODIMP_(ULONG)99 STDMETHODIMP_(ULONG) CInputContextOwner::Release()
100 {
101 if (--m_cRefs == 0)
102 {
103 delete this;
104 return 0;
105 }
106 return m_cRefs;
107 }
108
109 /// @unimplemented
110 STDMETHODIMP
GetACPFromPoint(const POINT * ptScreen,DWORD dwFlags,LONG * pacp)111 CInputContextOwner::GetACPFromPoint(
112 const POINT *ptScreen,
113 DWORD dwFlags,
114 LONG *pacp)
115 {
116 return E_NOTIMPL;
117 }
118
119 /// @unimplemented
120 STDMETHODIMP
GetTextExt(LONG acpStart,LONG acpEnd,RECT * prc,BOOL * pfClipped)121 CInputContextOwner::GetTextExt(
122 LONG acpStart,
123 LONG acpEnd,
124 RECT *prc,
125 BOOL *pfClipped)
126 {
127 return E_NOTIMPL;
128 }
129
130 /// @implemented
GetScreenExt(RECT * prc)131 STDMETHODIMP CInputContextOwner::GetScreenExt(RECT *prc)
132 {
133 return m_fnCallback(2, &prc, m_pCallbackPV);
134 }
135
136 /// @implemented
GetStatus(TF_STATUS * pdcs)137 STDMETHODIMP CInputContextOwner::GetStatus(TF_STATUS *pdcs)
138 {
139 return m_fnCallback(6, &pdcs, m_pCallbackPV);
140 }
141
142 /// @unimplemented
GetWnd(HWND * phwnd)143 STDMETHODIMP CInputContextOwner::GetWnd(HWND *phwnd)
144 {
145 return m_fnCallback(7, &phwnd, m_pCallbackPV);
146 }
147
148 /// @unimplemented
GetAttribute(REFGUID rguidAttribute,VARIANT * pvarValue)149 STDMETHODIMP CInputContextOwner::GetAttribute(REFGUID rguidAttribute, VARIANT *pvarValue)
150 {
151 return E_NOTIMPL;
152 }
153
154 struct MOUSE_SINK_ARGS
155 {
156 ITfRangeACP *range;
157 ITfMouseSink *pSink;
158 DWORD *pdwCookie;
159 };
160
161 /// @implemented
AdviseMouseSink(ITfRangeACP * range,ITfMouseSink * pSink,DWORD * pdwCookie)162 STDMETHODIMP CInputContextOwner::AdviseMouseSink(
163 ITfRangeACP *range,
164 ITfMouseSink *pSink,
165 DWORD *pdwCookie)
166 {
167 MOUSE_SINK_ARGS args = { range, pSink, pdwCookie };
168 return m_fnCallback(9, &args, m_pCallbackPV);
169 }
170
171 /// @implemented
UnadviseMouseSink(DWORD dwCookie)172 STDMETHODIMP CInputContextOwner::UnadviseMouseSink(DWORD dwCookie)
173 {
174 return m_fnCallback(10, &dwCookie, m_pCallbackPV);
175 }
176
177 /***********************************************************************
178 * CicInputContext
179 */
180
181 /// @unimplemented
CicInputContext(_In_ TfClientId cliendId,_Inout_ PCIC_LIBTHREAD pLibThread,_In_ HIMC hIMC)182 CicInputContext::CicInputContext(
183 _In_ TfClientId cliendId,
184 _Inout_ PCIC_LIBTHREAD pLibThread,
185 _In_ HIMC hIMC)
186 {
187 m_hIMC = hIMC;
188 m_dwQueryPos = 0;
189 m_cRefs = 1;
190 }
191
192 /// @implemented
QueryInterface(REFIID riid,LPVOID * ppvObj)193 STDMETHODIMP CicInputContext::QueryInterface(REFIID riid, LPVOID* ppvObj)
194 {
195 static const QITAB c_tab[] =
196 {
197 QITABENT(CicInputContext, ITfCleanupContextSink),
198 QITABENT(CicInputContext, ITfContextOwnerCompositionSink),
199 { NULL }
200 };
201 return ::QISearch(this, c_tab, riid, ppvObj);
202 }
203
204 /// @implemented
STDMETHODIMP_(ULONG)205 STDMETHODIMP_(ULONG) CicInputContext::AddRef()
206 {
207 return ::InterlockedIncrement(&m_cRefs);
208 }
209
210 /// @implemented
STDMETHODIMP_(ULONG)211 STDMETHODIMP_(ULONG) CicInputContext::Release()
212 {
213 if (::InterlockedDecrement(&m_cRefs) == 0)
214 {
215 delete this;
216 return 0;
217 }
218 return m_cRefs;
219 }
220
221 /// @implemented
222 STDMETHODIMP
OnStartComposition(ITfCompositionView * pComposition,BOOL * pfOk)223 CicInputContext::OnStartComposition(
224 ITfCompositionView *pComposition,
225 BOOL *pfOk)
226 {
227 if ((m_cCompLocks <= 0) || m_bReconverting)
228 {
229 *pfOk = TRUE;
230 ++m_cCompLocks;
231 }
232 else
233 {
234 *pfOk = FALSE;
235 }
236 return S_OK;
237 }
238
239 /// @implemented
240 STDMETHODIMP
OnUpdateComposition(ITfCompositionView * pComposition,ITfRange * pRangeNew)241 CicInputContext::OnUpdateComposition(
242 ITfCompositionView *pComposition,
243 ITfRange *pRangeNew)
244 {
245 return S_OK;
246 }
247
248 /// @implemented
249 STDMETHODIMP
OnEndComposition(ITfCompositionView * pComposition)250 CicInputContext::OnEndComposition(
251 ITfCompositionView *pComposition)
252 {
253 --m_cCompLocks;
254 return S_OK;
255 }
256
257 /// @implemented
258 HRESULT
GetGuidAtom(_Inout_ CicIMCLock & imcLock,_In_ BYTE iAtom,_Out_opt_ LPDWORD pdwGuidAtom)259 CicInputContext::GetGuidAtom(
260 _Inout_ CicIMCLock& imcLock,
261 _In_ BYTE iAtom,
262 _Out_opt_ LPDWORD pdwGuidAtom)
263 {
264 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCompStr);
265 if (FAILED(imeContext.m_hr))
266 return imeContext.m_hr;
267
268 HRESULT hr = E_FAIL;
269 if (iAtom < m_cGuidAtoms)
270 {
271 *pdwGuidAtom = m_adwGuidAtoms[iAtom];
272 hr = S_OK;
273 }
274
275 return hr;
276 }
277
278 /// @unimplemented
279 HRESULT
CreateInputContext(_Inout_ ITfThreadMgr * pThreadMgr,_Inout_ CicIMCLock & imcLock)280 CicInputContext::CreateInputContext(
281 _Inout_ ITfThreadMgr *pThreadMgr,
282 _Inout_ CicIMCLock& imcLock)
283 {
284 //FIXME
285 return E_NOTIMPL;
286 }
287
288 /// @unimplemented
289 HRESULT
DestroyInputContext()290 CicInputContext::DestroyInputContext()
291 {
292 ITfSourceSingle *pSource = NULL;
293
294 if (m_pContext && m_pContext->QueryInterface(IID_ITfSourceSingle, (void **)&pSource) == S_OK)
295 pSource->UnadviseSingleSink(m_clientId, IID_ITfCleanupContextSink);
296
297 //FIXME: m_dwUnknown5
298
299 if (m_pTextEventSink)
300 {
301 m_pTextEventSink->_Unadvise();
302 m_pTextEventSink->Release();
303 m_pTextEventSink = NULL;
304 }
305
306 if (m_pCompEventSink2)
307 {
308 m_pCompEventSink2->_Unadvise();
309 m_pCompEventSink2->Release();
310 m_pCompEventSink2 = NULL;
311 }
312
313 if (m_pCompEventSink1)
314 {
315 m_pCompEventSink1->_Unadvise();
316 m_pCompEventSink1->Release();
317 m_pCompEventSink1 = NULL;
318 }
319
320 if (m_pInputContextOwner)
321 {
322 m_pInputContextOwner->_Unadvise();
323 m_pInputContextOwner->Release();
324 m_pInputContextOwner = NULL;
325 }
326
327 if (m_pDocumentMgr)
328 m_pDocumentMgr->Pop(1);
329
330 if (m_pContext)
331 {
332 ClearCompartment(m_clientId, m_pContext, GUID_COMPARTMENT_CTFIME_CICINPUTCONTEXT, 0);
333 m_pContext->Release();
334 m_pContext = NULL;
335 }
336
337 if (m_pContextOwnerServices)
338 {
339 m_pContextOwnerServices->Release();
340 m_pContextOwnerServices = NULL;
341 }
342
343 // FIXME: m_pICOwnerCallback
344
345 if (m_pDocumentMgr)
346 {
347 m_pDocumentMgr->Release();
348 m_pDocumentMgr = NULL;
349 }
350
351 if (pSource)
352 pSource->Release();
353
354 return S_OK;
355 }
356
357 /// @implemented
358 STDMETHODIMP
OnCompositionTerminated(TfEditCookie ecWrite,ITfComposition * pComposition)359 CicInputContext::OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition)
360 {
361 return S_OK;
362 }
363
364 /// @implemented
365 STDMETHODIMP
OnCleanupContext(_In_ TfEditCookie ecWrite,_Inout_ ITfContext * pic)366 CicInputContext::OnCleanupContext(
367 _In_ TfEditCookie ecWrite,
368 _Inout_ ITfContext *pic)
369 {
370 TLS *pTLS = TLS::PeekTLS();
371 if (!pTLS || !pTLS->m_pProfile)
372 return E_OUTOFMEMORY;
373
374 LANGID LangID;
375 pTLS->m_pProfile->GetLangId(&LangID);
376
377 IMEINFO IMEInfo;
378 WCHAR szPath[MAX_PATH];
379 if (Inquire(&IMEInfo, szPath, 0, (HKL)UlongToHandle(LangID)) != S_OK)
380 return E_FAIL;
381
382 ITfProperty *pProp = NULL;
383 if (!(IMEInfo.fdwProperty & IME_PROP_COMPLETE_ON_UNSELECT))
384 return S_OK;
385
386 HRESULT hr = pic->GetProperty(GUID_PROP_COMPOSING, &pProp);
387 if (FAILED(hr))
388 return S_OK;
389
390 IEnumTfRanges *pRanges = NULL;
391 hr = pProp->EnumRanges(ecWrite, &pRanges, NULL);
392 if (SUCCEEDED(hr))
393 {
394 ITfRange *pRange = NULL;
395 while (pRanges->Next(1, &pRange, 0) == S_OK)
396 {
397 VARIANT vari;
398 V_VT(&vari) = VT_EMPTY;
399 pProp->GetValue(ecWrite, pRange, &vari);
400 if (V_VT(&vari) == VT_I4)
401 {
402 if (V_I4(&vari))
403 pProp->Clear(ecWrite, pRange);
404 }
405 pRange->Release();
406 pRange = NULL;
407 }
408 pRanges->Release();
409 }
410 pProp->Release();
411
412 return S_OK;
413 }
414
415 /// @unimplemented
SetupDocFeedString(CicIMCLock & imcLock,UINT uCodePage)416 HRESULT CicInputContext::SetupDocFeedString(CicIMCLock& imcLock, UINT uCodePage)
417 {
418 return E_NOTIMPL;
419 }
420
421 /// @unimplemented
EscbClearDocFeedBuffer(CicIMCLock & imcLock,BOOL bFlag)422 HRESULT CicInputContext::EscbClearDocFeedBuffer(CicIMCLock& imcLock, BOOL bFlag)
423 {
424 return E_NOTIMPL;
425 }
426
427 /// @unimplemented
EscbCompComplete(CicIMCLock & imcLock)428 HRESULT CicInputContext::EscbCompComplete(CicIMCLock& imcLock)
429 {
430 return E_NOTIMPL;
431 }
432
433 /// @unimplemented
EscbCompCancel(CicIMCLock & imcLock)434 HRESULT CicInputContext::EscbCompCancel(CicIMCLock& imcLock)
435 {
436 return E_NOTIMPL;
437 }
438
439 /// @unimplemented
OnSetCandidatePos(TLS * pTLS,CicIMCLock & imcLock)440 HRESULT CicInputContext::OnSetCandidatePos(TLS *pTLS, CicIMCLock& imcLock)
441 {
442 return E_NOTIMPL;
443 }
444
445 /// @unimplemented
DelayedReconvertFuncCall(CicIMCLock & imcLock)446 HRESULT CicInputContext::DelayedReconvertFuncCall(CicIMCLock& imcLock)
447 {
448 return E_NOTIMPL;
449 }
450
451 /// @unimplemented
452 HRESULT
MsImeMouseHandler(DWORD dwUnknown58,DWORD dwUnknown59,UINT keys,CicIMCLock & imcLock)453 CicInputContext::MsImeMouseHandler(
454 DWORD dwUnknown58,
455 DWORD dwUnknown59,
456 UINT keys,
457 CicIMCLock& imcLock)
458 {
459 return E_NOTIMPL;
460 }
461
462 /// @unimplemented
463 HRESULT
SetupReconvertString(CicIMCLock & imcLock,ITfThreadMgr_P * pThreadMgr,UINT uCodePage,UINT uMsg,BOOL bUndo)464 CicInputContext::SetupReconvertString(
465 CicIMCLock& imcLock,
466 ITfThreadMgr_P *pThreadMgr,
467 UINT uCodePage,
468 UINT uMsg,
469 BOOL bUndo)
470 {
471 return E_NOTIMPL;
472 }
473
ClearPrevCandidatePos()474 void CicInputContext::ClearPrevCandidatePos()
475 {
476 m_dwUnknown8 = 0;
477 ZeroMemory(&m_rcCandidate1, sizeof(m_rcCandidate1));
478 ZeroMemory(&m_CandForm, sizeof(m_CandForm));
479 ZeroMemory(&m_rcCandidate2, sizeof(m_rcCandidate2));
480 m_dwQueryPos = 0;
481 }
482
483 /// @unimplemented
EndReconvertString(CicIMCLock & imcLock)484 HRESULT CicInputContext::EndReconvertString(CicIMCLock& imcLock)
485 {
486 return E_NOTIMPL;
487 }
488
489 /// @unimplemented
SetCompositionString(CicIMCLock & imcLock,ITfThreadMgr_P * pThreadMgr,DWORD dwIndex,LPCVOID lpComp,DWORD dwCompLen,LPCVOID lpRead,DWORD dwReadLen,UINT uCodePage)490 BOOL CicInputContext::SetCompositionString(
491 CicIMCLock& imcLock,
492 ITfThreadMgr_P *pThreadMgr,
493 DWORD dwIndex,
494 LPCVOID lpComp,
495 DWORD dwCompLen,
496 LPCVOID lpRead,
497 DWORD dwReadLen,
498 UINT uCodePage)
499 {
500 return FALSE;
501 }
502