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