1 /*
2 * PROJECT: ReactOS msctfime.ime
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: The sinks 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 /// @implemented
CCompartmentEventSink(FN_EVENTSINK fnEventSink,LPVOID pUserData)13 CCompartmentEventSink::CCompartmentEventSink(FN_EVENTSINK fnEventSink, LPVOID pUserData)
14 : m_array()
15 , m_cRefs(1)
16 , m_fnEventSink(fnEventSink)
17 , m_pUserData(pUserData)
18 {
19 }
20
21 /// @implemented
~CCompartmentEventSink()22 CCompartmentEventSink::~CCompartmentEventSink()
23 {
24 }
25
26 /// @implemented
QueryInterface(REFIID riid,LPVOID * ppvObj)27 STDMETHODIMP CCompartmentEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj)
28 {
29 static const QITAB c_tab[] =
30 {
31 QITABENT(CCompartmentEventSink, ITfCompartmentEventSink),
32 { NULL }
33 };
34 return ::QISearch(this, c_tab, riid, ppvObj);
35 }
36
37 /// @implemented
STDMETHODIMP_(ULONG)38 STDMETHODIMP_(ULONG) CCompartmentEventSink::AddRef()
39 {
40 return ::InterlockedIncrement(&m_cRefs);
41 }
42
43 /// @implemented
STDMETHODIMP_(ULONG)44 STDMETHODIMP_(ULONG) CCompartmentEventSink::Release()
45 {
46 if (::InterlockedDecrement(&m_cRefs) == 0)
47 {
48 delete this;
49 return 0;
50 }
51 return m_cRefs;
52 }
53
54 /// @implemented
OnChange(REFGUID rguid)55 STDMETHODIMP CCompartmentEventSink::OnChange(REFGUID rguid)
56 {
57 return m_fnEventSink(m_pUserData, rguid);
58 }
59
60 /// @implemented
61 HRESULT
_Advise(IUnknown * pUnknown,REFGUID rguid,BOOL bThread)62 CCompartmentEventSink::_Advise(IUnknown *pUnknown, REFGUID rguid, BOOL bThread)
63 {
64 CESMAP *pCesMap = m_array.Append(1);
65 if (!pCesMap)
66 return E_OUTOFMEMORY;
67
68 ITfSource *pSource = NULL;
69
70 HRESULT hr = GetCompartment(pUnknown, rguid, &pCesMap->m_pComp, bThread);
71 if (FAILED(hr))
72 {
73 hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource);
74 if (FAILED(hr))
75 {
76 hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &pCesMap->m_dwCookie);
77 if (FAILED(hr))
78 {
79 if (pCesMap->m_pComp)
80 {
81 pCesMap->m_pComp->Release();
82 pCesMap->m_pComp = NULL;
83 }
84 m_array.Remove(m_array.size() - 1, 1);
85 }
86 else
87 {
88 hr = S_OK;
89 }
90 }
91 }
92
93 if (pSource)
94 pSource->Release();
95
96 return hr;
97 }
98
99 /// @implemented
_Unadvise()100 HRESULT CCompartmentEventSink::_Unadvise()
101 {
102 CESMAP *pCesMap = m_array.data();
103 size_t cItems = m_array.size();
104 if (!cItems)
105 return S_OK;
106
107 do
108 {
109 ITfSource *pSource = NULL;
110 HRESULT hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource);
111 if (SUCCEEDED(hr))
112 pSource->UnadviseSink(pCesMap->m_dwCookie);
113
114 if (pCesMap->m_pComp)
115 {
116 pCesMap->m_pComp->Release();
117 pCesMap->m_pComp = NULL;
118 }
119
120 if (pSource)
121 pSource->Release();
122
123 ++pCesMap;
124 --cItems;
125 } while (cItems);
126
127 return S_OK;
128 }
129
130 /***********************************************************************/
131
132 /// @implemented
CTextEventSink(FN_ENDEDIT fnEndEdit,LPVOID pCallbackPV)133 CTextEventSink::CTextEventSink(FN_ENDEDIT fnEndEdit, LPVOID pCallbackPV)
134 {
135 m_cRefs = 1;
136 m_pUnknown = NULL;
137 m_dwEditSinkCookie = (DWORD)-1;
138 m_dwLayoutSinkCookie = (DWORD)-1;
139 m_fnLayoutChange = NULL;
140 m_fnEndEdit = fnEndEdit;
141 m_pCallbackPV = pCallbackPV;
142 }
143
144 /// @implemented
~CTextEventSink()145 CTextEventSink::~CTextEventSink()
146 {
147 }
148
149 /// @implemented
QueryInterface(REFIID riid,LPVOID * ppvObj)150 STDMETHODIMP CTextEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj)
151 {
152 static const QITAB c_tab[] =
153 {
154 QITABENT(CTextEventSink, ITfTextEditSink),
155 QITABENT(CTextEventSink, ITfTextLayoutSink),
156 { NULL }
157 };
158 return ::QISearch(this, c_tab, riid, ppvObj);
159 }
160
161 /// @implemented
STDMETHODIMP_(ULONG)162 STDMETHODIMP_(ULONG) CTextEventSink::AddRef()
163 {
164 return ::InterlockedIncrement(&m_cRefs);
165 }
166
167 /// @implemented
STDMETHODIMP_(ULONG)168 STDMETHODIMP_(ULONG) CTextEventSink::Release()
169 {
170 if (::InterlockedDecrement(&m_cRefs) == 0)
171 {
172 delete this;
173 return 0;
174 }
175 return m_cRefs;
176 }
177
178 struct TEXT_EVENT_SINK_END_EDIT
179 {
180 TfEditCookie m_ecReadOnly;
181 ITfEditRecord *m_pEditRecord;
182 ITfContext *m_pContext;
183 };
184
185 /// @implemented
OnEndEdit(ITfContext * pic,TfEditCookie ecReadOnly,ITfEditRecord * pEditRecord)186 STDMETHODIMP CTextEventSink::OnEndEdit(
187 ITfContext *pic,
188 TfEditCookie ecReadOnly,
189 ITfEditRecord *pEditRecord)
190 {
191 TEXT_EVENT_SINK_END_EDIT Data = { ecReadOnly, pEditRecord, pic };
192 return m_fnEndEdit(1, m_pCallbackPV, (LPVOID)&Data);
193 }
194
195 /// @implemented
OnLayoutChange(ITfContext * pContext,TfLayoutCode lcode,ITfContextView * pContextView)196 STDMETHODIMP CTextEventSink::OnLayoutChange(
197 ITfContext *pContext,
198 TfLayoutCode lcode,
199 ITfContextView *pContextView)
200 {
201 switch (lcode)
202 {
203 case TF_LC_CREATE:
204 return m_fnLayoutChange(3, m_fnEndEdit, pContextView);
205 case TF_LC_CHANGE:
206 return m_fnLayoutChange(2, m_fnEndEdit, pContextView);
207 case TF_LC_DESTROY:
208 return m_fnLayoutChange(4, m_fnEndEdit, pContextView);
209 default:
210 return E_INVALIDARG;
211 }
212 }
213
214 /// @implemented
_Advise(IUnknown * pUnknown,UINT uFlags)215 HRESULT CTextEventSink::_Advise(IUnknown *pUnknown, UINT uFlags)
216 {
217 m_pUnknown = NULL;
218 m_uFlags = uFlags;
219
220 ITfSource *pSource = NULL;
221 HRESULT hr = pUnknown->QueryInterface(IID_ITfSource, (void**)&pSource);
222 if (SUCCEEDED(hr))
223 {
224 ITfTextEditSink *pSink = static_cast<ITfTextEditSink*>(this);
225 if (uFlags & 1)
226 hr = pSource->AdviseSink(IID_ITfTextEditSink, pSink, &m_dwEditSinkCookie);
227 if (SUCCEEDED(hr) && (uFlags & 2))
228 hr = pSource->AdviseSink(IID_ITfTextLayoutSink, pSink, &m_dwLayoutSinkCookie);
229
230 if (SUCCEEDED(hr))
231 {
232 m_pUnknown = pUnknown;
233 pUnknown->AddRef();
234 }
235 else
236 {
237 pSource->UnadviseSink(m_dwEditSinkCookie);
238 }
239 }
240
241 if (pSource)
242 pSource->Release();
243
244 return hr;
245 }
246
247 /// @implemented
_Unadvise()248 HRESULT CTextEventSink::_Unadvise()
249 {
250 if (!m_pUnknown)
251 return E_FAIL;
252
253 ITfSource *pSource = NULL;
254 HRESULT hr = m_pUnknown->QueryInterface(IID_ITfSource, (void**)&pSource);
255 if (SUCCEEDED(hr))
256 {
257 if (m_uFlags & 1)
258 hr = pSource->UnadviseSink(m_dwEditSinkCookie);
259 if (m_uFlags & 2)
260 hr = pSource->UnadviseSink(m_dwLayoutSinkCookie);
261
262 pSource->Release();
263 }
264
265 m_pUnknown->Release();
266 m_pUnknown = NULL;
267
268 return E_NOTIMPL;
269 }
270
271 /***********************************************************************/
272
273 /// @implemented
CThreadMgrEventSink(_In_ FN_INITDOCMGR fnInit,_In_ FN_PUSHPOP fnPushPop,_Inout_ LPVOID pvCallbackPV)274 CThreadMgrEventSink::CThreadMgrEventSink(
275 _In_ FN_INITDOCMGR fnInit,
276 _In_ FN_PUSHPOP fnPushPop,
277 _Inout_ LPVOID pvCallbackPV)
278 {
279 m_fnInit = fnInit;
280 m_fnPushPop = fnPushPop;
281 m_pCallbackPV = pvCallbackPV;
282 m_cRefs = 1;
283 }
284
285 /// @implemented
QueryInterface(REFIID riid,LPVOID * ppvObj)286 STDMETHODIMP CThreadMgrEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj)
287 {
288 static const QITAB c_tab[] =
289 {
290 QITABENT(CThreadMgrEventSink, ITfThreadMgrEventSink),
291 { NULL }
292 };
293 return ::QISearch(this, c_tab, riid, ppvObj);
294 }
295
296 /// @implemented
STDMETHODIMP_(ULONG)297 STDMETHODIMP_(ULONG) CThreadMgrEventSink::AddRef()
298 {
299 return ::InterlockedIncrement(&m_cRefs);
300 }
301
302 /// @implemented
STDMETHODIMP_(ULONG)303 STDMETHODIMP_(ULONG) CThreadMgrEventSink::Release()
304 {
305 if (::InterlockedDecrement(&m_cRefs) == 0)
306 {
307 delete this;
308 return 0;
309 }
310 return m_cRefs;
311 }
312
313 INT CALLBACK
DIMCallback(UINT nCode,ITfDocumentMgr * pDocMgr1,ITfDocumentMgr * pDocMgr2,LPVOID pUserData)314 CThreadMgrEventSink::DIMCallback(
315 UINT nCode,
316 ITfDocumentMgr *pDocMgr1,
317 ITfDocumentMgr *pDocMgr2,
318 LPVOID pUserData)
319 {
320 return E_NOTIMPL;
321 }
322
OnInitDocumentMgr(ITfDocumentMgr * pdim)323 STDMETHODIMP CThreadMgrEventSink::OnInitDocumentMgr(ITfDocumentMgr *pdim)
324 {
325 if (!m_fnInit)
326 return S_OK;
327 return m_fnInit(0, pdim, NULL, m_pCallbackPV);
328 }
329
OnUninitDocumentMgr(ITfDocumentMgr * pdim)330 STDMETHODIMP CThreadMgrEventSink::OnUninitDocumentMgr(ITfDocumentMgr *pdim)
331 {
332 if (!m_fnInit)
333 return S_OK;
334 return m_fnInit(1, pdim, NULL, m_pCallbackPV);
335 }
336
337 STDMETHODIMP
OnSetFocus(ITfDocumentMgr * pdimFocus,ITfDocumentMgr * pdimPrevFocus)338 CThreadMgrEventSink::OnSetFocus(ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus)
339 {
340 if (!m_fnInit)
341 return S_OK;
342 return m_fnInit(2, pdimFocus, pdimPrevFocus, m_pCallbackPV);
343 }
344
OnPushContext(ITfContext * pic)345 STDMETHODIMP CThreadMgrEventSink::OnPushContext(ITfContext *pic)
346 {
347 if (!m_fnPushPop)
348 return S_OK;
349 return m_fnPushPop(3, pic, m_pCallbackPV);
350 }
351
OnPopContext(ITfContext * pic)352 STDMETHODIMP CThreadMgrEventSink::OnPopContext(ITfContext *pic)
353 {
354 if (!m_fnPushPop)
355 return S_OK;
356 return m_fnPushPop(4, pic, m_pCallbackPV);
357 }
358
SetCallbackPV(_Inout_ LPVOID pv)359 void CThreadMgrEventSink::SetCallbackPV(_Inout_ LPVOID pv)
360 {
361 if (!m_pCallbackPV)
362 m_pCallbackPV = pv;
363 }
364
_Advise(ITfThreadMgr * pThreadMgr)365 HRESULT CThreadMgrEventSink::_Advise(ITfThreadMgr *pThreadMgr)
366 {
367 m_pThreadMgr = NULL;
368
369 HRESULT hr = E_FAIL;
370 ITfSource *pSource = NULL;
371 if (pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK &&
372 pSource->AdviseSink(IID_ITfThreadMgrEventSink, this, &m_dwCookie) == S_OK)
373 {
374 m_pThreadMgr = pThreadMgr;
375 pThreadMgr->AddRef();
376 hr = S_OK;
377 }
378
379 if (pSource)
380 pSource->Release();
381
382 return hr;
383 }
384
_Unadvise()385 HRESULT CThreadMgrEventSink::_Unadvise()
386 {
387 HRESULT hr = E_FAIL;
388 ITfSource *pSource = NULL;
389
390 if (m_pThreadMgr)
391 {
392 if (m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK &&
393 pSource->UnadviseSink(m_dwCookie) == S_OK)
394 {
395 hr = S_OK;
396 }
397
398 if (pSource)
399 pSource->Release();
400 }
401
402 if (m_pThreadMgr)
403 {
404 m_pThreadMgr->Release();
405 m_pThreadMgr = NULL;
406 }
407
408 return hr;
409 }
410
411 /***********************************************************************/
412
413 /// @implemented
CActiveLanguageProfileNotifySink(_In_ FN_COMPARE fnCompare,_Inout_opt_ void * pUserData)414 CActiveLanguageProfileNotifySink::CActiveLanguageProfileNotifySink(
415 _In_ FN_COMPARE fnCompare,
416 _Inout_opt_ void *pUserData)
417 {
418 m_dwConnection = (DWORD)-1;
419 m_fnCompare = fnCompare;
420 m_cRefs = 1;
421 m_pUserData = pUserData;
422 }
423
424 /// @implemented
~CActiveLanguageProfileNotifySink()425 CActiveLanguageProfileNotifySink::~CActiveLanguageProfileNotifySink()
426 {
427 }
428
429 /// @implemented
QueryInterface(REFIID riid,LPVOID * ppvObj)430 STDMETHODIMP CActiveLanguageProfileNotifySink::QueryInterface(REFIID riid, LPVOID* ppvObj)
431 {
432 static const QITAB c_tab[] =
433 {
434 QITABENT(CActiveLanguageProfileNotifySink, ITfActiveLanguageProfileNotifySink),
435 { NULL }
436 };
437 return ::QISearch(this, c_tab, riid, ppvObj);
438 }
439
440 /// @implemented
STDMETHODIMP_(ULONG)441 STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::AddRef()
442 {
443 return ::InterlockedIncrement(&m_cRefs);
444 }
445
446 /// @implemented
STDMETHODIMP_(ULONG)447 STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::Release()
448 {
449 if (::InterlockedDecrement(&m_cRefs) == 0)
450 {
451 delete this;
452 return 0;
453 }
454 return m_cRefs;
455 }
456
457 /// @implemented
458 STDMETHODIMP
OnActivated(REFCLSID clsid,REFGUID guidProfile,BOOL fActivated)459 CActiveLanguageProfileNotifySink::OnActivated(
460 REFCLSID clsid,
461 REFGUID guidProfile,
462 BOOL fActivated)
463 {
464 if (!m_fnCompare)
465 return 0;
466
467 return m_fnCompare(clsid, guidProfile, fActivated, m_pUserData);
468 }
469
470 /// @implemented
471 HRESULT
_Advise(ITfThreadMgr * pThreadMgr)472 CActiveLanguageProfileNotifySink::_Advise(
473 ITfThreadMgr *pThreadMgr)
474 {
475 m_pThreadMgr = NULL;
476
477 ITfSource *pSource = NULL;
478 HRESULT hr = pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource);
479 if (FAILED(hr))
480 return E_FAIL;
481
482 hr = pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, this, &m_dwConnection);
483 if (SUCCEEDED(hr))
484 {
485 m_pThreadMgr = pThreadMgr;
486 pThreadMgr->AddRef();
487 hr = S_OK;
488 }
489 else
490 {
491 hr = E_FAIL;
492 }
493
494 if (pSource)
495 pSource->Release();
496
497 return hr;
498 }
499
500 /// @implemented
501 HRESULT
_Unadvise()502 CActiveLanguageProfileNotifySink::_Unadvise()
503 {
504 if (!m_pThreadMgr)
505 return E_FAIL;
506
507 ITfSource *pSource = NULL;
508 HRESULT hr = m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource);
509 if (SUCCEEDED(hr))
510 {
511 hr = pSource->UnadviseSink(m_dwConnection);
512 if (SUCCEEDED(hr))
513 hr = S_OK;
514 }
515
516 if (pSource)
517 pSource->Release();
518
519 if (m_pThreadMgr)
520 {
521 m_pThreadMgr->Release();
522 m_pThreadMgr = NULL;
523 }
524
525 return hr;
526 }
527