Reactos
1/*
2 * PROJECT: ReactOS CTF
3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4 * PURPOSE: Implementation of ITfDocumentMgr and IEnumTfContexts
5 * COPYRIGHT: Copyright 2009 Aric Stewart, CodeWeavers
6 * Copyright 2025 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
7 */
8
9#include "precomp.h"
10#include "documentmgr.h"
11
12#include <wine/debug.h>
13WINE_DEFAULT_DEBUG_CHANNEL(msctf);
14
15////////////////////////////////////////////////////////////////////////////
16
17CDocumentMgr::CDocumentMgr(ITfThreadMgrEventSink *threadMgrSink)
18 : m_cRefs(1)
19 , m_pCompartmentMgr(NULL)
20 , m_initialContext(NULL)
21 , m_pThreadMgrSink(threadMgrSink)
22{
23 m_contextStack[1] = m_contextStack[0] = NULL;
24
25 list_init(&m_transitoryExtensionSink);
26
27 ITfDocumentMgr *pDocMgr = static_cast<ITfDocumentMgr *>(this);
28 ITfCompartmentMgr **ppCompMgr = static_cast<ITfCompartmentMgr **>(&m_pCompartmentMgr);
29 CompartmentMgr_Constructor(pDocMgr, IID_IUnknown, reinterpret_cast<IUnknown **>(ppCompMgr));
30
31 DWORD cookie;
32 Context_Constructor(g_processId, NULL, pDocMgr, &m_initialContext, &cookie);
33}
34
35CDocumentMgr::~CDocumentMgr()
36{
37 TRACE("destroying %p\n", this);
38
39 ITfThreadMgr *tm = NULL;
40 TF_GetThreadMgr(&tm);
41 if (tm)
42 {
43 ThreadMgr_OnDocumentMgrDestruction(tm, static_cast<ITfDocumentMgr*>(this));
44 tm->Release();
45 }
46
47 if (m_initialContext)
48 m_initialContext->Release();
49 if (m_contextStack[0])
50 m_contextStack[0]->Release();
51 if (m_contextStack[1])
52 m_contextStack[1]->Release();
53
54 free_sinks(&m_transitoryExtensionSink);
55
56 if (m_pCompartmentMgr)
57 {
58 m_pCompartmentMgr->Release();
59 m_pCompartmentMgr = NULL;
60 }
61}
62
63HRESULT
64CDocumentMgr::CreateInstance(
65 _In_ ITfThreadMgrEventSink *pThreadMgrSink,
66 _Out_ ITfDocumentMgr **ppOut)
67{
68 if (!ppOut)
69 {
70 ERR("!ppOut\n");
71 return E_POINTER;
72 }
73
74 if (!pThreadMgrSink)
75 {
76 ERR("!pThreadMgrSink\n");
77 return E_INVALIDARG;
78 }
79
80 CDocumentMgr *This = new(cicNoThrow) CDocumentMgr(pThreadMgrSink);
81 if (!This)
82 {
83 ERR("E_OUTOFMEMORY\n");
84 return E_OUTOFMEMORY;
85 }
86
87 *ppOut = static_cast<ITfDocumentMgr *>(This);
88 TRACE("returning %p\n", *ppOut);
89 return S_OK;
90}
91
92STDMETHODIMP CDocumentMgr::QueryInterface(REFIID iid, LPVOID *ppvObject)
93{
94 TRACE("%p -> (%s, %p)\n", this, wine_dbgstr_guid(&iid), ppvObject);
95 *ppvObject = NULL;
96
97 IUnknown *pUnk = NULL;
98 if (iid == IID_IUnknown || iid == IID_ITfDocumentMgr)
99 pUnk = static_cast<ITfDocumentMgr *>(this);
100 else if (iid == IID_ITfSource)
101 pUnk = static_cast<ITfSource *>(this);
102 else if (iid == IID_ITfCompartmentMgr)
103 pUnk = m_pCompartmentMgr;
104
105 if (pUnk)
106 {
107 pUnk->AddRef();
108 *ppvObject = pUnk;
109 return S_OK;
110 }
111
112 WARN("unsupported interface: %s\n", debugstr_guid(&iid));
113 return E_NOINTERFACE;
114}
115
116STDMETHODIMP_(ULONG) CDocumentMgr::AddRef()
117{
118 TRACE("%p -> ()\n", this);
119 return ::InterlockedIncrement(&m_cRefs);
120}
121
122STDMETHODIMP_(ULONG) CDocumentMgr::Release()
123{
124 TRACE("%p -> ()\n", this);
125 ULONG ret = ::InterlockedDecrement(&m_cRefs);
126 if (!ret)
127 delete this;
128 return ret;
129}
130
131STDMETHODIMP
132CDocumentMgr::CreateContext(
133 TfClientId tidOwner,
134 DWORD dwFlags,
135 IUnknown *punk,
136 ITfContext **ppic,
137 TfEditCookie *pecTextStore)
138{
139 TRACE("%p -> (%d, 0x%lX, %p, %p, %p)\n", this, tidOwner, dwFlags, punk, ppic, pecTextStore);
140 return Context_Constructor(tidOwner, punk, this, ppic, pecTextStore);
141}
142
143STDMETHODIMP CDocumentMgr::Push(ITfContext *pic)
144{
145 TRACE("%p -> (%p)\n", this, pic);
146
147 if (m_contextStack[1]) /* Full */
148 {
149 ERR("TF_E_STACKFULL\n");
150 return TF_E_STACKFULL;
151 }
152
153 if (!pic)
154 {
155 ERR("!pic\n");
156 return E_INVALIDARG;
157 }
158
159 ITfContext *check;
160 HRESULT hr = pic->QueryInterface(IID_ITfContext, reinterpret_cast<LPVOID *>(&check));
161 if (FAILED(hr))
162 {
163 ERR("hr: 0x%lX\n", hr);
164 return E_INVALIDARG;
165 }
166
167 if (!m_contextStack[0])
168 m_pThreadMgrSink->OnInitDocumentMgr(this);
169
170 m_contextStack[1] = m_contextStack[0];
171 m_contextStack[0] = check;
172
173 Context_Initialize(check, this);
174 m_pThreadMgrSink->OnPushContext(check);
175
176 return S_OK;
177}
178
179STDMETHODIMP CDocumentMgr::Pop(DWORD dwFlags)
180{
181 TRACE("%p -> (0x%lX)\n", this, dwFlags);
182
183 if (dwFlags == TF_POPF_ALL)
184 {
185 for (SIZE_T i = 0; i < _countof(m_contextStack); i++)
186 {
187 if (!m_contextStack[i])
188 continue;
189
190 m_pThreadMgrSink->OnPopContext(m_contextStack[i]);
191 Context_Uninitialize(m_contextStack[i]);
192 m_contextStack[i]->Release();
193 m_contextStack[i] = NULL;
194 }
195
196 m_pThreadMgrSink->OnUninitDocumentMgr(this);
197 return S_OK;
198 }
199
200 if (dwFlags)
201 {
202 ERR("E_INVALIDARG: 0x%lX\n", dwFlags);
203 return E_INVALIDARG;
204 }
205
206 if (!m_contextStack[1]) // Cannot pop last context
207 {
208 ERR("!m_contextStack[1]\n");
209 return E_FAIL;
210 }
211
212 m_pThreadMgrSink->OnPopContext(m_contextStack[0]);
213 Context_Uninitialize(m_contextStack[0]);
214
215 if (m_contextStack[0])
216 m_contextStack[0]->Release();
217
218 m_contextStack[0] = m_contextStack[1];
219 m_contextStack[1] = NULL;
220
221 if (!m_contextStack[0])
222 m_pThreadMgrSink->OnUninitDocumentMgr(this);
223
224 return S_OK;
225}
226
227STDMETHODIMP CDocumentMgr::GetTop(ITfContext **ppic)
228{
229 TRACE("%p -> (%p)\n", this, ppic);
230
231 if (!ppic)
232 {
233 ERR("!ppic\n");
234 return E_INVALIDARG;
235 }
236
237 ITfContext *target;
238 if (m_contextStack[0])
239 target = m_contextStack[0];
240 else
241 target = m_initialContext;
242
243 if (target)
244 target->AddRef();
245
246 *ppic = target;
247 return S_OK;
248}
249
250STDMETHODIMP CDocumentMgr::GetBase(ITfContext **ppic)
251{
252 TRACE("%p -> (%p)\n", this, ppic);
253
254 if (!ppic)
255 {
256 ERR("!ppic\n");
257 return E_INVALIDARG;
258 }
259
260 ITfContext *target;
261 if (m_contextStack[1])
262 target = m_contextStack[1];
263 else if (m_contextStack[0])
264 target = m_contextStack[0];
265 else
266 target = m_initialContext;
267
268 if (target)
269 target->AddRef();
270
271 *ppic = target;
272 return S_OK;
273}
274
275STDMETHODIMP CDocumentMgr::EnumContexts(IEnumTfContexts **ppEnum)
276{
277 TRACE("%p -> (%p)\n", this, ppEnum);
278 return EnumTfContext_Constructor(this, ppEnum);
279}
280
281STDMETHODIMP CDocumentMgr::AdviseSink(REFIID riid, IUnknown *punk, DWORD *pdwCookie)
282{
283 TRACE("%p -> (%s, %p, %p)\n", this, wine_dbgstr_guid(&riid), punk, pdwCookie);
284
285 if (cicIsNullPtr(&riid) || !punk || !pdwCookie)
286 return E_INVALIDARG;
287
288 if (riid == IID_ITfTransitoryExtensionSink)
289 {
290 WARN("semi-stub for ITfTransitoryExtensionSink: callback won't be used.\n");
291 return advise_sink(&m_transitoryExtensionSink, IID_ITfTransitoryExtensionSink,
292 COOKIE_MAGIC_DMSINK, punk, pdwCookie);
293 }
294
295 FIXME("(%p) Unhandled Sink: %s\n", this, debugstr_guid(&riid));
296 return E_NOTIMPL;
297}
298
299STDMETHODIMP CDocumentMgr::UnadviseSink(DWORD pdwCookie)
300{
301 TRACE("%p -> (%p)\n", this, pdwCookie);
302
303 if (get_Cookie_magic(pdwCookie) != COOKIE_MAGIC_DMSINK)
304 return E_INVALIDARG;
305
306 return unadvise_sink(pdwCookie);
307}
308
309////////////////////////////////////////////////////////////////////////////
310
311CEnumTfContext::CEnumTfContext(_In_opt_ CDocumentMgr *mgr)
312 : m_cRefs(1)
313 , m_index(0)
314 , m_pDocMgr(mgr)
315{
316 if (mgr)
317 mgr->AddRef();
318}
319
320CEnumTfContext::~CEnumTfContext()
321{
322 if (m_pDocMgr)
323 {
324 m_pDocMgr->Release();
325 m_pDocMgr = NULL;
326 }
327}
328
329HRESULT CEnumTfContext::CreateInstance(_In_opt_ CDocumentMgr *mgr, _Out_ IEnumTfContexts **ppOut)
330{
331 if (!ppOut)
332 {
333 ERR("!ppOut\n");
334 return E_POINTER;
335 }
336
337 CEnumTfContext *This = new(cicNoThrow) CEnumTfContext(mgr);
338 if (!This)
339 {
340 ERR("E_OUTOFMEMORY\n");
341 return E_OUTOFMEMORY;
342 }
343
344 *ppOut = static_cast<IEnumTfContexts *>(This);
345 TRACE("returning %p\n", *ppOut);
346 return S_OK;
347}
348
349STDMETHODIMP CEnumTfContext::QueryInterface(REFIID iid, LPVOID *ppvObject)
350{
351 TRACE("%p -> (%s, %p)\n", this, wine_dbgstr_guid(&iid), ppvObject);
352
353 *ppvObject = NULL;
354
355 if (iid == IID_IUnknown || iid == IID_IEnumTfContexts)
356 *ppvObject = static_cast<IEnumTfContexts *>(this);
357
358 if (*ppvObject)
359 {
360 AddRef();
361 return S_OK;
362 }
363
364 WARN("E_NOINTERFACE: %s\n", wine_dbgstr_guid(&iid));
365 return E_NOINTERFACE;
366}
367
368STDMETHODIMP_(ULONG) CEnumTfContext::AddRef()
369{
370 TRACE("%p -> ()\n", this);
371 return ::InterlockedIncrement(&m_cRefs);
372}
373
374STDMETHODIMP_(ULONG) CEnumTfContext::Release()
375{
376 TRACE("%p -> ()\n", this);
377 ULONG ret = ::InterlockedDecrement(&m_cRefs);
378 if (!ret)
379 delete this;
380 return ret;
381}
382
383STDMETHODIMP CEnumTfContext::Next(ULONG ulCount, ITfContext **rgContext, ULONG *pcFetched)
384{
385 TRACE("%p -> (%lu, %p, %p)\n",this, ulCount, rgContext, pcFetched);
386
387 if (!rgContext)
388 {
389 ERR("!rgContext\n");
390 return E_POINTER;
391 }
392
393 ULONG fetched;
394 for (fetched = 0; fetched < ulCount; ++fetched, ++m_index, ++rgContext)
395 {
396 if (m_index >= _countof(m_pDocMgr->m_contextStack))
397 break;
398
399 if (!m_pDocMgr->m_contextStack[m_index])
400 break;
401
402 *rgContext = m_pDocMgr->m_contextStack[m_index];
403 (*rgContext)->AddRef();
404 }
405
406 if (pcFetched)
407 *pcFetched = fetched;
408
409 return (fetched == ulCount) ? S_OK : S_FALSE;
410}
411
412STDMETHODIMP CEnumTfContext::Skip(ULONG celt)
413{
414 TRACE("%p -> (%lu)\n", this, celt);
415 m_index += celt;
416 return S_OK;
417}
418
419STDMETHODIMP CEnumTfContext::Reset()
420{
421 TRACE("%p -> ()\n", this);
422 m_index = 0;
423 return S_OK;
424}
425
426STDMETHODIMP CEnumTfContext::Clone(IEnumTfContexts **ppenum)
427{
428 TRACE("%p -> (%p)\n", this, ppenum);
429
430 if (!ppenum)
431 {
432 ERR("!ppenum\n");
433 return E_POINTER;
434 }
435
436 CEnumTfContext *This = new(cicNoThrow) CEnumTfContext(m_pDocMgr);
437 if (!This)
438 {
439 ERR("E_OUTOFMEMORY\n");
440 return E_OUTOFMEMORY;
441 }
442
443 This->m_index = m_index;
444 *ppenum = This;
445 return S_OK;
446}
447
448////////////////////////////////////////////////////////////////////////////
449
450EXTERN_C
451HRESULT DocumentMgr_Constructor(ITfThreadMgrEventSink *pThreadMgrSink, ITfDocumentMgr **ppOut)
452{
453 return CDocumentMgr::CreateInstance(pThreadMgrSink, ppOut);
454}
455
456EXTERN_C
457HRESULT EnumTfContext_Constructor(CDocumentMgr *mgr, IEnumTfContexts **ppOut)
458{
459 return CEnumTfContext::CreateInstance(mgr, ppOut);
460}