Reactos
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
10WINE_DEFAULT_DEBUG_CHANNEL(msctfime);
11
12/***********************************************************************
13 * CInputContextOwner
14 */
15
16/// @unimplemented
17CInputContextOwner::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
26CInputContextOwner::~CInputContextOwner()
27{
28}
29
30/// @implemented
31HRESULT 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
54HRESULT 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
81STDMETHODIMP 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
93STDMETHODIMP_(ULONG) CInputContextOwner::AddRef()
94{
95 return ++m_cRefs;
96}
97
98/// @implemented
99STDMETHODIMP_(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
110STDMETHODIMP
111CInputContextOwner::GetACPFromPoint(
112 const POINT *ptScreen,
113 DWORD dwFlags,
114 LONG *pacp)
115{
116 return E_NOTIMPL;
117}
118
119/// @unimplemented
120STDMETHODIMP
121CInputContextOwner::GetTextExt(
122 LONG acpStart,
123 LONG acpEnd,
124 RECT *prc,
125 BOOL *pfClipped)
126{
127 return E_NOTIMPL;
128}
129
130/// @implemented
131STDMETHODIMP CInputContextOwner::GetScreenExt(RECT *prc)
132{
133 return m_fnCallback(2, &prc, m_pCallbackPV);
134}
135
136/// @implemented
137STDMETHODIMP CInputContextOwner::GetStatus(TF_STATUS *pdcs)
138{
139 return m_fnCallback(6, &pdcs, m_pCallbackPV);
140}
141
142/// @unimplemented
143STDMETHODIMP CInputContextOwner::GetWnd(HWND *phwnd)
144{
145 return m_fnCallback(7, &phwnd, m_pCallbackPV);
146}
147
148/// @unimplemented
149STDMETHODIMP CInputContextOwner::GetAttribute(REFGUID rguidAttribute, VARIANT *pvarValue)
150{
151 return E_NOTIMPL;
152}
153
154struct MOUSE_SINK_ARGS
155{
156 ITfRangeACP *range;
157 ITfMouseSink *pSink;
158 DWORD *pdwCookie;
159};
160
161/// @implemented
162STDMETHODIMP 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
172STDMETHODIMP CInputContextOwner::UnadviseMouseSink(DWORD dwCookie)
173{
174 return m_fnCallback(10, &dwCookie, m_pCallbackPV);
175}
176
177/***********************************************************************
178 * CicInputContext
179 */
180
181/// @unimplemented
182CicInputContext::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
193STDMETHODIMP 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
205STDMETHODIMP_(ULONG) CicInputContext::AddRef()
206{
207 return ::InterlockedIncrement(&m_cRefs);
208}
209
210/// @implemented
211STDMETHODIMP_(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
222STDMETHODIMP
223CicInputContext::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
240STDMETHODIMP
241CicInputContext::OnUpdateComposition(
242 ITfCompositionView *pComposition,
243 ITfRange *pRangeNew)
244{
245 return S_OK;
246}
247
248/// @implemented
249STDMETHODIMP
250CicInputContext::OnEndComposition(
251 ITfCompositionView *pComposition)
252{
253 --m_cCompLocks;
254 return S_OK;
255}
256
257/// @implemented
258HRESULT
259CicInputContext::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
279HRESULT
280CicInputContext::CreateInputContext(
281 _Inout_ ITfThreadMgr *pThreadMgr,
282 _Inout_ CicIMCLock& imcLock)
283{
284 //FIXME
285 return E_NOTIMPL;
286}
287
288/// @unimplemented
289HRESULT
290CicInputContext::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
358STDMETHODIMP
359CicInputContext::OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition)
360{
361 return S_OK;
362}
363
364/// @implemented
365STDMETHODIMP
366CicInputContext::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
416HRESULT CicInputContext::SetupDocFeedString(CicIMCLock& imcLock, UINT uCodePage)
417{
418 return E_NOTIMPL;
419}
420
421/// @unimplemented
422HRESULT CicInputContext::EscbClearDocFeedBuffer(CicIMCLock& imcLock, BOOL bFlag)
423{
424 return E_NOTIMPL;
425}
426
427/// @unimplemented
428HRESULT CicInputContext::EscbCompComplete(CicIMCLock& imcLock)
429{
430 return E_NOTIMPL;
431}
432
433/// @unimplemented
434HRESULT CicInputContext::EscbCompCancel(CicIMCLock& imcLock)
435{
436 return E_NOTIMPL;
437}
438
439/// @unimplemented
440HRESULT CicInputContext::OnSetCandidatePos(TLS *pTLS, CicIMCLock& imcLock)
441{
442 return E_NOTIMPL;
443}
444
445/// @unimplemented
446HRESULT CicInputContext::DelayedReconvertFuncCall(CicIMCLock& imcLock)
447{
448 return E_NOTIMPL;
449}
450
451/// @unimplemented
452HRESULT
453CicInputContext::MsImeMouseHandler(
454 DWORD dwUnknown58,
455 DWORD dwUnknown59,
456 UINT keys,
457 CicIMCLock& imcLock)
458{
459 return E_NOTIMPL;
460}
461
462/// @unimplemented
463HRESULT
464CicInputContext::SetupReconvertString(
465 CicIMCLock& imcLock,
466 ITfThreadMgr_P *pThreadMgr,
467 UINT uCodePage,
468 UINT uMsg,
469 BOOL bUndo)
470{
471 return E_NOTIMPL;
472}
473
474void 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
484HRESULT CicInputContext::EndReconvertString(CicIMCLock& imcLock)
485{
486 return E_NOTIMPL;
487}
488
489/// @unimplemented
490BOOL 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}