| /* |
| * ITfContext implementation |
| * |
| * Copyright 2009 Aric Stewart, CodeWeavers |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| |
| #include "wine/debug.h" |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winreg.h" |
| #include "winuser.h" |
| #include "shlwapi.h" |
| #include "winerror.h" |
| #include "objbase.h" |
| #include "olectl.h" |
| |
| #include "wine/unicode.h" |
| #include "wine/list.h" |
| |
| #include "msctf.h" |
| #include "msctf_internal.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(msctf); |
| |
| typedef struct tagContextSink { |
| struct list entry; |
| union { |
| /* Context Sinks */ |
| IUnknown *pIUnknown; |
| /* ITfContextKeyEventSink *pITfContextKeyEventSink; */ |
| /* ITfEditTransactionSink *pITfEditTransactionSink; */ |
| /* ITfStatusSink *pITfStatusSink; */ |
| ITfTextEditSink *pITfTextEditSink; |
| /* ITfTextLayoutSink *pITfTextLayoutSink; */ |
| } interfaces; |
| } ContextSink; |
| |
| typedef struct tagContext { |
| const ITfContextVtbl *ContextVtbl; |
| const ITfSourceVtbl *SourceVtbl; |
| /* const ITfContextCompositionVtbl *ContextCompositionVtbl; */ |
| /* const ITfContextOwnerCompositionServicesVtbl *ContextOwnerCompositionServicesVtbl; */ |
| /* const ITfContextOwnerServicesVtbl *ContextOwnerServicesVtbl; */ |
| const ITfInsertAtSelectionVtbl *InsertAtSelectionVtbl; |
| /* const ITfMouseTrackerVtbl *MouseTrackerVtbl; */ |
| /* const ITfQueryEmbeddedVtbl *QueryEmbeddedVtbl; */ |
| const ITfSourceSingleVtbl *SourceSingleVtbl; |
| LONG refCount; |
| BOOL connected; |
| |
| /* Aggregation */ |
| ITfCompartmentMgr *CompartmentMgr; |
| |
| TfClientId tidOwner; |
| TfEditCookie defaultCookie; |
| TS_STATUS documentStatus; |
| ITfDocumentMgr *manager; |
| |
| ITextStoreACP *pITextStoreACP; |
| ITfContextOwnerCompositionSink *pITfContextOwnerCompositionSink; |
| |
| ITextStoreACPSink *pITextStoreACPSink; |
| ITfEditSession* currentEditSession; |
| |
| /* kept as separate lists to reduce unnecessary iterations */ |
| struct list pContextKeyEventSink; |
| struct list pEditTransactionSink; |
| struct list pStatusSink; |
| struct list pTextEditSink; |
| struct list pTextLayoutSink; |
| |
| } Context; |
| |
| typedef struct tagEditCookie { |
| DWORD lockType; |
| Context *pOwningContext; |
| } EditCookie; |
| |
| typedef struct tagTextStoreACPSink { |
| const ITextStoreACPSinkVtbl *TextStoreACPSinkVtbl; |
| /* const ITextStoreACPServicesVtbl *TextStoreACPServicesVtbl; */ |
| LONG refCount; |
| |
| Context *pContext; |
| } TextStoreACPSink; |
| |
| |
| static HRESULT TextStoreACPSink_Constructor(ITextStoreACPSink **ppOut, Context *pContext); |
| |
| static inline Context *impl_from_ITfSourceVtbl(ITfSource *iface) |
| { |
| return (Context *)((char *)iface - FIELD_OFFSET(Context,SourceVtbl)); |
| } |
| |
| static inline Context *impl_from_ITfInsertAtSelectionVtbl(ITfInsertAtSelection*iface) |
| { |
| return (Context *)((char *)iface - FIELD_OFFSET(Context,InsertAtSelectionVtbl)); |
| } |
| |
| static inline Context *impl_from_ITfSourceSingleVtbl(ITfSourceSingle* iface) |
| { |
| return (Context *)((char *)iface - FIELD_OFFSET(Context,SourceSingleVtbl)); |
| } |
| |
| static void free_sink(ContextSink *sink) |
| { |
| IUnknown_Release(sink->interfaces.pIUnknown); |
| HeapFree(GetProcessHeap(),0,sink); |
| } |
| |
| static void Context_Destructor(Context *This) |
| { |
| struct list *cursor, *cursor2; |
| EditCookie *cookie; |
| TRACE("destroying %p\n", This); |
| |
| if (This->pITextStoreACPSink) |
| { |
| ITextStoreACP_UnadviseSink(This->pITextStoreACP, (IUnknown*)This->pITextStoreACPSink); |
| ITextStoreACPSink_Release(This->pITextStoreACPSink); |
| } |
| |
| if (This->pITextStoreACP) |
| ITextStoreACP_Release(This->pITextStoreACP); |
| |
| if (This->pITfContextOwnerCompositionSink) |
| ITfContextOwnerCompositionSink_Release(This->pITfContextOwnerCompositionSink); |
| |
| if (This->defaultCookie) |
| { |
| cookie = remove_Cookie(This->defaultCookie); |
| HeapFree(GetProcessHeap(),0,cookie); |
| This->defaultCookie = 0; |
| } |
| |
| LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pContextKeyEventSink) |
| { |
| ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry); |
| list_remove(cursor); |
| free_sink(sink); |
| } |
| LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pEditTransactionSink) |
| { |
| ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry); |
| list_remove(cursor); |
| free_sink(sink); |
| } |
| LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pStatusSink) |
| { |
| ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry); |
| list_remove(cursor); |
| free_sink(sink); |
| } |
| LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pTextEditSink) |
| { |
| ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry); |
| list_remove(cursor); |
| free_sink(sink); |
| } |
| LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pTextLayoutSink) |
| { |
| ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry); |
| list_remove(cursor); |
| free_sink(sink); |
| } |
| |
| CompartmentMgr_Destructor(This->CompartmentMgr); |
| HeapFree(GetProcessHeap(),0,This); |
| } |
| |
| static HRESULT WINAPI Context_QueryInterface(ITfContext *iface, REFIID iid, LPVOID *ppvOut) |
| { |
| Context *This = (Context *)iface; |
| *ppvOut = NULL; |
| |
| if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfContext)) |
| { |
| *ppvOut = This; |
| } |
| else if (IsEqualIID(iid, &IID_ITfSource)) |
| { |
| *ppvOut = &This->SourceVtbl; |
| } |
| else if (IsEqualIID(iid, &IID_ITfInsertAtSelection)) |
| { |
| *ppvOut = &This->InsertAtSelectionVtbl; |
| } |
| else if (IsEqualIID(iid, &IID_ITfCompartmentMgr)) |
| { |
| *ppvOut = This->CompartmentMgr; |
| } |
| else if (IsEqualIID(iid, &IID_ITfSourceSingle)) |
| { |
| *ppvOut = &This->SourceSingleVtbl; |
| } |
| |
| if (*ppvOut) |
| { |
| ITfContext_AddRef(iface); |
| return S_OK; |
| } |
| |
| WARN("unsupported interface: %s\n", debugstr_guid(iid)); |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI Context_AddRef(ITfContext *iface) |
| { |
| Context *This = (Context *)iface; |
| return InterlockedIncrement(&This->refCount); |
| } |
| |
| static ULONG WINAPI Context_Release(ITfContext *iface) |
| { |
| Context *This = (Context *)iface; |
| ULONG ret; |
| |
| ret = InterlockedDecrement(&This->refCount); |
| if (ret == 0) |
| Context_Destructor(This); |
| return ret; |
| } |
| |
| /***************************************************** |
| * ITfContext functions |
| *****************************************************/ |
| static HRESULT WINAPI Context_RequestEditSession (ITfContext *iface, |
| TfClientId tid, ITfEditSession *pes, DWORD dwFlags, |
| HRESULT *phrSession) |
| { |
| HRESULT hr; |
| Context *This = (Context *)iface; |
| DWORD dwLockFlags = 0x0; |
| |
| TRACE("(%p) %i %p %x %p\n",This, tid, pes, dwFlags, phrSession); |
| |
| if (!(dwFlags & TF_ES_READ) && !(dwFlags & TF_ES_READWRITE)) |
| { |
| *phrSession = E_FAIL; |
| return E_INVALIDARG; |
| } |
| |
| if (!This->pITextStoreACP) |
| { |
| FIXME("No ITextStoreACP available\n"); |
| *phrSession = E_FAIL; |
| return E_FAIL; |
| } |
| |
| if (!(dwFlags & TF_ES_ASYNC)) |
| dwLockFlags |= TS_LF_SYNC; |
| |
| if ((dwFlags & TF_ES_READWRITE) == TF_ES_READWRITE) |
| dwLockFlags |= TS_LF_READWRITE; |
| else if (dwFlags & TF_ES_READ) |
| dwLockFlags |= TS_LF_READ; |
| |
| if (!This->documentStatus.dwDynamicFlags) |
| ITextStoreACP_GetStatus(This->pITextStoreACP, &This->documentStatus); |
| |
| if (((dwFlags & TF_ES_READWRITE) == TF_ES_READWRITE) && (This->documentStatus.dwDynamicFlags & TS_SD_READONLY)) |
| { |
| *phrSession = TS_E_READONLY; |
| return S_OK; |
| } |
| |
| if (FAILED (ITfEditSession_QueryInterface(pes, &IID_ITfEditSession, (LPVOID*)&This->currentEditSession))) |
| { |
| *phrSession = E_FAIL; |
| return E_INVALIDARG; |
| } |
| |
| hr = ITextStoreACP_RequestLock(This->pITextStoreACP, dwLockFlags, phrSession); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI Context_InWriteSession (ITfContext *iface, |
| TfClientId tid, |
| BOOL *pfWriteSession) |
| { |
| Context *This = (Context *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI Context_GetSelection (ITfContext *iface, |
| TfEditCookie ec, ULONG ulIndex, ULONG ulCount, |
| TF_SELECTION *pSelection, ULONG *pcFetched) |
| { |
| Context *This = (Context *)iface; |
| EditCookie *cookie; |
| ULONG count, i; |
| ULONG totalFetched = 0; |
| HRESULT hr = S_OK; |
| |
| if (!pSelection || !pcFetched) |
| return E_INVALIDARG; |
| |
| *pcFetched = 0; |
| |
| if (!This->connected) |
| return TF_E_DISCONNECTED; |
| |
| if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE) |
| return TF_E_NOLOCK; |
| |
| if (!This->pITextStoreACP) |
| { |
| FIXME("Context does not have a ITextStoreACP\n"); |
| return E_NOTIMPL; |
| } |
| |
| cookie = get_Cookie_data(ec); |
| |
| if (ulIndex == TF_DEFAULT_SELECTION) |
| count = 1; |
| else |
| count = ulCount; |
| |
| for (i = 0; i < count; i++) |
| { |
| DWORD fetched; |
| TS_SELECTION_ACP acps; |
| |
| hr = ITextStoreACP_GetSelection(This->pITextStoreACP, ulIndex + i, |
| 1, &acps, &fetched); |
| |
| if (hr == TS_E_NOLOCK) |
| return TF_E_NOLOCK; |
| else if (SUCCEEDED(hr)) |
| { |
| pSelection[totalFetched].style.ase = acps.style.ase; |
| pSelection[totalFetched].style.fInterimChar = acps.style.fInterimChar; |
| Range_Constructor(iface, This->pITextStoreACP, cookie->lockType, acps.acpStart, acps.acpEnd, &pSelection[totalFetched].range); |
| totalFetched ++; |
| } |
| else |
| break; |
| } |
| |
| *pcFetched = totalFetched; |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI Context_SetSelection (ITfContext *iface, |
| TfEditCookie ec, ULONG ulCount, const TF_SELECTION *pSelection) |
| { |
| TS_SELECTION_ACP *acp; |
| Context *This = (Context *)iface; |
| INT i; |
| HRESULT hr; |
| |
| TRACE("(%p) %i %i %p\n",This,ec,ulCount,pSelection); |
| |
| if (!This->pITextStoreACP) |
| { |
| FIXME("Context does not have a ITextStoreACP\n"); |
| return E_NOTIMPL; |
| } |
| |
| if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE) |
| return TF_E_NOLOCK; |
| |
| acp = HeapAlloc(GetProcessHeap(), 0, sizeof(TS_SELECTION_ACP) * ulCount); |
| if (!acp) |
| return E_OUTOFMEMORY; |
| |
| for (i = 0; i < ulCount; i++) |
| if (FAILED(TF_SELECTION_to_TS_SELECTION_ACP(&pSelection[i], &acp[i]))) |
| { |
| TRACE("Selection Conversion Failed\n"); |
| HeapFree(GetProcessHeap(), 0 , acp); |
| return E_FAIL; |
| } |
| |
| hr = ITextStoreACP_SetSelection(This->pITextStoreACP, ulCount, acp); |
| |
| HeapFree(GetProcessHeap(), 0, acp); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI Context_GetStart (ITfContext *iface, |
| TfEditCookie ec, ITfRange **ppStart) |
| { |
| Context *This = (Context *)iface; |
| EditCookie *cookie; |
| TRACE("(%p) %i %p\n",This,ec,ppStart); |
| |
| if (!ppStart) |
| return E_INVALIDARG; |
| |
| *ppStart = NULL; |
| |
| if (!This->connected) |
| return TF_E_DISCONNECTED; |
| |
| if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE) |
| return TF_E_NOLOCK; |
| |
| cookie = get_Cookie_data(ec); |
| return Range_Constructor(iface, This->pITextStoreACP, cookie->lockType, 0, 0, ppStart); |
| } |
| |
| static HRESULT WINAPI Context_GetEnd (ITfContext *iface, |
| TfEditCookie ec, ITfRange **ppEnd) |
| { |
| Context *This = (Context *)iface; |
| EditCookie *cookie; |
| LONG end; |
| TRACE("(%p) %i %p\n",This,ec,ppEnd); |
| |
| if (!ppEnd) |
| return E_INVALIDARG; |
| |
| *ppEnd = NULL; |
| |
| if (!This->connected) |
| return TF_E_DISCONNECTED; |
| |
| if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE) |
| return TF_E_NOLOCK; |
| |
| if (!This->pITextStoreACP) |
| { |
| FIXME("Context does not have a ITextStoreACP\n"); |
| return E_NOTIMPL; |
| } |
| |
| cookie = get_Cookie_data(ec); |
| ITextStoreACP_GetEndACP(This->pITextStoreACP,&end); |
| |
| return Range_Constructor(iface, This->pITextStoreACP, cookie->lockType, end, end, ppEnd); |
| } |
| |
| static HRESULT WINAPI Context_GetActiveView (ITfContext *iface, |
| ITfContextView **ppView) |
| { |
| Context *This = (Context *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI Context_EnumViews (ITfContext *iface, |
| IEnumTfContextViews **ppEnum) |
| { |
| Context *This = (Context *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI Context_GetStatus (ITfContext *iface, |
| TF_STATUS *pdcs) |
| { |
| Context *This = (Context *)iface; |
| TRACE("(%p) %p\n",This,pdcs); |
| |
| if (!This->connected) |
| return TF_E_DISCONNECTED; |
| |
| if (!pdcs) |
| return E_INVALIDARG; |
| |
| if (!This->pITextStoreACP) |
| { |
| FIXME("Context does not have a ITextStoreACP\n"); |
| return E_NOTIMPL; |
| } |
| |
| ITextStoreACP_GetStatus(This->pITextStoreACP, &This->documentStatus); |
| |
| *pdcs = This->documentStatus; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI Context_GetProperty (ITfContext *iface, |
| REFGUID guidProp, ITfProperty **ppProp) |
| { |
| Context *This = (Context *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI Context_GetAppProperty (ITfContext *iface, |
| REFGUID guidProp, ITfReadOnlyProperty **ppProp) |
| { |
| Context *This = (Context *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI Context_TrackProperties (ITfContext *iface, |
| const GUID **prgProp, ULONG cProp, const GUID **prgAppProp, |
| ULONG cAppProp, ITfReadOnlyProperty **ppProperty) |
| { |
| Context *This = (Context *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI Context_EnumProperties (ITfContext *iface, |
| IEnumTfProperties **ppEnum) |
| { |
| Context *This = (Context *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI Context_GetDocumentMgr (ITfContext *iface, |
| ITfDocumentMgr **ppDm) |
| { |
| Context *This = (Context *)iface; |
| TRACE("(%p) %p\n",This,ppDm); |
| |
| if (!ppDm) |
| return E_INVALIDARG; |
| |
| *ppDm = This->manager; |
| if (!This->manager) |
| return S_FALSE; |
| |
| ITfDocumentMgr_AddRef(This->manager); |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI Context_CreateRangeBackup (ITfContext *iface, |
| TfEditCookie ec, ITfRange *pRange, ITfRangeBackup **ppBackup) |
| { |
| Context *This = (Context *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static const ITfContextVtbl Context_ContextVtbl = |
| { |
| Context_QueryInterface, |
| Context_AddRef, |
| Context_Release, |
| |
| Context_RequestEditSession, |
| Context_InWriteSession, |
| Context_GetSelection, |
| Context_SetSelection, |
| Context_GetStart, |
| Context_GetEnd, |
| Context_GetActiveView, |
| Context_EnumViews, |
| Context_GetStatus, |
| Context_GetProperty, |
| Context_GetAppProperty, |
| Context_TrackProperties, |
| Context_EnumProperties, |
| Context_GetDocumentMgr, |
| Context_CreateRangeBackup |
| }; |
| |
| static HRESULT WINAPI Source_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut) |
| { |
| Context *This = impl_from_ITfSourceVtbl(iface); |
| return Context_QueryInterface((ITfContext *)This, iid, *ppvOut); |
| } |
| |
| static ULONG WINAPI Source_AddRef(ITfSource *iface) |
| { |
| Context *This = impl_from_ITfSourceVtbl(iface); |
| return Context_AddRef((ITfContext *)This); |
| } |
| |
| static ULONG WINAPI Source_Release(ITfSource *iface) |
| { |
| Context *This = impl_from_ITfSourceVtbl(iface); |
| return Context_Release((ITfContext *)This); |
| } |
| |
| /***************************************************** |
| * ITfSource functions |
| *****************************************************/ |
| static HRESULT WINAPI ContextSource_AdviseSink(ITfSource *iface, |
| REFIID riid, IUnknown *punk, DWORD *pdwCookie) |
| { |
| ContextSink *es; |
| Context *This = impl_from_ITfSourceVtbl(iface); |
| TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie); |
| |
| if (!riid || !punk || !pdwCookie) |
| return E_INVALIDARG; |
| |
| if (IsEqualIID(riid, &IID_ITfTextEditSink)) |
| { |
| es = HeapAlloc(GetProcessHeap(),0,sizeof(ContextSink)); |
| if (!es) |
| return E_OUTOFMEMORY; |
| if (FAILED(IUnknown_QueryInterface(punk, riid, (LPVOID *)&es->interfaces.pITfTextEditSink))) |
| { |
| HeapFree(GetProcessHeap(),0,es); |
| return CONNECT_E_CANNOTCONNECT; |
| } |
| list_add_head(&This->pTextEditSink ,&es->entry); |
| *pdwCookie = generate_Cookie(COOKIE_MAGIC_CONTEXTSINK, es); |
| } |
| else |
| { |
| FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid)); |
| return E_NOTIMPL; |
| } |
| |
| TRACE("cookie %x\n",*pdwCookie); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ContextSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie) |
| { |
| ContextSink *sink; |
| Context *This = impl_from_ITfSourceVtbl(iface); |
| |
| TRACE("(%p) %x\n",This,pdwCookie); |
| |
| if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_CONTEXTSINK) |
| return E_INVALIDARG; |
| |
| sink = (ContextSink*)remove_Cookie(pdwCookie); |
| if (!sink) |
| return CONNECT_E_NOCONNECTION; |
| |
| list_remove(&sink->entry); |
| free_sink(sink); |
| |
| return S_OK; |
| } |
| |
| static const ITfSourceVtbl Context_SourceVtbl = |
| { |
| Source_QueryInterface, |
| Source_AddRef, |
| Source_Release, |
| |
| ContextSource_AdviseSink, |
| ContextSource_UnadviseSink, |
| }; |
| |
| /***************************************************** |
| * ITfInsertAtSelection functions |
| *****************************************************/ |
| static HRESULT WINAPI InsertAtSelection_QueryInterface(ITfInsertAtSelection *iface, REFIID iid, LPVOID *ppvOut) |
| { |
| Context *This = impl_from_ITfInsertAtSelectionVtbl(iface); |
| return Context_QueryInterface((ITfContext *)This, iid, *ppvOut); |
| } |
| |
| static ULONG WINAPI InsertAtSelection_AddRef(ITfInsertAtSelection *iface) |
| { |
| Context *This = impl_from_ITfInsertAtSelectionVtbl(iface); |
| return Context_AddRef((ITfContext *)This); |
| } |
| |
| static ULONG WINAPI InsertAtSelection_Release(ITfInsertAtSelection *iface) |
| { |
| Context *This = impl_from_ITfInsertAtSelectionVtbl(iface); |
| return Context_Release((ITfContext *)This); |
| } |
| |
| static HRESULT WINAPI InsertAtSelection_InsertTextAtSelection( |
| ITfInsertAtSelection *iface, TfEditCookie ec, DWORD dwFlags, |
| const WCHAR *pchText, LONG cch, ITfRange **ppRange) |
| { |
| Context *This = impl_from_ITfInsertAtSelectionVtbl(iface); |
| EditCookie *cookie; |
| LONG acpStart, acpEnd; |
| TS_TEXTCHANGE change; |
| HRESULT hr; |
| |
| TRACE("(%p) %i %x %s %p\n",This, ec, dwFlags, debugstr_wn(pchText,cch), ppRange); |
| |
| if (!This->connected) |
| return TF_E_DISCONNECTED; |
| |
| if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE) |
| return TF_E_NOLOCK; |
| |
| cookie = get_Cookie_data(ec); |
| |
| if ((cookie->lockType & TS_LF_READWRITE) != TS_LF_READWRITE ) |
| return TS_E_READONLY; |
| |
| if (!This->pITextStoreACP) |
| { |
| FIXME("Context does not have a ITextStoreACP\n"); |
| return E_NOTIMPL; |
| } |
| |
| hr = ITextStoreACP_InsertTextAtSelection(This->pITextStoreACP, dwFlags, pchText, cch, &acpStart, &acpEnd, &change); |
| if (SUCCEEDED(hr)) |
| Range_Constructor((ITfContext*)This, This->pITextStoreACP, cookie->lockType, change.acpStart, change.acpNewEnd, ppRange); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI InsertAtSelection_InsertEmbeddedAtSelection( |
| ITfInsertAtSelection *iface, TfEditCookie ec, DWORD dwFlags, |
| IDataObject *pDataObject, ITfRange **ppRange) |
| { |
| Context *This = impl_from_ITfInsertAtSelectionVtbl(iface); |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static const ITfInsertAtSelectionVtbl Context_InsertAtSelectionVtbl = |
| { |
| InsertAtSelection_QueryInterface, |
| InsertAtSelection_AddRef, |
| InsertAtSelection_Release, |
| |
| InsertAtSelection_InsertTextAtSelection, |
| InsertAtSelection_InsertEmbeddedAtSelection, |
| }; |
| |
| /***************************************************** |
| * ITfSourceSingle functions |
| *****************************************************/ |
| static HRESULT WINAPI SourceSingle_QueryInterface(ITfSourceSingle *iface, REFIID iid, LPVOID *ppvOut) |
| { |
| Context *This = impl_from_ITfSourceSingleVtbl(iface); |
| return Context_QueryInterface((ITfContext *)This, iid, *ppvOut); |
| } |
| |
| static ULONG WINAPI SourceSingle_AddRef(ITfSourceSingle *iface) |
| { |
| Context *This = impl_from_ITfSourceSingleVtbl(iface); |
| return Context_AddRef((ITfContext *)This); |
| } |
| |
| static ULONG WINAPI SourceSingle_Release(ITfSourceSingle *iface) |
| { |
| Context *This = impl_from_ITfSourceSingleVtbl(iface); |
| return Context_Release((ITfContext *)This); |
| } |
| |
| static HRESULT WINAPI SourceSingle_AdviseSingleSink( ITfSourceSingle *iface, |
| TfClientId tid, REFIID riid, IUnknown *punk) |
| { |
| Context *This = impl_from_ITfSourceSingleVtbl(iface); |
| FIXME("STUB:(%p) %i %s %p\n",This, tid, debugstr_guid(riid),punk); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI SourceSingle_UnadviseSingleSink( ITfSourceSingle *iface, |
| TfClientId tid, REFIID riid) |
| { |
| Context *This = impl_from_ITfSourceSingleVtbl(iface); |
| FIXME("STUB:(%p) %i %s\n",This, tid, debugstr_guid(riid)); |
| return E_NOTIMPL; |
| } |
| |
| static const ITfSourceSingleVtbl Context_SourceSingleVtbl = |
| { |
| SourceSingle_QueryInterface, |
| SourceSingle_AddRef, |
| SourceSingle_Release, |
| |
| SourceSingle_AdviseSingleSink, |
| SourceSingle_UnadviseSingleSink, |
| }; |
| |
| HRESULT Context_Constructor(TfClientId tidOwner, IUnknown *punk, ITfDocumentMgr *mgr, ITfContext **ppOut, TfEditCookie *pecTextStore) |
| { |
| Context *This; |
| EditCookie *cookie; |
| |
| This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Context)); |
| if (This == NULL) |
| return E_OUTOFMEMORY; |
| |
| cookie = HeapAlloc(GetProcessHeap(),0,sizeof(EditCookie)); |
| if (cookie == NULL) |
| { |
| HeapFree(GetProcessHeap(),0,This); |
| return E_OUTOFMEMORY; |
| } |
| |
| TRACE("(%p) %x %p %p %p\n",This, tidOwner, punk, ppOut, pecTextStore); |
| |
| This->ContextVtbl= &Context_ContextVtbl; |
| This->SourceVtbl = &Context_SourceVtbl; |
| This->InsertAtSelectionVtbl = &Context_InsertAtSelectionVtbl; |
| This->SourceSingleVtbl = &Context_SourceSingleVtbl; |
| This->refCount = 1; |
| This->tidOwner = tidOwner; |
| This->connected = FALSE; |
| This->manager = mgr; |
| |
| CompartmentMgr_Constructor((IUnknown*)This, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); |
| |
| cookie->lockType = TF_ES_READ; |
| cookie->pOwningContext = This; |
| |
| if (punk) |
| { |
| IUnknown_QueryInterface(punk, &IID_ITextStoreACP, |
| (LPVOID*)&This->pITextStoreACP); |
| |
| IUnknown_QueryInterface(punk, &IID_ITfContextOwnerCompositionSink, |
| (LPVOID*)&This->pITfContextOwnerCompositionSink); |
| |
| if (!This->pITextStoreACP && !This->pITfContextOwnerCompositionSink) |
| FIXME("Unhandled pUnk\n"); |
| } |
| |
| This->defaultCookie = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE,cookie); |
| *pecTextStore = This->defaultCookie; |
| |
| list_init(&This->pContextKeyEventSink); |
| list_init(&This->pEditTransactionSink); |
| list_init(&This->pStatusSink); |
| list_init(&This->pTextEditSink); |
| list_init(&This->pTextLayoutSink); |
| |
| *ppOut = (ITfContext*)This; |
| TRACE("returning %p\n", This); |
| |
| return S_OK; |
| } |
| |
| HRESULT Context_Initialize(ITfContext *iface, ITfDocumentMgr *manager) |
| { |
| Context *This = (Context *)iface; |
| |
| if (This->pITextStoreACP) |
| { |
| if (SUCCEEDED(TextStoreACPSink_Constructor(&This->pITextStoreACPSink, This))) |
| ITextStoreACP_AdviseSink(This->pITextStoreACP, &IID_ITextStoreACPSink, |
| (IUnknown*)This->pITextStoreACPSink, TS_AS_ALL_SINKS); |
| } |
| This->connected = TRUE; |
| This->manager = manager; |
| return S_OK; |
| } |
| |
| HRESULT Context_Uninitialize(ITfContext *iface) |
| { |
| Context *This = (Context *)iface; |
| |
| if (This->pITextStoreACPSink) |
| { |
| ITextStoreACP_UnadviseSink(This->pITextStoreACP, (IUnknown*)This->pITextStoreACPSink); |
| if (ITextStoreACPSink_Release(This->pITextStoreACPSink) == 0) |
| This->pITextStoreACPSink = NULL; |
| } |
| This->connected = FALSE; |
| This->manager = NULL; |
| return S_OK; |
| } |
| |
| /************************************************************************** |
| * ITextStoreACPSink |
| **************************************************************************/ |
| |
| static void TextStoreACPSink_Destructor(TextStoreACPSink *This) |
| { |
| TRACE("destroying %p\n", This); |
| HeapFree(GetProcessHeap(),0,This); |
| } |
| |
| static HRESULT WINAPI TextStoreACPSink_QueryInterface(ITextStoreACPSink *iface, REFIID iid, LPVOID *ppvOut) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| *ppvOut = NULL; |
| |
| if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITextStoreACPSink)) |
| { |
| *ppvOut = This; |
| } |
| |
| if (*ppvOut) |
| { |
| ITextStoreACPSink_AddRef(iface); |
| return S_OK; |
| } |
| |
| WARN("unsupported interface: %s\n", debugstr_guid(iid)); |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI TextStoreACPSink_AddRef(ITextStoreACPSink *iface) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| return InterlockedIncrement(&This->refCount); |
| } |
| |
| static ULONG WINAPI TextStoreACPSink_Release(ITextStoreACPSink *iface) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| ULONG ret; |
| |
| ret = InterlockedDecrement(&This->refCount); |
| if (ret == 0) |
| TextStoreACPSink_Destructor(This); |
| return ret; |
| } |
| |
| /***************************************************** |
| * ITextStoreACPSink functions |
| *****************************************************/ |
| |
| static HRESULT WINAPI TextStoreACPSink_OnTextChange(ITextStoreACPSink *iface, |
| DWORD dwFlags, const TS_TEXTCHANGE *pChange) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TextStoreACPSink_OnSelectionChange(ITextStoreACPSink *iface) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TextStoreACPSink_OnLayoutChange(ITextStoreACPSink *iface, |
| TsLayoutCode lcode, TsViewCookie vcView) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TextStoreACPSink_OnStatusChange(ITextStoreACPSink *iface, |
| DWORD dwFlags) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| HRESULT hr, hrSession; |
| |
| TRACE("(%p) %x\n",This, dwFlags); |
| |
| if (!This->pContext) |
| { |
| ERR("No context?\n"); |
| return E_FAIL; |
| } |
| |
| if (!This->pContext->pITextStoreACP) |
| { |
| FIXME("Context does not have a ITextStoreACP\n"); |
| return E_NOTIMPL; |
| } |
| |
| hr = ITextStoreACP_RequestLock(This->pContext->pITextStoreACP, TS_LF_READ, &hrSession); |
| |
| if(SUCCEEDED(hr) && SUCCEEDED(hrSession)) |
| This->pContext->documentStatus.dwDynamicFlags = dwFlags; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI TextStoreACPSink_OnAttrsChange(ITextStoreACPSink *iface, |
| LONG acpStart, LONG acpEnd, ULONG cAttrs, const TS_ATTRID *paAttrs) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TextStoreACPSink_OnLockGranted(ITextStoreACPSink *iface, |
| DWORD dwLockFlags) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| HRESULT hr; |
| EditCookie *cookie,*sinkcookie; |
| TfEditCookie ec; |
| struct list *cursor; |
| |
| TRACE("(%p) %x\n",This, dwLockFlags); |
| |
| if (!This->pContext) |
| { |
| ERR("OnLockGranted called without a context\n"); |
| return E_FAIL; |
| } |
| |
| if (!This->pContext->currentEditSession) |
| { |
| FIXME("OnLockGranted called for something other than an EditSession\n"); |
| return S_OK; |
| } |
| |
| cookie = HeapAlloc(GetProcessHeap(),0,sizeof(EditCookie)); |
| if (!cookie) |
| return E_OUTOFMEMORY; |
| |
| sinkcookie = HeapAlloc(GetProcessHeap(),0,sizeof(EditCookie)); |
| if (!sinkcookie) |
| { |
| HeapFree(GetProcessHeap(), 0, cookie); |
| return E_OUTOFMEMORY; |
| } |
| |
| cookie->lockType = dwLockFlags; |
| cookie->pOwningContext = This->pContext; |
| ec = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE, cookie); |
| |
| hr = ITfEditSession_DoEditSession(This->pContext->currentEditSession, ec); |
| |
| if ((dwLockFlags&TS_LF_READWRITE) == TS_LF_READWRITE) |
| { |
| TfEditCookie sc; |
| |
| sinkcookie->lockType = TS_LF_READ; |
| sinkcookie->pOwningContext = This->pContext; |
| sc = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE, sinkcookie); |
| |
| /*TODO: implement ITfEditRecord */ |
| LIST_FOR_EACH(cursor, &This->pContext->pTextEditSink) |
| { |
| ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry); |
| ITfTextEditSink_OnEndEdit(sink->interfaces.pITfTextEditSink, |
| (ITfContext*) &This->pContext, sc, NULL); |
| } |
| sinkcookie = remove_Cookie(sc); |
| } |
| HeapFree(GetProcessHeap(),0,sinkcookie); |
| |
| ITfEditSession_Release(This->pContext->currentEditSession); |
| This->pContext->currentEditSession = NULL; |
| |
| /* Edit Cookie is only valid during the edit session */ |
| cookie = remove_Cookie(ec); |
| HeapFree(GetProcessHeap(),0,cookie); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI TextStoreACPSink_OnStartEditTransaction(ITextStoreACPSink *iface) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TextStoreACPSink_OnEndEditTransaction(ITextStoreACPSink *iface) |
| { |
| TextStoreACPSink *This = (TextStoreACPSink *)iface; |
| FIXME("STUB:(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static const ITextStoreACPSinkVtbl TextStoreACPSink_TextStoreACPSinkVtbl = |
| { |
| TextStoreACPSink_QueryInterface, |
| TextStoreACPSink_AddRef, |
| TextStoreACPSink_Release, |
| |
| TextStoreACPSink_OnTextChange, |
| TextStoreACPSink_OnSelectionChange, |
| TextStoreACPSink_OnLayoutChange, |
| TextStoreACPSink_OnStatusChange, |
| TextStoreACPSink_OnAttrsChange, |
| TextStoreACPSink_OnLockGranted, |
| TextStoreACPSink_OnStartEditTransaction, |
| TextStoreACPSink_OnEndEditTransaction |
| }; |
| |
| static HRESULT TextStoreACPSink_Constructor(ITextStoreACPSink **ppOut, Context *pContext) |
| { |
| TextStoreACPSink *This; |
| |
| This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextStoreACPSink)); |
| if (This == NULL) |
| return E_OUTOFMEMORY; |
| |
| This->TextStoreACPSinkVtbl= &TextStoreACPSink_TextStoreACPSinkVtbl; |
| This->refCount = 1; |
| |
| This->pContext = pContext; |
| |
| TRACE("returning %p\n", This); |
| *ppOut = (ITextStoreACPSink*)This; |
| return S_OK; |
| } |