/* * PROJECT: ReactOS CTF * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Implementation of ITfDocumentMgr and IEnumTfContexts * COPYRIGHT: Copyright 2009 Aric Stewart, CodeWeavers * Copyright 2025 Katayama Hirofumi MZ */ #include #include #include #include #include #include #include // Cicero #include #include "documentmgr.h" #include "msctf_internal.h" #include WINE_DEFAULT_DEBUG_CHANNEL(msctf); EXTERN_C HRESULT EnumTfContext_Constructor(CDocumentMgr *mgr, IEnumTfContexts **ppOut); //////////////////////////////////////////////////////////////////////////// // CDocumentMgr CDocumentMgr::CDocumentMgr(ITfThreadMgrEventSink *threadMgrSink) : m_cRefs(1) , m_pCompartmentMgr(NULL) , m_pThreadMgrSink(threadMgrSink) { m_contextStack[1] = m_contextStack[0] = NULL; list_init(&m_transitoryExtensionSink); ITfDocumentMgr *pDocMgr = static_cast(this); ITfCompartmentMgr **ppCompMgr = static_cast(&m_pCompartmentMgr); CompartmentMgr_Constructor(pDocMgr, IID_IUnknown, reinterpret_cast(ppCompMgr)); } CDocumentMgr::~CDocumentMgr() { TRACE("destroying %p\n", this); ITfThreadMgr *tm = NULL; TF_GetThreadMgr(&tm); if (tm) { ThreadMgr_OnDocumentMgrDestruction(tm, static_cast(this)); tm->Release(); } if (m_contextStack[0]) { m_contextStack[0]->Release(); m_contextStack[0] = NULL; } if (m_contextStack[1]) { m_contextStack[1]->Release(); m_contextStack[1] = NULL; } free_sinks(&m_transitoryExtensionSink); if (m_pCompartmentMgr) { m_pCompartmentMgr->Release(); m_pCompartmentMgr = NULL; } } HRESULT CDocumentMgr::CreateInstance( _In_ ITfThreadMgrEventSink *pThreadMgrSink, _Out_ ITfDocumentMgr **ppOut) { if (!ppOut) { ERR("!ppOut\n"); return E_POINTER; } if (!pThreadMgrSink) { ERR("!pThreadMgrSink\n"); return E_INVALIDARG; } CDocumentMgr *This = new(cicNoThrow) CDocumentMgr(pThreadMgrSink); if (!This) { ERR("E_OUTOFMEMORY\n"); return E_OUTOFMEMORY; } *ppOut = static_cast(This); TRACE("returning %p\n", *ppOut); return S_OK; } STDMETHODIMP CDocumentMgr::QueryInterface(REFIID iid, LPVOID *ppvObject) { TRACE("%p -> (%s, %p)\n", this, wine_dbgstr_guid(&iid), ppvObject); *ppvObject = NULL; if (iid == IID_IUnknown || iid == IID_ITfDocumentMgr) *ppvObject = static_cast(this); else if (iid == IID_ITfSource) *ppvObject = static_cast(this); else if (iid == IID_ITfCompartmentMgr) *ppvObject = m_pCompartmentMgr; if (*ppvObject) { AddRef(); return S_OK; } ERR("E_NOINTERFACE: %s\n", wine_dbgstr_guid(&iid)); return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CDocumentMgr::AddRef() { TRACE("%p -> ()\n", this); return ::InterlockedIncrement(&m_cRefs); } STDMETHODIMP_(ULONG) CDocumentMgr::Release() { TRACE("%p -> ()\n", this); ULONG ret = ::InterlockedDecrement(&m_cRefs); if (!ret) delete this; return ret; } STDMETHODIMP CDocumentMgr::CreateContext( TfClientId tidOwner, DWORD dwFlags, IUnknown *punk, ITfContext **ppic, TfEditCookie *pecTextStore) { TRACE("%p -> (%d, 0x%lX, %p, %p, %p)\n", this, tidOwner, dwFlags, punk, ppic, pecTextStore); return Context_Constructor(tidOwner, punk, this, ppic, pecTextStore); } STDMETHODIMP CDocumentMgr::Push(ITfContext *pic) { TRACE("%p -> (%p)\n", this, pic); if (m_contextStack[1]) /* Full */ { ERR("TF_E_STACKFULL\n"); return TF_E_STACKFULL; } if (!pic) { ERR("!pic\n"); return E_INVALIDARG; } ITfContext *check; HRESULT hr = pic->QueryInterface(IID_ITfContext, reinterpret_cast(&check)); if (FAILED(hr)) { ERR("hr: 0x%lX\n", hr); return E_INVALIDARG; } if (!m_contextStack[0]) m_pThreadMgrSink->OnInitDocumentMgr(this); m_contextStack[1] = m_contextStack[0]; m_contextStack[0] = check; Context_Initialize(check, this); m_pThreadMgrSink->OnPushContext(check); return S_OK; } STDMETHODIMP CDocumentMgr::Pop(DWORD dwFlags) { TRACE("%p -> (0x%lX)\n", this, dwFlags); if (dwFlags == TF_POPF_ALL) { for (SIZE_T i = 0; i < _countof(m_contextStack); i++) { if (!m_contextStack[i]) continue; m_pThreadMgrSink->OnPopContext(m_contextStack[i]); Context_Uninitialize(m_contextStack[i]); m_contextStack[i]->Release(); m_contextStack[i] = NULL; } m_pThreadMgrSink->OnUninitDocumentMgr(this); return S_OK; } if (dwFlags) { ERR("E_INVALIDARG: 0x%lX\n", dwFlags); return E_INVALIDARG; } if (!m_contextStack[1]) // Cannot pop last context { ERR("!m_contextStack[1]\n"); return E_FAIL; } m_pThreadMgrSink->OnPopContext(m_contextStack[0]); Context_Uninitialize(m_contextStack[0]); if (m_contextStack[0]) m_contextStack[0]->Release(); m_contextStack[0] = m_contextStack[1]; m_contextStack[1] = NULL; if (!m_contextStack[0]) m_pThreadMgrSink->OnUninitDocumentMgr(this); return S_OK; } STDMETHODIMP CDocumentMgr::GetTop(ITfContext **ppic) { TRACE("%p -> (%p)\n", this, ppic); if (!ppic) { ERR("!ppic\n"); return E_INVALIDARG; } if (m_contextStack[0]) m_contextStack[0]->AddRef(); *ppic = m_contextStack[0]; return S_OK; } STDMETHODIMP CDocumentMgr::GetBase(ITfContext **ppic) { TRACE("%p -> (%p)\n", this, ppic); if (!ppic) { ERR("!ppic\n"); return E_INVALIDARG; } ITfContext *target = (m_contextStack[1] ? m_contextStack[1] : m_contextStack[0]); *ppic = target; if (target) target->AddRef(); return S_OK; } STDMETHODIMP CDocumentMgr::EnumContexts(IEnumTfContexts **ppEnum) { TRACE("%p -> (%p)\n", this, ppEnum); return EnumTfContext_Constructor(this, ppEnum); } STDMETHODIMP CDocumentMgr::AdviseSink(REFIID riid, IUnknown *punk, DWORD *pdwCookie) { TRACE("%p -> (%s, %p, %p)\n", this, wine_dbgstr_guid(&riid), punk, pdwCookie); if (!punk || !pdwCookie) return E_INVALIDARG; if (riid != IID_ITfTransitoryExtensionSink) { FIXME("E_NOTIMPL: %s\n", wine_dbgstr_guid(&riid)); return E_NOTIMPL; } return advise_sink(&m_transitoryExtensionSink, IID_ITfTransitoryExtensionSink, COOKIE_MAGIC_DMSINK, punk, pdwCookie); } STDMETHODIMP CDocumentMgr::UnadviseSink(DWORD pdwCookie) { TRACE("%p -> (%p)\n", this, pdwCookie); if (get_Cookie_magic(pdwCookie) != COOKIE_MAGIC_DMSINK) return E_INVALIDARG; return unadvise_sink(pdwCookie); } //////////////////////////////////////////////////////////////////////////// // CEnumTfContext CEnumTfContext::CEnumTfContext(_In_opt_ CDocumentMgr *mgr) : m_cRefs(1) , m_index(0) , m_pDocMgr(mgr) { if (mgr) mgr->AddRef(); } CEnumTfContext::~CEnumTfContext() { if (m_pDocMgr) { m_pDocMgr->Release(); m_pDocMgr = NULL; } } HRESULT CEnumTfContext::CreateInstance(_In_opt_ CDocumentMgr *mgr, _Out_ IEnumTfContexts **ppOut) { if (!ppOut) { ERR("!ppOut\n"); return E_POINTER; } CEnumTfContext *This = new(cicNoThrow) CEnumTfContext(mgr); if (This == NULL) { ERR("E_OUTOFMEMORY\n"); return E_OUTOFMEMORY; } *ppOut = static_cast(This); TRACE("returning %p\n", *ppOut); return S_OK; } STDMETHODIMP CEnumTfContext::QueryInterface(REFIID iid, LPVOID *ppvObject) { TRACE("%p -> (%s, %p)\n", this, wine_dbgstr_guid(&iid), ppvObject); *ppvObject = NULL; if (iid == IID_IUnknown || iid == IID_IEnumTfContexts) *ppvObject = static_cast(this); if (*ppvObject) { AddRef(); return S_OK; } WARN("E_NOINTERFACE: %s\n", wine_dbgstr_guid(&iid)); return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CEnumTfContext::AddRef() { TRACE("%p -> ()\n", this); return ::InterlockedIncrement(&m_cRefs); } STDMETHODIMP_(ULONG) CEnumTfContext::Release() { TRACE("%p -> ()\n", this); ULONG ret = ::InterlockedDecrement(&m_cRefs); if (!ret) delete this; return ret; } STDMETHODIMP CEnumTfContext::Next(ULONG ulCount, ITfContext **rgContext, ULONG *pcFetched) { TRACE("%p -> (%lu, %p, %p)\n",this, ulCount, rgContext, pcFetched); if (!rgContext) { ERR("!rgContext\n"); return E_POINTER; } ULONG fetched; for (fetched = 0; fetched < ulCount; ++fetched, ++m_index, ++rgContext) { if (m_index >= _countof(m_pDocMgr->m_contextStack)) break; if (!m_pDocMgr->m_contextStack[m_index]) break; *rgContext = m_pDocMgr->m_contextStack[m_index]; (*rgContext)->AddRef(); } if (pcFetched) *pcFetched = fetched; return (fetched == ulCount) ? S_OK : S_FALSE; } STDMETHODIMP CEnumTfContext::Skip(ULONG celt) { TRACE("%p -> (%lu)\n", this, celt); m_index += celt; return S_OK; } STDMETHODIMP CEnumTfContext::Reset() { TRACE("%p -> ()\n", this); m_index = 0; return S_OK; } STDMETHODIMP CEnumTfContext::Clone(IEnumTfContexts **ppenum) { TRACE("%p -> (%p)\n", this, ppenum); if (!ppenum) { ERR("!ppenum\n"); return E_POINTER; } CEnumTfContext *This = new(cicNoThrow) CEnumTfContext(m_pDocMgr); if (!This) { ERR("E_OUTOFMEMORY\n"); return E_OUTOFMEMORY; } This->m_index = m_index; *ppenum = This; return S_OK; } //////////////////////////////////////////////////////////////////////////// EXTERN_C HRESULT DocumentMgr_Constructor(ITfThreadMgrEventSink *pThreadMgrSink, ITfDocumentMgr **ppOut) { return CDocumentMgr::CreateInstance(pThreadMgrSink, ppOut); } EXTERN_C HRESULT EnumTfContext_Constructor(CDocumentMgr *mgr, IEnumTfContexts **ppOut) { return CEnumTfContext::CreateInstance(mgr, ppOut); }