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: The sinks 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/// @implemented
13CCompartmentEventSink::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
22CCompartmentEventSink::~CCompartmentEventSink()
23{
24}
25
26/// @implemented
27STDMETHODIMP 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
38STDMETHODIMP_(ULONG) CCompartmentEventSink::AddRef()
39{
40 return ::InterlockedIncrement(&m_cRefs);
41}
42
43/// @implemented
44STDMETHODIMP_(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
55STDMETHODIMP CCompartmentEventSink::OnChange(REFGUID rguid)
56{
57 return m_fnEventSink(m_pUserData, rguid);
58}
59
60/// @implemented
61HRESULT
62CCompartmentEventSink::_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
100HRESULT 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
133CTextEventSink::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
145CTextEventSink::~CTextEventSink()
146{
147}
148
149/// @implemented
150STDMETHODIMP 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
162STDMETHODIMP_(ULONG) CTextEventSink::AddRef()
163{
164 return ::InterlockedIncrement(&m_cRefs);
165}
166
167/// @implemented
168STDMETHODIMP_(ULONG) CTextEventSink::Release()
169{
170 if (::InterlockedDecrement(&m_cRefs) == 0)
171 {
172 delete this;
173 return 0;
174 }
175 return m_cRefs;
176}
177
178struct TEXT_EVENT_SINK_END_EDIT
179{
180 TfEditCookie m_ecReadOnly;
181 ITfEditRecord *m_pEditRecord;
182 ITfContext *m_pContext;
183};
184
185/// @implemented
186STDMETHODIMP 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
196STDMETHODIMP 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
215HRESULT 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
248HRESULT 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
274CThreadMgrEventSink::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
286STDMETHODIMP 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
297STDMETHODIMP_(ULONG) CThreadMgrEventSink::AddRef()
298{
299 return ::InterlockedIncrement(&m_cRefs);
300}
301
302/// @implemented
303STDMETHODIMP_(ULONG) CThreadMgrEventSink::Release()
304{
305 if (::InterlockedDecrement(&m_cRefs) == 0)
306 {
307 delete this;
308 return 0;
309 }
310 return m_cRefs;
311}
312
313INT CALLBACK
314CThreadMgrEventSink::DIMCallback(
315 UINT nCode,
316 ITfDocumentMgr *pDocMgr1,
317 ITfDocumentMgr *pDocMgr2,
318 LPVOID pUserData)
319{
320 return E_NOTIMPL;
321}
322
323STDMETHODIMP CThreadMgrEventSink::OnInitDocumentMgr(ITfDocumentMgr *pdim)
324{
325 if (!m_fnInit)
326 return S_OK;
327 return m_fnInit(0, pdim, NULL, m_pCallbackPV);
328}
329
330STDMETHODIMP CThreadMgrEventSink::OnUninitDocumentMgr(ITfDocumentMgr *pdim)
331{
332 if (!m_fnInit)
333 return S_OK;
334 return m_fnInit(1, pdim, NULL, m_pCallbackPV);
335}
336
337STDMETHODIMP
338CThreadMgrEventSink::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
345STDMETHODIMP CThreadMgrEventSink::OnPushContext(ITfContext *pic)
346{
347 if (!m_fnPushPop)
348 return S_OK;
349 return m_fnPushPop(3, pic, m_pCallbackPV);
350}
351
352STDMETHODIMP CThreadMgrEventSink::OnPopContext(ITfContext *pic)
353{
354 if (!m_fnPushPop)
355 return S_OK;
356 return m_fnPushPop(4, pic, m_pCallbackPV);
357}
358
359void CThreadMgrEventSink::SetCallbackPV(_Inout_ LPVOID pv)
360{
361 if (!m_pCallbackPV)
362 m_pCallbackPV = pv;
363}
364
365HRESULT 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
385HRESULT 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
414CActiveLanguageProfileNotifySink::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
425CActiveLanguageProfileNotifySink::~CActiveLanguageProfileNotifySink()
426{
427}
428
429/// @implemented
430STDMETHODIMP 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
441STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::AddRef()
442{
443 return ::InterlockedIncrement(&m_cRefs);
444}
445
446/// @implemented
447STDMETHODIMP_(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
458STDMETHODIMP
459CActiveLanguageProfileNotifySink::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
471HRESULT
472CActiveLanguageProfileNotifySink::_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
501HRESULT
502CActiveLanguageProfileNotifySink::_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}