msctf: Framework for ITfInsertAtSelection in ITfContext.
[wine] / dlls / msctf / context.c
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 #include "wine/list.h"
39
40 #include "msctf.h"
41 #include "msctf_internal.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
44
45 typedef struct tagContextSink {
46     struct list         entry;
47     union {
48         /* Context Sinks */
49         IUnknown            *pIUnknown;
50         /* ITfContextKeyEventSink  *pITfContextKeyEventSink; */
51         /* ITfEditTransactionSink  *pITfEditTransactionSink; */
52         /* ITfStatusSink           *pITfStatusSink; */
53         ITfTextEditSink     *pITfTextEditSink;
54         /* ITfTextLayoutSink       *pITfTextLayoutSink; */
55     } interfaces;
56 } ContextSink;
57
58 typedef struct tagContext {
59     const ITfContextVtbl *ContextVtbl;
60     const ITfSourceVtbl *SourceVtbl;
61     /* const ITfContextCompositionVtbl *ContextCompositionVtbl; */
62     /* const ITfContextOwnerCompositionServicesVtbl *ContextOwnerCompositionServicesVtbl; */
63     /* const ITfContextOwnerServicesVtbl *ContextOwnerServicesVtbl; */
64     const ITfInsertAtSelectionVtbl *InsertAtSelectionVtbl;
65     /* const ITfMouseTrackerVtbl *MouseTrackerVtbl; */
66     /* const ITfQueryEmbeddedVtbl *QueryEmbeddedVtbl; */
67     /* const ITfSourceSingleVtbl *SourceSingleVtbl; */
68     LONG refCount;
69     BOOL connected;
70
71     TfClientId tidOwner;
72     TfEditCookie defaultCookie;
73     TS_STATUS documentStatus;
74
75     ITextStoreACP   *pITextStoreACP;
76     ITfContextOwnerCompositionSink *pITfContextOwnerCompositionSink;
77
78     ITextStoreACPSink *pITextStoreACPSink;
79     ITfEditSession* currentEditSession;
80
81     /* kept as separate lists to reduce unnecessary iterations */
82     struct list     pContextKeyEventSink;
83     struct list     pEditTransactionSink;
84     struct list     pStatusSink;
85     struct list     pTextEditSink;
86     struct list     pTextLayoutSink;
87
88 } Context;
89
90 typedef struct tagEditCookie {
91     DWORD lockType;
92     Context *pOwningContext;
93 } EditCookie;
94
95 typedef struct tagTextStoreACPSink {
96     const ITextStoreACPSinkVtbl *TextStoreACPSinkVtbl;
97     /* const ITextStoreACPServicesVtbl *TextStoreACPServicesVtbl; */
98     LONG refCount;
99
100     Context *pContext;
101 } TextStoreACPSink;
102
103
104 static HRESULT TextStoreACPSink_Constructor(ITextStoreACPSink **ppOut, Context *pContext);
105
106 static inline Context *impl_from_ITfSourceVtbl(ITfSource *iface)
107 {
108     return (Context *)((char *)iface - FIELD_OFFSET(Context,SourceVtbl));
109 }
110
111 static inline Context *impl_from_ITfInsertAtSelectionVtbl(ITfInsertAtSelection*iface)
112 {
113     return (Context *)((char *)iface - FIELD_OFFSET(Context,InsertAtSelectionVtbl));
114 }
115
116 static void free_sink(ContextSink *sink)
117 {
118         IUnknown_Release(sink->interfaces.pIUnknown);
119         HeapFree(GetProcessHeap(),0,sink);
120 }
121
122 static void Context_Destructor(Context *This)
123 {
124     struct list *cursor, *cursor2;
125     EditCookie *cookie;
126     TRACE("destroying %p\n", This);
127
128     if (This->pITextStoreACPSink)
129     {
130         ITextStoreACP_UnadviseSink(This->pITextStoreACP, (IUnknown*)This->pITextStoreACPSink);
131         ITextStoreACPSink_Release(This->pITextStoreACPSink);
132     }
133
134     if (This->pITextStoreACP)
135         ITextStoreACPSink_Release(This->pITextStoreACP);
136
137     if (This->pITfContextOwnerCompositionSink)
138         ITextStoreACPSink_Release(This->pITfContextOwnerCompositionSink);
139
140     if (This->defaultCookie)
141     {
142         cookie = remove_Cookie(This->defaultCookie);
143         HeapFree(GetProcessHeap(),0,cookie);
144         This->defaultCookie = 0;
145     }
146
147     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pContextKeyEventSink)
148     {
149         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
150         list_remove(cursor);
151         free_sink(sink);
152     }
153     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pEditTransactionSink)
154     {
155         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
156         list_remove(cursor);
157         free_sink(sink);
158     }
159     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pStatusSink)
160     {
161         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
162         list_remove(cursor);
163         free_sink(sink);
164     }
165     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pTextEditSink)
166     {
167         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
168         list_remove(cursor);
169         free_sink(sink);
170     }
171     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pTextLayoutSink)
172     {
173         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
174         list_remove(cursor);
175         free_sink(sink);
176     }
177
178     HeapFree(GetProcessHeap(),0,This);
179 }
180
181 static HRESULT WINAPI Context_QueryInterface(ITfContext *iface, REFIID iid, LPVOID *ppvOut)
182 {
183     Context *This = (Context *)iface;
184     *ppvOut = NULL;
185
186     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfContext))
187     {
188         *ppvOut = This;
189     }
190     else if (IsEqualIID(iid, &IID_ITfSource))
191     {
192         *ppvOut = &This->SourceVtbl;
193     }
194     else if (IsEqualIID(iid, &IID_ITfInsertAtSelection))
195     {
196         *ppvOut = &This->InsertAtSelectionVtbl;
197     }
198
199     if (*ppvOut)
200     {
201         IUnknown_AddRef(iface);
202         return S_OK;
203     }
204
205     WARN("unsupported interface: %s\n", debugstr_guid(iid));
206     return E_NOINTERFACE;
207 }
208
209 static ULONG WINAPI Context_AddRef(ITfContext *iface)
210 {
211     Context *This = (Context *)iface;
212     return InterlockedIncrement(&This->refCount);
213 }
214
215 static ULONG WINAPI Context_Release(ITfContext *iface)
216 {
217     Context *This = (Context *)iface;
218     ULONG ret;
219
220     ret = InterlockedDecrement(&This->refCount);
221     if (ret == 0)
222         Context_Destructor(This);
223     return ret;
224 }
225
226 /*****************************************************
227  * ITfContext functions
228  *****************************************************/
229 static HRESULT WINAPI Context_RequestEditSession (ITfContext *iface,
230         TfClientId tid, ITfEditSession *pes, DWORD dwFlags,
231         HRESULT *phrSession)
232 {
233     HRESULT hr;
234     Context *This = (Context *)iface;
235     DWORD  dwLockFlags = 0x0;
236
237     TRACE("(%p) %i %p %x %p\n",This, tid, pes, dwFlags, phrSession);
238
239     if (!(dwFlags & TF_ES_READ) && !(dwFlags & TF_ES_READWRITE))
240     {
241         *phrSession = E_FAIL;
242         return E_INVALIDARG;
243     }
244
245     if (!This->pITextStoreACP)
246     {
247         FIXME("No ITextStoreACP avaliable\n");
248         *phrSession = E_FAIL;
249         return E_FAIL;
250     }
251
252     if (!(dwFlags & TF_ES_ASYNC))
253         dwLockFlags |= TS_LF_SYNC;
254
255     if ((dwFlags & TF_ES_READWRITE) == TF_ES_READWRITE)
256         dwLockFlags |= TS_LF_READWRITE;
257     else if (dwFlags & TF_ES_READ)
258         dwLockFlags |= TS_LF_READ;
259
260     if (!This->documentStatus.dwDynamicFlags)
261         ITextStoreACP_GetStatus(This->pITextStoreACP, &This->documentStatus);
262
263     if (((dwFlags & TF_ES_READWRITE) == TF_ES_READWRITE) && (This->documentStatus.dwDynamicFlags & TS_SD_READONLY))
264     {
265         *phrSession = TS_E_READONLY;
266         return S_OK;
267     }
268
269     if (FAILED (ITfEditSession_QueryInterface(pes, &IID_ITfEditSession, (LPVOID*)&This->currentEditSession)))
270     {
271         *phrSession = E_FAIL;
272         return E_INVALIDARG;
273     }
274
275     hr = ITextStoreACP_RequestLock(This->pITextStoreACP, dwLockFlags, phrSession);
276
277     return hr;
278 }
279
280 static HRESULT WINAPI Context_InWriteSession (ITfContext *iface,
281          TfClientId tid,
282          BOOL *pfWriteSession)
283 {
284     Context *This = (Context *)iface;
285     FIXME("STUB:(%p)\n",This);
286     return E_NOTIMPL;
287 }
288
289 static HRESULT WINAPI Context_GetSelection (ITfContext *iface,
290         TfEditCookie ec, ULONG ulIndex, ULONG ulCount,
291         TF_SELECTION *pSelection, ULONG *pcFetched)
292 {
293     Context *This = (Context *)iface;
294     EditCookie *cookie;
295     ULONG count, i;
296     ULONG totalFetched = 0;
297     HRESULT hr = S_OK;
298
299     if (!pSelection || !pcFetched)
300         return E_INVALIDARG;
301
302     *pcFetched = 0;
303
304     if (!This->connected)
305         return TF_E_DISCONNECTED;
306
307     if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE)
308         return TF_E_NOLOCK;
309
310     if (!This->pITextStoreACP)
311     {
312         FIXME("Context does not have a ITextStoreACP\n");
313         return E_NOTIMPL;
314     }
315
316     cookie = get_Cookie_data(ec);
317
318     if (ulIndex == TF_DEFAULT_SELECTION)
319         count = 1;
320     else
321         count = ulCount;
322
323     for (i = 0; i < count; i++)
324     {
325         DWORD fetched;
326         TS_SELECTION_ACP acps;
327
328         hr = ITextStoreACP_GetSelection(This->pITextStoreACP, ulIndex + i,
329                 1, &acps, &fetched);
330
331         if (hr == TS_E_NOLOCK)
332             return TF_E_NOLOCK;
333         else if (SUCCEEDED(hr))
334         {
335             pSelection[totalFetched].style.ase = acps.style.ase;
336             pSelection[totalFetched].style.fInterimChar = acps.style.fInterimChar;
337             Range_Constructor(iface, This->pITextStoreACP, cookie->lockType, acps.acpStart, acps.acpEnd, &pSelection[totalFetched].range);
338             totalFetched ++;
339         }
340         else
341             break;
342     }
343
344     *pcFetched = totalFetched;
345
346     return hr;
347 }
348
349 static HRESULT WINAPI Context_SetSelection (ITfContext *iface,
350         TfEditCookie ec, ULONG ulCount, const TF_SELECTION *pSelection)
351 {
352     Context *This = (Context *)iface;
353     FIXME("STUB:(%p)\n",This);
354     return E_NOTIMPL;
355 }
356
357 static HRESULT WINAPI Context_GetStart (ITfContext *iface,
358         TfEditCookie ec, ITfRange **ppStart)
359 {
360     Context *This = (Context *)iface;
361     EditCookie *cookie;
362     TRACE("(%p) %i %p\n",This,ec,ppStart);
363
364     if (!ppStart)
365         return E_INVALIDARG;
366
367     *ppStart = NULL;
368
369     if (!This->connected)
370         return TF_E_DISCONNECTED;
371
372     if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE)
373         return TF_E_NOLOCK;
374
375     cookie = get_Cookie_data(ec);
376     return Range_Constructor(iface, This->pITextStoreACP, cookie->lockType, 0, 0, ppStart);
377 }
378
379 static HRESULT WINAPI Context_GetEnd (ITfContext *iface,
380         TfEditCookie ec, ITfRange **ppEnd)
381 {
382     Context *This = (Context *)iface;
383     EditCookie *cookie;
384     LONG end;
385     TRACE("(%p) %i %p\n",This,ec,ppEnd);
386
387     if (!ppEnd)
388         return E_INVALIDARG;
389
390     *ppEnd = NULL;
391
392     if (!This->connected)
393         return TF_E_DISCONNECTED;
394
395     if (get_Cookie_magic(ec)!=COOKIE_MAGIC_EDITCOOKIE)
396         return TF_E_NOLOCK;
397
398     if (!This->pITextStoreACP)
399     {
400         FIXME("Context does not have a ITextStoreACP\n");
401         return E_NOTIMPL;
402     }
403
404     cookie = get_Cookie_data(ec);
405     ITextStoreACP_GetEndACP(This->pITextStoreACP,&end);
406
407     return Range_Constructor(iface, This->pITextStoreACP, cookie->lockType, end, end, ppEnd);
408 }
409
410 static HRESULT WINAPI Context_GetActiveView (ITfContext *iface,
411   ITfContextView **ppView)
412 {
413     Context *This = (Context *)iface;
414     FIXME("STUB:(%p)\n",This);
415     return E_NOTIMPL;
416 }
417
418 static HRESULT WINAPI Context_EnumViews (ITfContext *iface,
419         IEnumTfContextViews **ppEnum)
420 {
421     Context *This = (Context *)iface;
422     FIXME("STUB:(%p)\n",This);
423     return E_NOTIMPL;
424 }
425
426 static HRESULT WINAPI Context_GetStatus (ITfContext *iface,
427         TF_STATUS *pdcs)
428 {
429     Context *This = (Context *)iface;
430     FIXME("STUB:(%p)\n",This);
431     return E_NOTIMPL;
432 }
433
434 static HRESULT WINAPI Context_GetProperty (ITfContext *iface,
435         REFGUID guidProp, ITfProperty **ppProp)
436 {
437     Context *This = (Context *)iface;
438     FIXME("STUB:(%p)\n",This);
439     return E_NOTIMPL;
440 }
441
442 static HRESULT WINAPI Context_GetAppProperty (ITfContext *iface,
443         REFGUID guidProp, ITfReadOnlyProperty **ppProp)
444 {
445     Context *This = (Context *)iface;
446     FIXME("STUB:(%p)\n",This);
447     return E_NOTIMPL;
448 }
449
450 static HRESULT WINAPI Context_TrackProperties (ITfContext *iface,
451         const GUID **prgProp, ULONG cProp, const GUID **prgAppProp,
452         ULONG cAppProp, ITfReadOnlyProperty **ppProperty)
453 {
454     Context *This = (Context *)iface;
455     FIXME("STUB:(%p)\n",This);
456     return E_NOTIMPL;
457 }
458
459 static HRESULT WINAPI Context_EnumProperties (ITfContext *iface,
460         IEnumTfProperties **ppEnum)
461 {
462     Context *This = (Context *)iface;
463     FIXME("STUB:(%p)\n",This);
464     return E_NOTIMPL;
465 }
466
467 static HRESULT WINAPI Context_GetDocumentMgr (ITfContext *iface,
468         ITfDocumentMgr **ppDm)
469 {
470     Context *This = (Context *)iface;
471     FIXME("STUB:(%p)\n",This);
472     return E_NOTIMPL;
473 }
474
475 static HRESULT WINAPI Context_CreateRangeBackup (ITfContext *iface,
476         TfEditCookie ec, ITfRange *pRange, ITfRangeBackup **ppBackup)
477 {
478     Context *This = (Context *)iface;
479     FIXME("STUB:(%p)\n",This);
480     return E_NOTIMPL;
481 }
482
483 static const ITfContextVtbl Context_ContextVtbl =
484 {
485     Context_QueryInterface,
486     Context_AddRef,
487     Context_Release,
488
489     Context_RequestEditSession,
490     Context_InWriteSession,
491     Context_GetSelection,
492     Context_SetSelection,
493     Context_GetStart,
494     Context_GetEnd,
495     Context_GetActiveView,
496     Context_EnumViews,
497     Context_GetStatus,
498     Context_GetProperty,
499     Context_GetAppProperty,
500     Context_TrackProperties,
501     Context_EnumProperties,
502     Context_GetDocumentMgr,
503     Context_CreateRangeBackup
504 };
505
506 static HRESULT WINAPI Source_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
507 {
508     Context *This = impl_from_ITfSourceVtbl(iface);
509     return Context_QueryInterface((ITfContext *)This, iid, *ppvOut);
510 }
511
512 static ULONG WINAPI Source_AddRef(ITfSource *iface)
513 {
514     Context *This = impl_from_ITfSourceVtbl(iface);
515     return Context_AddRef((ITfContext *)This);
516 }
517
518 static ULONG WINAPI Source_Release(ITfSource *iface)
519 {
520     Context *This = impl_from_ITfSourceVtbl(iface);
521     return Context_Release((ITfContext *)This);
522 }
523
524 /*****************************************************
525  * ITfSource functions
526  *****************************************************/
527 static WINAPI HRESULT ContextSource_AdviseSink(ITfSource *iface,
528         REFIID riid, IUnknown *punk, DWORD *pdwCookie)
529 {
530     ContextSink *es;
531     Context *This = impl_from_ITfSourceVtbl(iface);
532     TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);
533
534     if (!riid || !punk || !pdwCookie)
535         return E_INVALIDARG;
536
537     if (IsEqualIID(riid, &IID_ITfTextEditSink))
538     {
539         es = HeapAlloc(GetProcessHeap(),0,sizeof(ContextSink));
540         if (!es)
541             return E_OUTOFMEMORY;
542         if (FAILED(IUnknown_QueryInterface(punk, riid, (LPVOID *)&es->interfaces.pITfTextEditSink)))
543         {
544             HeapFree(GetProcessHeap(),0,es);
545             return CONNECT_E_CANNOTCONNECT;
546         }
547         list_add_head(&This->pTextEditSink ,&es->entry);
548         *pdwCookie = generate_Cookie(COOKIE_MAGIC_CONTEXTSINK, es);
549     }
550     else
551     {
552         FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
553         return E_NOTIMPL;
554     }
555
556     TRACE("cookie %x\n",*pdwCookie);
557     return S_OK;
558 }
559
560 static WINAPI HRESULT ContextSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
561 {
562     ContextSink *sink;
563     Context *This = impl_from_ITfSourceVtbl(iface);
564
565     TRACE("(%p) %x\n",This,pdwCookie);
566
567     if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_CONTEXTSINK)
568         return E_INVALIDARG;
569
570     sink = (ContextSink*)remove_Cookie(pdwCookie);
571     if (!sink)
572         return CONNECT_E_NOCONNECTION;
573
574     list_remove(&sink->entry);
575     free_sink(sink);
576
577     return S_OK;
578 }
579
580 static const ITfSourceVtbl Context_SourceVtbl =
581 {
582     Source_QueryInterface,
583     Source_AddRef,
584     Source_Release,
585
586     ContextSource_AdviseSink,
587     ContextSource_UnadviseSink,
588 };
589
590 /*****************************************************
591  * ITfInsertAtSelection functions
592  *****************************************************/
593 static HRESULT WINAPI InsertAtSelection_QueryInterface(ITfInsertAtSelection *iface, REFIID iid, LPVOID *ppvOut)
594 {
595     Context *This = impl_from_ITfInsertAtSelectionVtbl(iface);
596     return Context_QueryInterface((ITfContext *)This, iid, *ppvOut);
597 }
598
599 static ULONG WINAPI InsertAtSelection_AddRef(ITfInsertAtSelection *iface)
600 {
601     Context *This = impl_from_ITfInsertAtSelectionVtbl(iface);
602     return Context_AddRef((ITfContext *)This);
603 }
604
605 static ULONG WINAPI InsertAtSelection_Release(ITfInsertAtSelection *iface)
606 {
607     Context *This = impl_from_ITfInsertAtSelectionVtbl(iface);
608     return Context_Release((ITfContext *)This);
609 }
610
611 static WINAPI HRESULT InsertAtSelection_InsertTextAtSelection(
612         ITfInsertAtSelection *iface, TfEditCookie ec, DWORD dwFlags,
613         const WCHAR *pchText, LONG cch, ITfRange **ppRange)
614 {
615     Context *This = impl_from_ITfInsertAtSelectionVtbl(iface);
616     FIXME("STUB:(%p)\n",This);
617     return E_NOTIMPL;
618 }
619
620 static WINAPI HRESULT InsertAtSelection_InsertEmbeddedAtSelection(
621         ITfInsertAtSelection *iface, TfEditCookie ec, DWORD dwFlags,
622         IDataObject *pDataObject, ITfRange **ppRange)
623 {
624     Context *This = impl_from_ITfInsertAtSelectionVtbl(iface);
625     FIXME("STUB:(%p)\n",This);
626     return E_NOTIMPL;
627 }
628
629 static const ITfInsertAtSelectionVtbl Context_InsertAtSelectionVtbl =
630 {
631     InsertAtSelection_QueryInterface,
632     InsertAtSelection_AddRef,
633     InsertAtSelection_Release,
634
635     InsertAtSelection_InsertTextAtSelection,
636     InsertAtSelection_InsertEmbeddedAtSelection,
637 };
638
639 HRESULT Context_Constructor(TfClientId tidOwner, IUnknown *punk, ITfContext **ppOut, TfEditCookie *pecTextStore)
640 {
641     Context *This;
642     EditCookie *cookie;
643
644     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Context));
645     if (This == NULL)
646         return E_OUTOFMEMORY;
647
648     cookie = HeapAlloc(GetProcessHeap(),0,sizeof(EditCookie));
649     if (cookie == NULL)
650     {
651         HeapFree(GetProcessHeap(),0,This);
652         return E_OUTOFMEMORY;
653     }
654
655     TRACE("(%p) %x %p %p %p\n",This, tidOwner, punk, ppOut, pecTextStore);
656
657     This->ContextVtbl= &Context_ContextVtbl;
658     This->SourceVtbl = &Context_SourceVtbl;
659     This->InsertAtSelectionVtbl = &Context_InsertAtSelectionVtbl;
660     This->refCount = 1;
661     This->tidOwner = tidOwner;
662     This->connected = FALSE;
663
664     cookie->lockType = TF_ES_READ;
665     cookie->pOwningContext = This;
666
667     if (punk)
668     {
669         IUnknown_QueryInterface(punk, &IID_ITextStoreACP,
670                           (LPVOID*)&This->pITextStoreACP);
671
672         IUnknown_QueryInterface(punk, &IID_ITfContextOwnerCompositionSink,
673                                 (LPVOID*)&This->pITfContextOwnerCompositionSink);
674
675         if (!This->pITextStoreACP && !This->pITfContextOwnerCompositionSink)
676             FIXME("Unhandled pUnk\n");
677     }
678
679     This->defaultCookie = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE,cookie);
680     *pecTextStore = This->defaultCookie;
681
682     list_init(&This->pContextKeyEventSink);
683     list_init(&This->pEditTransactionSink);
684     list_init(&This->pStatusSink);
685     list_init(&This->pTextEditSink);
686     list_init(&This->pTextLayoutSink);
687
688     *ppOut = (ITfContext*)This;
689     TRACE("returning %p\n", This);
690
691     return S_OK;
692 }
693
694 HRESULT Context_Initialize(ITfContext *iface)
695 {
696     Context *This = (Context *)iface;
697
698     if (This->pITextStoreACP)
699     {
700         if (SUCCEEDED(TextStoreACPSink_Constructor(&This->pITextStoreACPSink, This)))
701             ITextStoreACP_AdviseSink(This->pITextStoreACP, &IID_ITextStoreACPSink,
702                             (IUnknown*)This->pITextStoreACPSink, TS_AS_ALL_SINKS);
703     }
704     This->connected = TRUE;
705     return S_OK;
706 }
707
708 HRESULT Context_Uninitialize(ITfContext *iface)
709 {
710     Context *This = (Context *)iface;
711
712     if (This->pITextStoreACPSink)
713     {
714         ITextStoreACP_UnadviseSink(This->pITextStoreACP, (IUnknown*)This->pITextStoreACPSink);
715         if (ITextStoreACPSink_Release(This->pITextStoreACPSink) == 0)
716             This->pITextStoreACPSink = NULL;
717     }
718     This->connected = FALSE;
719     return S_OK;
720 }
721
722 /**************************************************************************
723  *  ITextStoreACPSink
724  **************************************************************************/
725
726 static void TextStoreACPSink_Destructor(TextStoreACPSink *This)
727 {
728     TRACE("destroying %p\n", This);
729     HeapFree(GetProcessHeap(),0,This);
730 }
731
732 static HRESULT WINAPI TextStoreACPSink_QueryInterface(ITextStoreACPSink *iface, REFIID iid, LPVOID *ppvOut)
733 {
734     TextStoreACPSink *This = (TextStoreACPSink *)iface;
735     *ppvOut = NULL;
736
737     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITextStoreACPSink))
738     {
739         *ppvOut = This;
740     }
741
742     if (*ppvOut)
743     {
744         IUnknown_AddRef(iface);
745         return S_OK;
746     }
747
748     WARN("unsupported interface: %s\n", debugstr_guid(iid));
749     return E_NOINTERFACE;
750 }
751
752 static ULONG WINAPI TextStoreACPSink_AddRef(ITextStoreACPSink *iface)
753 {
754     TextStoreACPSink *This = (TextStoreACPSink *)iface;
755     return InterlockedIncrement(&This->refCount);
756 }
757
758 static ULONG WINAPI TextStoreACPSink_Release(ITextStoreACPSink *iface)
759 {
760     TextStoreACPSink *This = (TextStoreACPSink *)iface;
761     ULONG ret;
762
763     ret = InterlockedDecrement(&This->refCount);
764     if (ret == 0)
765         TextStoreACPSink_Destructor(This);
766     return ret;
767 }
768
769 /*****************************************************
770  * ITextStoreACPSink functions
771  *****************************************************/
772
773 static HRESULT WINAPI TextStoreACPSink_OnTextChange(ITextStoreACPSink *iface,
774         DWORD dwFlags, const TS_TEXTCHANGE *pChange)
775 {
776     TextStoreACPSink *This = (TextStoreACPSink *)iface;
777     FIXME("STUB:(%p)\n",This);
778     return E_NOTIMPL;
779 }
780
781 static HRESULT WINAPI TextStoreACPSink_OnSelectionChange(ITextStoreACPSink *iface)
782 {
783     TextStoreACPSink *This = (TextStoreACPSink *)iface;
784     FIXME("STUB:(%p)\n",This);
785     return E_NOTIMPL;
786 }
787
788 static HRESULT WINAPI TextStoreACPSink_OnLayoutChange(ITextStoreACPSink *iface,
789     TsLayoutCode lcode, TsViewCookie vcView)
790 {
791     TextStoreACPSink *This = (TextStoreACPSink *)iface;
792     FIXME("STUB:(%p)\n",This);
793     return E_NOTIMPL;
794 }
795
796 static HRESULT WINAPI TextStoreACPSink_OnStatusChange(ITextStoreACPSink *iface,
797         DWORD dwFlags)
798 {
799     TextStoreACPSink *This = (TextStoreACPSink *)iface;
800     HRESULT hr, hrSession;
801
802     TRACE("(%p) %x\n",This, dwFlags);
803
804     if (!This->pContext)
805     {
806         ERR("No context?\n");
807         return E_FAIL;
808     }
809
810     if (!This->pContext->pITextStoreACP)
811     {
812         FIXME("Context does not have a ITextStoreACP\n");
813         return E_NOTIMPL;
814     }
815
816     hr = ITextStoreACP_RequestLock(This->pContext->pITextStoreACP, TS_LF_READ, &hrSession);
817
818     if(SUCCEEDED(hr) && SUCCEEDED(hrSession))
819         This->pContext->documentStatus.dwDynamicFlags = dwFlags;
820
821     return S_OK;
822 }
823
824 static HRESULT WINAPI TextStoreACPSink_OnAttrsChange(ITextStoreACPSink *iface,
825         LONG acpStart, LONG acpEnd, ULONG cAttrs, const TS_ATTRID *paAttrs)
826 {
827     TextStoreACPSink *This = (TextStoreACPSink *)iface;
828     FIXME("STUB:(%p)\n",This);
829     return E_NOTIMPL;
830 }
831
832 static HRESULT WINAPI TextStoreACPSink_OnLockGranted(ITextStoreACPSink *iface,
833         DWORD dwLockFlags)
834 {
835     TextStoreACPSink *This = (TextStoreACPSink *)iface;
836     HRESULT hr;
837     EditCookie *cookie;
838     TfEditCookie ec;
839
840     TRACE("(%p) %x\n",This, dwLockFlags);
841
842     if (!This->pContext)
843     {
844         ERR("OnLockGranted called without a context\n");
845         return E_FAIL;
846     }
847
848     if (!This->pContext->currentEditSession)
849     {
850         FIXME("OnLockGranted called for something other than an EditSession\n");
851         return S_OK;
852     }
853
854     cookie = HeapAlloc(GetProcessHeap(),0,sizeof(EditCookie));
855     if (!cookie)
856         return E_OUTOFMEMORY;
857
858     cookie->lockType = dwLockFlags;
859     cookie->pOwningContext = This->pContext;
860     ec = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE, cookie);
861
862     hr = ITfEditSession_DoEditSession(This->pContext->currentEditSession, ec);
863
864     ITfEditSession_Release(This->pContext->currentEditSession);
865     This->pContext->currentEditSession = NULL;
866
867     /* Edit Cookie is only valid during the edit session */
868     cookie = remove_Cookie(ec);
869     HeapFree(GetProcessHeap(),0,cookie);
870
871     return hr;
872 }
873
874 static HRESULT WINAPI TextStoreACPSink_OnStartEditTransaction(ITextStoreACPSink *iface)
875 {
876     TextStoreACPSink *This = (TextStoreACPSink *)iface;
877     FIXME("STUB:(%p)\n",This);
878     return E_NOTIMPL;
879 }
880
881 static HRESULT WINAPI TextStoreACPSink_OnEndEditTransaction(ITextStoreACPSink *iface)
882 {
883     TextStoreACPSink *This = (TextStoreACPSink *)iface;
884     FIXME("STUB:(%p)\n",This);
885     return E_NOTIMPL;
886 }
887
888 static const ITextStoreACPSinkVtbl TextStoreACPSink_TextStoreACPSinkVtbl =
889 {
890     TextStoreACPSink_QueryInterface,
891     TextStoreACPSink_AddRef,
892     TextStoreACPSink_Release,
893
894     TextStoreACPSink_OnTextChange,
895     TextStoreACPSink_OnSelectionChange,
896     TextStoreACPSink_OnLayoutChange,
897     TextStoreACPSink_OnStatusChange,
898     TextStoreACPSink_OnAttrsChange,
899     TextStoreACPSink_OnLockGranted,
900     TextStoreACPSink_OnStartEditTransaction,
901     TextStoreACPSink_OnEndEditTransaction
902 };
903
904 static HRESULT TextStoreACPSink_Constructor(ITextStoreACPSink **ppOut, Context *pContext)
905 {
906     TextStoreACPSink *This;
907
908     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextStoreACPSink));
909     if (This == NULL)
910         return E_OUTOFMEMORY;
911
912     This->TextStoreACPSinkVtbl= &TextStoreACPSink_TextStoreACPSinkVtbl;
913     This->refCount = 1;
914
915     This->pContext = pContext;
916
917     TRACE("returning %p\n", This);
918     *ppOut = (ITextStoreACPSink*)This;
919     return S_OK;
920 }