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