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