xref: /reactos/dll/win32/msctf/context.c (revision 8786e12d)
1 /*
2  *  ITfContext implementation
3  *
4  *  Copyright 2009 Aric Stewart, CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "config.h"
22 
23 #include <stdarg.h>
24 
25 #define COBJMACROS
26 
27 #include "wine/debug.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "winuser.h"
32 #include "shlwapi.h"
33 #include "winerror.h"
34 #include "objbase.h"
35 #include "olectl.h"
36 
37 #include "wine/unicode.h"
38 
39 #include "msctf.h"
40 #include "msctf_internal.h"
41 
42 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
43 
44 typedef struct tagContext {
45     ITfContext ITfContext_iface;
46     ITfSource ITfSource_iface;
47     /* const ITfContextCompositionVtbl *ContextCompositionVtbl; */
48     /* const ITfContextOwnerCompositionServicesVtbl *ContextOwnerCompositionServicesVtbl; */
49     /* const ITfContextOwnerServicesVtbl *ContextOwnerServicesVtbl; */
50     ITfInsertAtSelection ITfInsertAtSelection_iface;
51     /* const ITfMouseTrackerVtbl *MouseTrackerVtbl; */
52     /* const ITfQueryEmbeddedVtbl *QueryEmbeddedVtbl; */
53     ITfSourceSingle ITfSourceSingle_iface;
54     ITextStoreACPSink ITextStoreACPSink_iface;
55     ITextStoreACPServices ITextStoreACPServices_iface;
56     LONG refCount;
57     BOOL connected;
58 
59     /* Aggregation */
60     ITfCompartmentMgr  *CompartmentMgr;
61 
62     TfClientId tidOwner;
63     TfEditCookie defaultCookie;
64     TS_STATUS documentStatus;
65     ITfDocumentMgr *manager;
66 
67     ITextStoreACP   *pITextStoreACP;
68     ITfContextOwnerCompositionSink *pITfContextOwnerCompositionSink;
69 
70     ITfEditSession* currentEditSession;
71 
72     /* kept as separate lists to reduce unnecessary iterations */
73     struct list     pContextKeyEventSink;
74     struct list     pEditTransactionSink;
75     struct list     pStatusSink;
76     struct list     pTextEditSink;
77     struct list     pTextLayoutSink;
78 
79 } Context;
80 
81 typedef struct tagEditCookie {
82     DWORD lockType;
83     Context *pOwningContext;
84 } EditCookie;
85 
86 static inline Context *impl_from_ITfContext(ITfContext *iface)
87 {
88     return CONTAINING_RECORD(iface, Context, ITfContext_iface);
89 }
90 
91 static inline Context *impl_from_ITfSource(ITfSource *iface)
92 {
93     return CONTAINING_RECORD(iface, Context, ITfSource_iface);
94 }
95 
96 static inline Context *impl_from_ITfInsertAtSelection(ITfInsertAtSelection *iface)
97 {
98     return CONTAINING_RECORD(iface, Context, ITfInsertAtSelection_iface);
99 }
100 
101 static inline Context *impl_from_ITfSourceSingle(ITfSourceSingle* iface)
102 {
103     return CONTAINING_RECORD(iface, Context, ITfSourceSingle_iface);
104 }
105 
106 static inline Context *impl_from_ITextStoreACPSink(ITextStoreACPSink *iface)
107 {
108     return CONTAINING_RECORD(iface, Context, ITextStoreACPSink_iface);
109 }
110 
111 static inline Context *impl_from_ITextStoreACPServices(ITextStoreACPServices *iface)
112 {
113     return CONTAINING_RECORD(iface, Context, ITextStoreACPServices_iface);
114 }
115 
116 static void Context_Destructor(Context *This)
117 {
118     EditCookie *cookie;
119     TRACE("destroying %p\n", This);
120 
121     if (This->pITextStoreACP)
122         ITextStoreACP_Release(This->pITextStoreACP);
123 
124     if (This->pITfContextOwnerCompositionSink)
125         ITfContextOwnerCompositionSink_Release(This->pITfContextOwnerCompositionSink);
126 
127     if (This->defaultCookie)
128     {
129         cookie = remove_Cookie(This->defaultCookie);
130         HeapFree(GetProcessHeap(),0,cookie);
131         This->defaultCookie = 0;
132     }
133 
134     free_sinks(&This->pContextKeyEventSink);
135     free_sinks(&This->pEditTransactionSink);
136     free_sinks(&This->pStatusSink);
137     free_sinks(&This->pTextEditSink);
138     free_sinks(&This->pTextLayoutSink);
139 
140     CompartmentMgr_Destructor(This->CompartmentMgr);
141     HeapFree(GetProcessHeap(),0,This);
142 }
143 
144 static HRESULT WINAPI Context_QueryInterface(ITfContext *iface, REFIID iid, LPVOID *ppvOut)
145 {
146     Context *This = impl_from_ITfContext(iface);
147     *ppvOut = NULL;
148 
149     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfContext))
150     {
151         *ppvOut = &This->ITfContext_iface;
152     }
153     else if (IsEqualIID(iid, &IID_ITfSource))
154     {
155         *ppvOut = &This->ITfSource_iface;
156     }
157     else if (IsEqualIID(iid, &IID_ITfInsertAtSelection))
158     {
159         *ppvOut = &This->ITfInsertAtSelection_iface;
160     }
161     else if (IsEqualIID(iid, &IID_ITfCompartmentMgr))
162     {
163         *ppvOut = This->CompartmentMgr;
164     }
165     else if (IsEqualIID(iid, &IID_ITfSourceSingle))
166     {
167         *ppvOut = &This->ITfSourceSingle_iface;
168     }
169 
170     if (*ppvOut)
171     {
172         ITfContext_AddRef(iface);
173         return S_OK;
174     }
175 
176     WARN("unsupported interface: %s\n", debugstr_guid(iid));
177     return E_NOINTERFACE;
178 }
179 
180 static ULONG WINAPI Context_AddRef(ITfContext *iface)
181 {
182     Context *This = impl_from_ITfContext(iface);
183     return InterlockedIncrement(&This->refCount);
184 }
185 
186 static ULONG WINAPI Context_Release(ITfContext *iface)
187 {
188     Context *This = impl_from_ITfContext(iface);
189     ULONG ret;
190 
191     ret = InterlockedDecrement(&This->refCount);
192     if (ret == 0)
193         Context_Destructor(This);
194     return ret;
195 }
196 
197 /*****************************************************
198  * ITfContext functions
199  *****************************************************/
200 static HRESULT WINAPI Context_RequestEditSession (ITfContext *iface,
201         TfClientId tid, ITfEditSession *pes, DWORD dwFlags,
202         HRESULT *phrSession)
203 {
204     Context *This = impl_from_ITfContext(iface);
205     HRESULT hr;
206     DWORD  dwLockFlags = 0x0;
207 
208     TRACE("(%p) %i %p %x %p\n",This, tid, pes, dwFlags, phrSession);
209 
210     if (!(dwFlags & TF_ES_READ) && !(dwFlags & TF_ES_READWRITE))
211     {
212         *phrSession = E_FAIL;
213         return E_INVALIDARG;
214     }
215 
216     if (!This->pITextStoreACP)
217     {
218         FIXME("No ITextStoreACP available\n");
219         *phrSession = E_FAIL;
220         return E_FAIL;
221     }
222 
223     if (!(dwFlags & TF_ES_ASYNC))
224         dwLockFlags |= TS_LF_SYNC;
225 
226     if ((dwFlags & TF_ES_READWRITE) == TF_ES_READWRITE)
227         dwLockFlags |= TS_LF_READWRITE;
228     else if (dwFlags & TF_ES_READ)
229         dwLockFlags |= TS_LF_READ;
230 
231     if (!This->documentStatus.dwDynamicFlags)
232         ITextStoreACP_GetStatus(This->pITextStoreACP, &This->documentStatus);
233 
234     if (((dwFlags & TF_ES_READWRITE) == TF_ES_READWRITE) && (This->documentStatus.dwDynamicFlags & TS_SD_READONLY))
235     {
236         *phrSession = TS_E_READONLY;
237         return S_OK;
238     }
239 
240     if (FAILED (ITfEditSession_QueryInterface(pes, &IID_ITfEditSession, (LPVOID*)&This->currentEditSession)))
241     {
242         *phrSession = E_FAIL;
243         return E_INVALIDARG;
244     }
245 
246     hr = ITextStoreACP_RequestLock(This->pITextStoreACP, dwLockFlags, phrSession);
247 
248     return hr;
249 }
250 
251 static HRESULT WINAPI Context_InWriteSession (ITfContext *iface,
252          TfClientId tid,
253          BOOL *pfWriteSession)
254 {
255     Context *This = impl_from_ITfContext(iface);
256     FIXME("STUB:(%p)\n",This);
257     return E_NOTIMPL;
258 }
259 
260 static HRESULT WINAPI Context_GetSelection (ITfContext *iface,
261         TfEditCookie ec, ULONG ulIndex, ULONG ulCount,
262         TF_SELECTION *pSelection, ULONG *pcFetched)
263 {
264     Context *This = impl_from_ITfContext(iface);
265     EditCookie *cookie;
266     ULONG count, i;
267     ULONG totalFetched = 0;
268     HRESULT hr = S_OK;
269 
270     if (!pSelection || !pcFetched)
271         return E_INVALIDARG;
272 
273     *pcFetched = 0;
274 
275     if (!This->connected)
276         return TF_E_DISCONNECTED;
277 
278     if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE)
279         return TF_E_NOLOCK;
280 
281     if (!This->pITextStoreACP)
282     {
283         FIXME("Context does not have a ITextStoreACP\n");
284         return E_NOTIMPL;
285     }
286 
287     cookie = get_Cookie_data(ec);
288 
289     if (ulIndex == TF_DEFAULT_SELECTION)
290         count = 1;
291     else
292         count = ulCount;
293 
294     for (i = 0; i < count; i++)
295     {
296         DWORD fetched;
297         TS_SELECTION_ACP acps;
298 
299         hr = ITextStoreACP_GetSelection(This->pITextStoreACP, ulIndex + i,
300                 1, &acps, &fetched);
301 
302         if (hr == TS_E_NOLOCK)
303             return TF_E_NOLOCK;
304         else if (SUCCEEDED(hr))
305         {
306             pSelection[totalFetched].style.ase = acps.style.ase;
307             pSelection[totalFetched].style.fInterimChar = acps.style.fInterimChar;
308             Range_Constructor(iface, This->pITextStoreACP, cookie->lockType, acps.acpStart, acps.acpEnd, &pSelection[totalFetched].range);
309             totalFetched ++;
310         }
311         else
312             break;
313     }
314 
315     *pcFetched = totalFetched;
316 
317     return hr;
318 }
319 
320 static HRESULT WINAPI Context_SetSelection (ITfContext *iface,
321         TfEditCookie ec, ULONG ulCount, const TF_SELECTION *pSelection)
322 {
323     Context *This = impl_from_ITfContext(iface);
324     TS_SELECTION_ACP *acp;
325     ULONG i;
326     HRESULT hr;
327 
328     TRACE("(%p) %i %i %p\n",This,ec,ulCount,pSelection);
329 
330     if (!This->pITextStoreACP)
331     {
332         FIXME("Context does not have a ITextStoreACP\n");
333         return E_NOTIMPL;
334     }
335 
336     if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE)
337         return TF_E_NOLOCK;
338 
339     acp = HeapAlloc(GetProcessHeap(), 0, sizeof(TS_SELECTION_ACP) * ulCount);
340     if (!acp)
341         return E_OUTOFMEMORY;
342 
343     for (i = 0; i < ulCount; i++)
344         if (FAILED(TF_SELECTION_to_TS_SELECTION_ACP(&pSelection[i], &acp[i])))
345         {
346             TRACE("Selection Conversion Failed\n");
347             HeapFree(GetProcessHeap(), 0 , acp);
348             return E_FAIL;
349         }
350 
351     hr = ITextStoreACP_SetSelection(This->pITextStoreACP, ulCount, acp);
352 
353     HeapFree(GetProcessHeap(), 0, acp);
354 
355     return hr;
356 }
357 
358 static HRESULT WINAPI Context_GetStart (ITfContext *iface,
359         TfEditCookie ec, ITfRange **ppStart)
360 {
361     Context *This = impl_from_ITfContext(iface);
362     EditCookie *cookie;
363     TRACE("(%p) %i %p\n",This,ec,ppStart);
364 
365     if (!ppStart)
366         return E_INVALIDARG;
367 
368     *ppStart = NULL;
369 
370     if (!This->connected)
371         return TF_E_DISCONNECTED;
372 
373     if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE)
374         return TF_E_NOLOCK;
375 
376     cookie = get_Cookie_data(ec);
377     return Range_Constructor(iface, This->pITextStoreACP, cookie->lockType, 0, 0, ppStart);
378 }
379 
380 static HRESULT WINAPI Context_GetEnd (ITfContext *iface,
381         TfEditCookie ec, ITfRange **ppEnd)
382 {
383     Context *This = impl_from_ITfContext(iface);
384     EditCookie *cookie;
385     LONG end;
386     TRACE("(%p) %i %p\n",This,ec,ppEnd);
387 
388     if (!ppEnd)
389         return E_INVALIDARG;
390 
391     *ppEnd = NULL;
392 
393     if (!This->connected)
394         return TF_E_DISCONNECTED;
395 
396     if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE)
397         return TF_E_NOLOCK;
398 
399     if (!This->pITextStoreACP)
400     {
401         FIXME("Context does not have a ITextStoreACP\n");
402         return E_NOTIMPL;
403     }
404 
405     cookie = get_Cookie_data(ec);
406     ITextStoreACP_GetEndACP(This->pITextStoreACP,&end);
407 
408     return Range_Constructor(iface, This->pITextStoreACP, cookie->lockType, end, end, ppEnd);
409 }
410 
411 static HRESULT WINAPI Context_GetActiveView (ITfContext *iface,
412   ITfContextView **ppView)
413 {
414     Context *This = impl_from_ITfContext(iface);
415     FIXME("STUB:(%p)\n",This);
416     return E_NOTIMPL;
417 }
418 
419 static HRESULT WINAPI Context_EnumViews (ITfContext *iface,
420         IEnumTfContextViews **ppEnum)
421 {
422     Context *This = impl_from_ITfContext(iface);
423     FIXME("STUB:(%p)\n",This);
424     return E_NOTIMPL;
425 }
426 
427 static HRESULT WINAPI Context_GetStatus (ITfContext *iface,
428         TF_STATUS *pdcs)
429 {
430     Context *This = impl_from_ITfContext(iface);
431     TRACE("(%p) %p\n",This,pdcs);
432 
433     if (!This->connected)
434         return TF_E_DISCONNECTED;
435 
436     if (!pdcs)
437         return E_INVALIDARG;
438 
439     if (!This->pITextStoreACP)
440     {
441         FIXME("Context does not have a ITextStoreACP\n");
442         return E_NOTIMPL;
443     }
444 
445     ITextStoreACP_GetStatus(This->pITextStoreACP, &This->documentStatus);
446 
447     *pdcs = This->documentStatus;
448 
449     return S_OK;
450 }
451 
452 static HRESULT WINAPI Context_GetProperty (ITfContext *iface,
453         REFGUID guidProp, ITfProperty **ppProp)
454 {
455     Context *This = impl_from_ITfContext(iface);
456     FIXME("STUB:(%p)\n",This);
457     return E_NOTIMPL;
458 }
459 
460 static HRESULT WINAPI Context_GetAppProperty (ITfContext *iface,
461         REFGUID guidProp, ITfReadOnlyProperty **ppProp)
462 {
463     Context *This = impl_from_ITfContext(iface);
464     FIXME("STUB:(%p)\n",This);
465     return E_NOTIMPL;
466 }
467 
468 static HRESULT WINAPI Context_TrackProperties (ITfContext *iface,
469         const GUID **prgProp, ULONG cProp, const GUID **prgAppProp,
470         ULONG cAppProp, ITfReadOnlyProperty **ppProperty)
471 {
472     Context *This = impl_from_ITfContext(iface);
473     FIXME("STUB:(%p)\n",This);
474     return E_NOTIMPL;
475 }
476 
477 static HRESULT WINAPI Context_EnumProperties (ITfContext *iface,
478         IEnumTfProperties **ppEnum)
479 {
480     Context *This = impl_from_ITfContext(iface);
481     FIXME("STUB:(%p)\n",This);
482     return E_NOTIMPL;
483 }
484 
485 static HRESULT WINAPI Context_GetDocumentMgr (ITfContext *iface,
486         ITfDocumentMgr **ppDm)
487 {
488     Context *This = impl_from_ITfContext(iface);
489     TRACE("(%p) %p\n",This,ppDm);
490 
491     if (!ppDm)
492         return E_INVALIDARG;
493 
494     *ppDm = This->manager;
495     if (!This->manager)
496         return S_FALSE;
497 
498     ITfDocumentMgr_AddRef(This->manager);
499 
500     return S_OK;
501 }
502 
503 static HRESULT WINAPI Context_CreateRangeBackup (ITfContext *iface,
504         TfEditCookie ec, ITfRange *pRange, ITfRangeBackup **ppBackup)
505 {
506     Context *This = impl_from_ITfContext(iface);
507     FIXME("STUB:(%p)\n",This);
508     return E_NOTIMPL;
509 }
510 
511 static const ITfContextVtbl ContextVtbl =
512 {
513     Context_QueryInterface,
514     Context_AddRef,
515     Context_Release,
516     Context_RequestEditSession,
517     Context_InWriteSession,
518     Context_GetSelection,
519     Context_SetSelection,
520     Context_GetStart,
521     Context_GetEnd,
522     Context_GetActiveView,
523     Context_EnumViews,
524     Context_GetStatus,
525     Context_GetProperty,
526     Context_GetAppProperty,
527     Context_TrackProperties,
528     Context_EnumProperties,
529     Context_GetDocumentMgr,
530     Context_CreateRangeBackup
531 };
532 
533 static HRESULT WINAPI ContextSource_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
534 {
535     Context *This = impl_from_ITfSource(iface);
536     return ITfContext_QueryInterface(&This->ITfContext_iface, iid, ppvOut);
537 }
538 
539 static ULONG WINAPI ContextSource_AddRef(ITfSource *iface)
540 {
541     Context *This = impl_from_ITfSource(iface);
542     return ITfContext_AddRef(&This->ITfContext_iface);
543 }
544 
545 static ULONG WINAPI ContextSource_Release(ITfSource *iface)
546 {
547     Context *This = impl_from_ITfSource(iface);
548     return ITfContext_Release(&This->ITfContext_iface);
549 }
550 
551 /*****************************************************
552  * ITfSource functions
553  *****************************************************/
554 static HRESULT WINAPI ContextSource_AdviseSink(ITfSource *iface,
555         REFIID riid, IUnknown *punk, DWORD *pdwCookie)
556 {
557     Context *This = impl_from_ITfSource(iface);
558 
559     TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);
560 
561     if (!riid || !punk || !pdwCookie)
562         return E_INVALIDARG;
563 
564     if (IsEqualIID(riid, &IID_ITfTextEditSink))
565         return advise_sink(&This->pTextEditSink, &IID_ITfTextEditSink, COOKIE_MAGIC_CONTEXTSINK, punk, pdwCookie);
566 
567     FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
568     return E_NOTIMPL;
569 }
570 
571 static HRESULT WINAPI ContextSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
572 {
573     Context *This = impl_from_ITfSource(iface);
574 
575     TRACE("(%p) %x\n",This,pdwCookie);
576 
577     if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_CONTEXTSINK)
578         return E_INVALIDARG;
579 
580     return unadvise_sink(pdwCookie);
581 }
582 
583 static const ITfSourceVtbl ContextSourceVtbl =
584 {
585     ContextSource_QueryInterface,
586     ContextSource_AddRef,
587     ContextSource_Release,
588     ContextSource_AdviseSink,
589     ContextSource_UnadviseSink
590 };
591 
592 /*****************************************************
593  * ITfInsertAtSelection functions
594  *****************************************************/
595 static HRESULT WINAPI InsertAtSelection_QueryInterface(ITfInsertAtSelection *iface, REFIID iid, LPVOID *ppvOut)
596 {
597     Context *This = impl_from_ITfInsertAtSelection(iface);
598     return ITfContext_QueryInterface(&This->ITfContext_iface, iid, ppvOut);
599 }
600 
601 static ULONG WINAPI InsertAtSelection_AddRef(ITfInsertAtSelection *iface)
602 {
603     Context *This = impl_from_ITfInsertAtSelection(iface);
604     return ITfContext_AddRef(&This->ITfContext_iface);
605 }
606 
607 static ULONG WINAPI InsertAtSelection_Release(ITfInsertAtSelection *iface)
608 {
609     Context *This = impl_from_ITfInsertAtSelection(iface);
610     return ITfContext_Release(&This->ITfContext_iface);
611 }
612 
613 static HRESULT WINAPI InsertAtSelection_InsertTextAtSelection(
614         ITfInsertAtSelection *iface, TfEditCookie ec, DWORD dwFlags,
615         const WCHAR *pchText, LONG cch, ITfRange **ppRange)
616 {
617     Context *This = impl_from_ITfInsertAtSelection(iface);
618     EditCookie *cookie;
619     LONG acpStart, acpEnd;
620     TS_TEXTCHANGE change;
621     HRESULT hr;
622 
623     TRACE("(%p) %i %x %s %p\n",This, ec, dwFlags, debugstr_wn(pchText,cch), ppRange);
624 
625     if (!This->connected)
626         return TF_E_DISCONNECTED;
627 
628     if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE)
629         return TF_E_NOLOCK;
630 
631     cookie = get_Cookie_data(ec);
632 
633     if ((cookie->lockType & TS_LF_READWRITE) != TS_LF_READWRITE )
634         return TS_E_READONLY;
635 
636     if (!This->pITextStoreACP)
637     {
638         FIXME("Context does not have a ITextStoreACP\n");
639         return E_NOTIMPL;
640     }
641 
642     hr = ITextStoreACP_InsertTextAtSelection(This->pITextStoreACP, dwFlags, pchText, cch, &acpStart, &acpEnd, &change);
643     if (SUCCEEDED(hr))
644         Range_Constructor(&This->ITfContext_iface, This->pITextStoreACP, cookie->lockType, change.acpStart, change.acpNewEnd, ppRange);
645 
646     return hr;
647 }
648 
649 static HRESULT WINAPI InsertAtSelection_InsertEmbeddedAtSelection(
650         ITfInsertAtSelection *iface, TfEditCookie ec, DWORD dwFlags,
651         IDataObject *pDataObject, ITfRange **ppRange)
652 {
653     Context *This = impl_from_ITfInsertAtSelection(iface);
654     FIXME("STUB:(%p)\n",This);
655     return E_NOTIMPL;
656 }
657 
658 static const ITfInsertAtSelectionVtbl InsertAtSelectionVtbl =
659 {
660     InsertAtSelection_QueryInterface,
661     InsertAtSelection_AddRef,
662     InsertAtSelection_Release,
663     InsertAtSelection_InsertTextAtSelection,
664     InsertAtSelection_InsertEmbeddedAtSelection,
665 };
666 
667 /*****************************************************
668  * ITfSourceSingle functions
669  *****************************************************/
670 static HRESULT WINAPI SourceSingle_QueryInterface(ITfSourceSingle *iface, REFIID iid, LPVOID *ppvOut)
671 {
672     Context *This = impl_from_ITfSourceSingle(iface);
673     return ITfContext_QueryInterface(&This->ITfContext_iface, iid, ppvOut);
674 }
675 
676 static ULONG WINAPI SourceSingle_AddRef(ITfSourceSingle *iface)
677 {
678     Context *This = impl_from_ITfSourceSingle(iface);
679     return ITfContext_AddRef(&This->ITfContext_iface);
680 }
681 
682 static ULONG WINAPI SourceSingle_Release(ITfSourceSingle *iface)
683 {
684     Context *This = impl_from_ITfSourceSingle(iface);
685     return ITfContext_Release(&This->ITfContext_iface);
686 }
687 
688 static HRESULT WINAPI SourceSingle_AdviseSingleSink( ITfSourceSingle *iface,
689     TfClientId tid, REFIID riid, IUnknown *punk)
690 {
691     Context *This = impl_from_ITfSourceSingle(iface);
692     FIXME("STUB:(%p) %i %s %p\n",This, tid, debugstr_guid(riid),punk);
693     return E_NOTIMPL;
694 }
695 
696 static HRESULT WINAPI SourceSingle_UnadviseSingleSink( ITfSourceSingle *iface,
697     TfClientId tid, REFIID riid)
698 {
699     Context *This = impl_from_ITfSourceSingle(iface);
700     FIXME("STUB:(%p) %i %s\n",This, tid, debugstr_guid(riid));
701     return E_NOTIMPL;
702 }
703 
704 static const ITfSourceSingleVtbl ContextSourceSingleVtbl =
705 {
706     SourceSingle_QueryInterface,
707     SourceSingle_AddRef,
708     SourceSingle_Release,
709     SourceSingle_AdviseSingleSink,
710     SourceSingle_UnadviseSingleSink,
711 };
712 
713 /**************************************************************************
714  *  ITextStoreACPSink
715  **************************************************************************/
716 
717 static HRESULT WINAPI TextStoreACPSink_QueryInterface(ITextStoreACPSink *iface, REFIID iid, LPVOID *ppvOut)
718 {
719     Context *This = impl_from_ITextStoreACPSink(iface);
720 
721     *ppvOut = NULL;
722 
723     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITextStoreACPSink))
724     {
725         *ppvOut = &This->ITextStoreACPSink_iface;
726     }
727     else if (IsEqualIID(iid, &IID_ITextStoreACPServices))
728         *ppvOut = &This->ITextStoreACPServices_iface;
729 
730     if (*ppvOut)
731     {
732         ITextStoreACPSink_AddRef(iface);
733         return S_OK;
734     }
735 
736     WARN("unsupported interface: %s\n", debugstr_guid(iid));
737     return E_NOINTERFACE;
738 }
739 
740 static ULONG WINAPI TextStoreACPSink_AddRef(ITextStoreACPSink *iface)
741 {
742     Context *This = impl_from_ITextStoreACPSink(iface);
743     return ITfContext_AddRef(&This->ITfContext_iface);
744 }
745 
746 static ULONG WINAPI TextStoreACPSink_Release(ITextStoreACPSink *iface)
747 {
748     Context *This = impl_from_ITextStoreACPSink(iface);
749     return ITfContext_Release(&This->ITfContext_iface);
750 }
751 
752 /*****************************************************
753  * ITextStoreACPSink functions
754  *****************************************************/
755 
756 static HRESULT WINAPI TextStoreACPSink_OnTextChange(ITextStoreACPSink *iface,
757         DWORD dwFlags, const TS_TEXTCHANGE *pChange)
758 {
759     Context *This = impl_from_ITextStoreACPSink(iface);
760     FIXME("STUB:(%p)\n",This);
761     return S_OK;
762 }
763 
764 static HRESULT WINAPI TextStoreACPSink_OnSelectionChange(ITextStoreACPSink *iface)
765 {
766     Context *This = impl_from_ITextStoreACPSink(iface);
767     FIXME("STUB:(%p)\n",This);
768     return S_OK;
769 }
770 
771 static HRESULT WINAPI TextStoreACPSink_OnLayoutChange(ITextStoreACPSink *iface,
772     TsLayoutCode lcode, TsViewCookie vcView)
773 {
774     Context *This = impl_from_ITextStoreACPSink(iface);
775     FIXME("STUB:(%p)\n",This);
776     return S_OK;
777 }
778 
779 static HRESULT WINAPI TextStoreACPSink_OnStatusChange(ITextStoreACPSink *iface,
780         DWORD dwFlags)
781 {
782     Context *This = impl_from_ITextStoreACPSink(iface);
783     HRESULT hr, hrSession;
784 
785     TRACE("(%p) %x\n",This, dwFlags);
786 
787     if (!This->pITextStoreACP)
788     {
789         FIXME("Context does not have a ITextStoreACP\n");
790         return E_NOTIMPL;
791     }
792 
793     hr = ITextStoreACP_RequestLock(This->pITextStoreACP, TS_LF_READ, &hrSession);
794 
795     if(SUCCEEDED(hr) && SUCCEEDED(hrSession))
796         This->documentStatus.dwDynamicFlags = dwFlags;
797 
798     return S_OK;
799 }
800 
801 static HRESULT WINAPI TextStoreACPSink_OnAttrsChange(ITextStoreACPSink *iface,
802         LONG acpStart, LONG acpEnd, ULONG cAttrs, const TS_ATTRID *paAttrs)
803 {
804     Context *This = impl_from_ITextStoreACPSink(iface);
805     FIXME("STUB:(%p)\n",This);
806     return E_NOTIMPL;
807 }
808 
809 static HRESULT WINAPI TextStoreACPSink_OnLockGranted(ITextStoreACPSink *iface,
810         DWORD dwLockFlags)
811 {
812     Context *This = impl_from_ITextStoreACPSink(iface);
813     HRESULT hr;
814     EditCookie *cookie,*sinkcookie;
815     TfEditCookie ec;
816     struct list *cursor;
817 
818     TRACE("(%p) %x\n",This, dwLockFlags);
819 
820     if (!This->currentEditSession)
821     {
822         FIXME("OnLockGranted called for something other than an EditSession\n");
823         return S_OK;
824     }
825 
826     cookie = HeapAlloc(GetProcessHeap(),0,sizeof(EditCookie));
827     if (!cookie)
828         return E_OUTOFMEMORY;
829 
830     sinkcookie = HeapAlloc(GetProcessHeap(),0,sizeof(EditCookie));
831     if (!sinkcookie)
832     {
833         HeapFree(GetProcessHeap(), 0, cookie);
834         return E_OUTOFMEMORY;
835     }
836 
837     cookie->lockType = dwLockFlags;
838     cookie->pOwningContext = This;
839     ec = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE, cookie);
840 
841     hr = ITfEditSession_DoEditSession(This->currentEditSession, ec);
842 
843     if ((dwLockFlags&TS_LF_READWRITE) == TS_LF_READWRITE)
844     {
845         ITfTextEditSink *sink;
846         TfEditCookie sc;
847 
848         sinkcookie->lockType = TS_LF_READ;
849         sinkcookie->pOwningContext = This;
850         sc = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE, sinkcookie);
851 
852         /*TODO: implement ITfEditRecord */
853         SINK_FOR_EACH(cursor, &This->pTextEditSink, ITfTextEditSink, sink)
854         {
855             ITfTextEditSink_OnEndEdit(sink, (ITfContext*) &This->ITfContext_iface, sc, NULL);
856         }
857         sinkcookie = remove_Cookie(sc);
858     }
859     HeapFree(GetProcessHeap(),0,sinkcookie);
860 
861     ITfEditSession_Release(This->currentEditSession);
862     This->currentEditSession = NULL;
863 
864     /* Edit Cookie is only valid during the edit session */
865     cookie = remove_Cookie(ec);
866     HeapFree(GetProcessHeap(),0,cookie);
867 
868     return hr;
869 }
870 
871 static HRESULT WINAPI TextStoreACPSink_OnStartEditTransaction(ITextStoreACPSink *iface)
872 {
873     Context *This = impl_from_ITextStoreACPSink(iface);
874     FIXME("STUB:(%p)\n",This);
875     return E_NOTIMPL;
876 }
877 
878 static HRESULT WINAPI TextStoreACPSink_OnEndEditTransaction(ITextStoreACPSink *iface)
879 {
880     Context *This = impl_from_ITextStoreACPSink(iface);
881     FIXME("STUB:(%p)\n",This);
882     return E_NOTIMPL;
883 }
884 
885 static const ITextStoreACPSinkVtbl TextStoreACPSinkVtbl =
886 {
887     TextStoreACPSink_QueryInterface,
888     TextStoreACPSink_AddRef,
889     TextStoreACPSink_Release,
890     TextStoreACPSink_OnTextChange,
891     TextStoreACPSink_OnSelectionChange,
892     TextStoreACPSink_OnLayoutChange,
893     TextStoreACPSink_OnStatusChange,
894     TextStoreACPSink_OnAttrsChange,
895     TextStoreACPSink_OnLockGranted,
896     TextStoreACPSink_OnStartEditTransaction,
897     TextStoreACPSink_OnEndEditTransaction
898 };
899 
900 static HRESULT WINAPI TextStoreACPServices_QueryInterface(ITextStoreACPServices *iface, REFIID riid, void **obj)
901 {
902     Context *This = impl_from_ITextStoreACPServices(iface);
903     return ITextStoreACPSink_QueryInterface(&This->ITextStoreACPSink_iface, riid, obj);
904 }
905 
906 static ULONG WINAPI TextStoreACPServices_AddRef(ITextStoreACPServices *iface)
907 {
908     Context *This = impl_from_ITextStoreACPServices(iface);
909     return ITextStoreACPSink_AddRef(&This->ITextStoreACPSink_iface);
910 }
911 
912 static ULONG WINAPI TextStoreACPServices_Release(ITextStoreACPServices *iface)
913 {
914     Context *This = impl_from_ITextStoreACPServices(iface);
915     return ITextStoreACPSink_Release(&This->ITextStoreACPSink_iface);
916 }
917 
918 static HRESULT WINAPI TextStoreACPServices_Serialize(ITextStoreACPServices *iface, ITfProperty *prop, ITfRange *range,
919     TF_PERSISTENT_PROPERTY_HEADER_ACP *header, IStream *stream)
920 {
921     Context *This = impl_from_ITextStoreACPServices(iface);
922 
923     FIXME("stub: %p %p %p %p %p\n", This, prop, range, header, stream);
924 
925     return E_NOTIMPL;
926 }
927 
928 static HRESULT WINAPI TextStoreACPServices_Unserialize(ITextStoreACPServices *iface, ITfProperty *prop,
929     const TF_PERSISTENT_PROPERTY_HEADER_ACP *header, IStream *stream, ITfPersistentPropertyLoaderACP *loader)
930 {
931     Context *This = impl_from_ITextStoreACPServices(iface);
932 
933     FIXME("stub: %p %p %p %p %p\n", This, prop, header, stream, loader);
934 
935     return E_NOTIMPL;
936 }
937 
938 static HRESULT WINAPI TextStoreACPServices_ForceLoadProperty(ITextStoreACPServices *iface, ITfProperty *prop)
939 {
940     Context *This = impl_from_ITextStoreACPServices(iface);
941 
942     FIXME("stub: %p %p\n", This, prop);
943 
944     return E_NOTIMPL;
945 }
946 
947 static HRESULT WINAPI TextStoreACPServices_CreateRange(ITextStoreACPServices *iface,
948     LONG start, LONG end, ITfRangeACP **range)
949 {
950     Context *This = impl_from_ITextStoreACPServices(iface);
951 
952     FIXME("stub: %p %d %d %p\n", This, start, end, range);
953 
954     return S_OK;
955 }
956 
957 static const ITextStoreACPServicesVtbl TextStoreACPServicesVtbl =
958 {
959     TextStoreACPServices_QueryInterface,
960     TextStoreACPServices_AddRef,
961     TextStoreACPServices_Release,
962     TextStoreACPServices_Serialize,
963     TextStoreACPServices_Unserialize,
964     TextStoreACPServices_ForceLoadProperty,
965     TextStoreACPServices_CreateRange
966 };
967 
968 HRESULT Context_Constructor(TfClientId tidOwner, IUnknown *punk, ITfDocumentMgr *mgr, ITfContext **ppOut, TfEditCookie *pecTextStore)
969 {
970     Context *This;
971     EditCookie *cookie;
972 
973     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Context));
974     if (This == NULL)
975         return E_OUTOFMEMORY;
976 
977     cookie = HeapAlloc(GetProcessHeap(),0,sizeof(EditCookie));
978     if (cookie == NULL)
979     {
980         HeapFree(GetProcessHeap(),0,This);
981         return E_OUTOFMEMORY;
982     }
983 
984     TRACE("(%p) %x %p %p %p\n",This, tidOwner, punk, ppOut, pecTextStore);
985 
986     This->ITfContext_iface.lpVtbl= &ContextVtbl;
987     This->ITfSource_iface.lpVtbl = &ContextSourceVtbl;
988     This->ITfInsertAtSelection_iface.lpVtbl = &InsertAtSelectionVtbl;
989     This->ITfSourceSingle_iface.lpVtbl = &ContextSourceSingleVtbl;
990     This->ITextStoreACPSink_iface.lpVtbl = &TextStoreACPSinkVtbl;
991     This->ITextStoreACPServices_iface.lpVtbl = &TextStoreACPServicesVtbl;
992     This->refCount = 1;
993     This->tidOwner = tidOwner;
994     This->connected = FALSE;
995     This->manager = mgr;
996 
997     CompartmentMgr_Constructor((IUnknown*)&This->ITfContext_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr);
998 
999     cookie->lockType = TF_ES_READ;
1000     cookie->pOwningContext = This;
1001 
1002     if (punk)
1003     {
1004         IUnknown_QueryInterface(punk, &IID_ITextStoreACP,
1005                           (LPVOID*)&This->pITextStoreACP);
1006 
1007         IUnknown_QueryInterface(punk, &IID_ITfContextOwnerCompositionSink,
1008                                 (LPVOID*)&This->pITfContextOwnerCompositionSink);
1009 
1010         if (!This->pITextStoreACP && !This->pITfContextOwnerCompositionSink)
1011             FIXME("Unhandled pUnk\n");
1012     }
1013 
1014     This->defaultCookie = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE,cookie);
1015     *pecTextStore = This->defaultCookie;
1016 
1017     list_init(&This->pContextKeyEventSink);
1018     list_init(&This->pEditTransactionSink);
1019     list_init(&This->pStatusSink);
1020     list_init(&This->pTextEditSink);
1021     list_init(&This->pTextLayoutSink);
1022 
1023     *ppOut = &This->ITfContext_iface;
1024     TRACE("returning %p\n", *ppOut);
1025 
1026     return S_OK;
1027 }
1028 
1029 HRESULT Context_Initialize(ITfContext *iface, ITfDocumentMgr *manager)
1030 {
1031     Context *This = impl_from_ITfContext(iface);
1032 
1033     if (This->pITextStoreACP)
1034         ITextStoreACP_AdviseSink(This->pITextStoreACP, &IID_ITextStoreACPSink,
1035             (IUnknown*)&This->ITextStoreACPSink_iface, TS_AS_ALL_SINKS);
1036     This->connected = TRUE;
1037     This->manager = manager;
1038     return S_OK;
1039 }
1040 
1041 HRESULT Context_Uninitialize(ITfContext *iface)
1042 {
1043     Context *This = impl_from_ITfContext(iface);
1044 
1045     if (This->pITextStoreACP)
1046         ITextStoreACP_UnadviseSink(This->pITextStoreACP, (IUnknown*)&This->ITextStoreACPSink_iface);
1047     This->connected = FALSE;
1048     This->manager = NULL;
1049     return S_OK;
1050 }
1051