xref: /reactos/dll/ime/msctfime/inputcontext.cpp (revision c5e64563)
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