msctf: Add internal functions for managing active text services.
[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     LONG refCount;
62
63     TfClientId tidOwner;
64
65     ITextStoreACP   *pITextStoreACP;
66     ITfContextOwnerCompositionSink *pITfContextOwnerCompositionSink;
67
68     ITextStoreACPSink *pITextStoreACPSink;
69
70     /* kept as separate lists to reduce unnecessary iterations */
71     struct list     pContextKeyEventSink;
72     struct list     pEditTransactionSink;
73     struct list     pStatusSink;
74     struct list     pTextEditSink;
75     struct list     pTextLayoutSink;
76
77 } Context;
78
79
80 typedef struct tagTextStoreACPSink {
81     const ITextStoreACPSinkVtbl *TextStoreACPSinkVtbl;
82     LONG refCount;
83
84     Context *pContext;
85 } TextStoreACPSink;
86
87
88 static HRESULT TextStoreACPSink_Constructor(ITextStoreACPSink **ppOut, Context *pContext);
89
90 static inline Context *impl_from_ITfSourceVtbl(ITfSource *iface)
91 {
92     return (Context *)((char *)iface - FIELD_OFFSET(Context,SourceVtbl));
93 }
94
95 static void free_sink(ContextSink *sink)
96 {
97         IUnknown_Release(sink->interfaces.pIUnknown);
98         HeapFree(GetProcessHeap(),0,sink);
99 }
100
101 static void Context_Destructor(Context *This)
102 {
103     struct list *cursor, *cursor2;
104     TRACE("destroying %p\n", This);
105
106     if (This->pITextStoreACPSink)
107     {
108         ITextStoreACP_UnadviseSink(This->pITextStoreACP, (IUnknown*)This->pITextStoreACPSink);
109         ITextStoreACPSink_Release(This->pITextStoreACPSink);
110     }
111
112     if (This->pITextStoreACP)
113         ITextStoreACPSink_Release(This->pITextStoreACP);
114
115     if (This->pITfContextOwnerCompositionSink)
116         ITextStoreACPSink_Release(This->pITfContextOwnerCompositionSink);
117
118     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pContextKeyEventSink)
119     {
120         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
121         list_remove(cursor);
122         free_sink(sink);
123     }
124     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pEditTransactionSink)
125     {
126         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
127         list_remove(cursor);
128         free_sink(sink);
129     }
130     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pStatusSink)
131     {
132         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
133         list_remove(cursor);
134         free_sink(sink);
135     }
136     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pTextEditSink)
137     {
138         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
139         list_remove(cursor);
140         free_sink(sink);
141     }
142     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->pTextLayoutSink)
143     {
144         ContextSink* sink = LIST_ENTRY(cursor,ContextSink,entry);
145         list_remove(cursor);
146         free_sink(sink);
147     }
148
149     HeapFree(GetProcessHeap(),0,This);
150 }
151
152 static HRESULT WINAPI Context_QueryInterface(ITfContext *iface, REFIID iid, LPVOID *ppvOut)
153 {
154     Context *This = (Context *)iface;
155     *ppvOut = NULL;
156
157     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfContext))
158     {
159         *ppvOut = This;
160     }
161     else if (IsEqualIID(iid, &IID_ITfSource))
162     {
163         *ppvOut = &This->SourceVtbl;
164     }
165
166     if (*ppvOut)
167     {
168         IUnknown_AddRef(iface);
169         return S_OK;
170     }
171
172     WARN("unsupported interface: %s\n", debugstr_guid(iid));
173     return E_NOINTERFACE;
174 }
175
176 static ULONG WINAPI Context_AddRef(ITfContext *iface)
177 {
178     Context *This = (Context *)iface;
179     return InterlockedIncrement(&This->refCount);
180 }
181
182 static ULONG WINAPI Context_Release(ITfContext *iface)
183 {
184     Context *This = (Context *)iface;
185     ULONG ret;
186
187     ret = InterlockedDecrement(&This->refCount);
188     if (ret == 0)
189         Context_Destructor(This);
190     return ret;
191 }
192
193 /*****************************************************
194  * ITfContext functions
195  *****************************************************/
196 static HRESULT WINAPI Context_RequestEditSession (ITfContext *iface,
197         TfClientId tid, ITfEditSession *pes, DWORD dwFlags,
198         HRESULT *phrSession)
199 {
200     Context *This = (Context *)iface;
201     FIXME("STUB:(%p)\n",This);
202     return E_NOTIMPL;
203 }
204
205 static HRESULT WINAPI Context_InWriteSession (ITfContext *iface,
206          TfClientId tid,
207          BOOL *pfWriteSession)
208 {
209     Context *This = (Context *)iface;
210     FIXME("STUB:(%p)\n",This);
211     return E_NOTIMPL;
212 }
213
214 static HRESULT WINAPI Context_GetSelection (ITfContext *iface,
215         TfEditCookie ec, ULONG ulIndex, ULONG ulCount,
216         TF_SELECTION *pSelection, ULONG *pcFetched)
217 {
218     Context *This = (Context *)iface;
219     FIXME("STUB:(%p)\n",This);
220     return E_NOTIMPL;
221 }
222
223 static HRESULT WINAPI Context_SetSelection (ITfContext *iface,
224         TfEditCookie ec, ULONG ulCount, const TF_SELECTION *pSelection)
225 {
226     Context *This = (Context *)iface;
227     FIXME("STUB:(%p)\n",This);
228     return E_NOTIMPL;
229 }
230
231 static HRESULT WINAPI Context_GetStart (ITfContext *iface,
232         TfEditCookie ec, ITfRange **ppStart)
233 {
234     Context *This = (Context *)iface;
235     FIXME("STUB:(%p)\n",This);
236     return E_NOTIMPL;
237 }
238
239 static HRESULT WINAPI Context_GetEnd (ITfContext *iface,
240         TfEditCookie ec, ITfRange **ppEnd)
241 {
242     Context *This = (Context *)iface;
243     FIXME("STUB:(%p)\n",This);
244     return E_NOTIMPL;
245 }
246
247 static HRESULT WINAPI Context_GetActiveView (ITfContext *iface,
248   ITfContextView **ppView)
249 {
250     Context *This = (Context *)iface;
251     FIXME("STUB:(%p)\n",This);
252     return E_NOTIMPL;
253 }
254
255 static HRESULT WINAPI Context_EnumViews (ITfContext *iface,
256         IEnumTfContextViews **ppEnum)
257 {
258     Context *This = (Context *)iface;
259     FIXME("STUB:(%p)\n",This);
260     return E_NOTIMPL;
261 }
262
263 static HRESULT WINAPI Context_GetStatus (ITfContext *iface,
264         TF_STATUS *pdcs)
265 {
266     Context *This = (Context *)iface;
267     FIXME("STUB:(%p)\n",This);
268     return E_NOTIMPL;
269 }
270
271 static HRESULT WINAPI Context_GetProperty (ITfContext *iface,
272         REFGUID guidProp, ITfProperty **ppProp)
273 {
274     Context *This = (Context *)iface;
275     FIXME("STUB:(%p)\n",This);
276     return E_NOTIMPL;
277 }
278
279 static HRESULT WINAPI Context_GetAppProperty (ITfContext *iface,
280         REFGUID guidProp, ITfReadOnlyProperty **ppProp)
281 {
282     Context *This = (Context *)iface;
283     FIXME("STUB:(%p)\n",This);
284     return E_NOTIMPL;
285 }
286
287 static HRESULT WINAPI Context_TrackProperties (ITfContext *iface,
288         const GUID **prgProp, ULONG cProp, const GUID **prgAppProp,
289         ULONG cAppProp, ITfReadOnlyProperty **ppProperty)
290 {
291     Context *This = (Context *)iface;
292     FIXME("STUB:(%p)\n",This);
293     return E_NOTIMPL;
294 }
295
296 static HRESULT WINAPI Context_EnumProperties (ITfContext *iface,
297         IEnumTfProperties **ppEnum)
298 {
299     Context *This = (Context *)iface;
300     FIXME("STUB:(%p)\n",This);
301     return E_NOTIMPL;
302 }
303
304 static HRESULT WINAPI Context_GetDocumentMgr (ITfContext *iface,
305         ITfDocumentMgr **ppDm)
306 {
307     Context *This = (Context *)iface;
308     FIXME("STUB:(%p)\n",This);
309     return E_NOTIMPL;
310 }
311
312 static HRESULT WINAPI Context_CreateRangeBackup (ITfContext *iface,
313         TfEditCookie ec, ITfRange *pRange, ITfRangeBackup **ppBackup)
314 {
315     Context *This = (Context *)iface;
316     FIXME("STUB:(%p)\n",This);
317     return E_NOTIMPL;
318 }
319
320 static const ITfContextVtbl Context_ContextVtbl =
321 {
322     Context_QueryInterface,
323     Context_AddRef,
324     Context_Release,
325
326     Context_RequestEditSession,
327     Context_InWriteSession,
328     Context_GetSelection,
329     Context_SetSelection,
330     Context_GetStart,
331     Context_GetEnd,
332     Context_GetActiveView,
333     Context_EnumViews,
334     Context_GetStatus,
335     Context_GetProperty,
336     Context_GetAppProperty,
337     Context_TrackProperties,
338     Context_EnumProperties,
339     Context_GetDocumentMgr,
340     Context_CreateRangeBackup
341 };
342
343 static HRESULT WINAPI Source_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
344 {
345     Context *This = impl_from_ITfSourceVtbl(iface);
346     return Context_QueryInterface((ITfContext *)This, iid, *ppvOut);
347 }
348
349 static ULONG WINAPI Source_AddRef(ITfSource *iface)
350 {
351     Context *This = impl_from_ITfSourceVtbl(iface);
352     return Context_AddRef((ITfContext *)This);
353 }
354
355 static ULONG WINAPI Source_Release(ITfSource *iface)
356 {
357     Context *This = impl_from_ITfSourceVtbl(iface);
358     return Context_Release((ITfContext *)This);
359 }
360
361 /*****************************************************
362  * ITfSource functions
363  *****************************************************/
364 static WINAPI HRESULT ContextSource_AdviseSink(ITfSource *iface,
365         REFIID riid, IUnknown *punk, DWORD *pdwCookie)
366 {
367     ContextSink *es;
368     Context *This = impl_from_ITfSourceVtbl(iface);
369     TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);
370
371     if (!riid || !punk || !pdwCookie)
372         return E_INVALIDARG;
373
374     if (IsEqualIID(riid, &IID_ITfTextEditSink))
375     {
376         es = HeapAlloc(GetProcessHeap(),0,sizeof(ContextSink));
377         if (!es)
378             return E_OUTOFMEMORY;
379         if (!SUCCEEDED(IUnknown_QueryInterface(punk, riid, (LPVOID*)&es->interfaces.pITfTextEditSink)))
380         {
381             HeapFree(GetProcessHeap(),0,es);
382             return CONNECT_E_CANNOTCONNECT;
383         }
384         list_add_head(&This->pTextEditSink ,&es->entry);
385         *pdwCookie = generate_Cookie(COOKIE_MAGIC_CONTEXTSINK, es);
386     }
387     else
388     {
389         FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
390         return E_NOTIMPL;
391     }
392
393     TRACE("cookie %x\n",*pdwCookie);
394     return S_OK;
395 }
396
397 static WINAPI HRESULT ContextSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
398 {
399     ContextSink *sink;
400     Context *This = impl_from_ITfSourceVtbl(iface);
401
402     TRACE("(%p) %x\n",This,pdwCookie);
403
404     if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_CONTEXTSINK)
405         return E_INVALIDARG;
406
407     sink = (ContextSink*)remove_Cookie(pdwCookie);
408     if (!sink)
409         return CONNECT_E_NOCONNECTION;
410
411     list_remove(&sink->entry);
412     free_sink(sink);
413
414     return S_OK;
415 }
416
417 static const ITfSourceVtbl Context_SourceVtbl =
418 {
419     Source_QueryInterface,
420     Source_AddRef,
421     Source_Release,
422
423     ContextSource_AdviseSink,
424     ContextSource_UnadviseSink,
425 };
426
427 HRESULT Context_Constructor(TfClientId tidOwner, IUnknown *punk, ITfContext **ppOut, TfEditCookie *pecTextStore)
428 {
429     Context *This;
430
431     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Context));
432     if (This == NULL)
433         return E_OUTOFMEMORY;
434
435     TRACE("(%p) %x %p %p %p\n",This, tidOwner, punk, ppOut, pecTextStore);
436
437     This->ContextVtbl= &Context_ContextVtbl;
438     This->SourceVtbl = &Context_SourceVtbl;
439     This->refCount = 1;
440     This->tidOwner = tidOwner;
441
442     if (punk)
443     {
444         if (SUCCEEDED(IUnknown_QueryInterface(punk, &IID_ITextStoreACP,
445                           (LPVOID*)&This->pITextStoreACP)))
446         {
447             if (SUCCEEDED(TextStoreACPSink_Constructor(&This->pITextStoreACPSink, This)))
448                 ITextStoreACP_AdviseSink(This->pITextStoreACP, &IID_ITextStoreACPSink,
449                                         (IUnknown*)This->pITextStoreACPSink, TS_AS_ALL_SINKS);
450         }
451
452         IUnknown_QueryInterface(punk, &IID_ITfContextOwnerCompositionSink,
453                                 (LPVOID*)&This->pITfContextOwnerCompositionSink);
454
455         if (!This->pITextStoreACP && !This->pITfContextOwnerCompositionSink)
456             FIXME("Unhandled pUnk\n");
457     }
458
459     TRACE("returning %p\n", This);
460     *ppOut = (ITfContext*)This;
461     /* FIXME */
462     *pecTextStore = 0xdeaddead;
463
464     list_init(&This->pContextKeyEventSink);
465     list_init(&This->pEditTransactionSink);
466     list_init(&This->pStatusSink);
467     list_init(&This->pTextEditSink);
468     list_init(&This->pTextLayoutSink);
469
470     return S_OK;
471 }
472
473 /**************************************************************************
474  *  ITextStoreACPSink
475  **************************************************************************/
476
477 static void TextStoreACPSink_Destructor(TextStoreACPSink *This)
478 {
479     TRACE("destroying %p\n", This);
480     HeapFree(GetProcessHeap(),0,This);
481 }
482
483 static HRESULT WINAPI TextStoreACPSink_QueryInterface(ITextStoreACPSink *iface, REFIID iid, LPVOID *ppvOut)
484 {
485     TextStoreACPSink *This = (TextStoreACPSink *)iface;
486     *ppvOut = NULL;
487
488     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITextStoreACPSink))
489     {
490         *ppvOut = This;
491     }
492
493     if (*ppvOut)
494     {
495         IUnknown_AddRef(iface);
496         return S_OK;
497     }
498
499     WARN("unsupported interface: %s\n", debugstr_guid(iid));
500     return E_NOINTERFACE;
501 }
502
503 static ULONG WINAPI TextStoreACPSink_AddRef(ITextStoreACPSink *iface)
504 {
505     TextStoreACPSink *This = (TextStoreACPSink *)iface;
506     return InterlockedIncrement(&This->refCount);
507 }
508
509 static ULONG WINAPI TextStoreACPSink_Release(ITextStoreACPSink *iface)
510 {
511     TextStoreACPSink *This = (TextStoreACPSink *)iface;
512     ULONG ret;
513
514     ret = InterlockedDecrement(&This->refCount);
515     if (ret == 0)
516         TextStoreACPSink_Destructor(This);
517     return ret;
518 }
519
520 /*****************************************************
521  * ITextStoreACPSink functions
522  *****************************************************/
523
524 static HRESULT WINAPI TextStoreACPSink_OnTextChange(ITextStoreACPSink *iface,
525         DWORD dwFlags, const TS_TEXTCHANGE *pChange)
526 {
527     TextStoreACPSink *This = (TextStoreACPSink *)iface;
528     FIXME("STUB:(%p)\n",This);
529     return E_NOTIMPL;
530 }
531
532 static HRESULT WINAPI TextStoreACPSink_OnSelectionChange(ITextStoreACPSink *iface)
533 {
534     TextStoreACPSink *This = (TextStoreACPSink *)iface;
535     FIXME("STUB:(%p)\n",This);
536     return E_NOTIMPL;
537 }
538
539 static HRESULT WINAPI TextStoreACPSink_OnLayoutChange(ITextStoreACPSink *iface,
540     TsLayoutCode lcode, TsViewCookie vcView)
541 {
542     TextStoreACPSink *This = (TextStoreACPSink *)iface;
543     FIXME("STUB:(%p)\n",This);
544     return E_NOTIMPL;
545 }
546
547 static HRESULT WINAPI TextStoreACPSink_OnStatusChange(ITextStoreACPSink *iface,
548         DWORD dwFlags)
549 {
550     TextStoreACPSink *This = (TextStoreACPSink *)iface;
551     FIXME("STUB:(%p)\n",This);
552     return E_NOTIMPL;
553 }
554
555 static HRESULT WINAPI TextStoreACPSink_OnAttrsChange(ITextStoreACPSink *iface,
556         LONG acpStart, LONG acpEnd, ULONG cAttrs, const TS_ATTRID *paAttrs)
557 {
558     TextStoreACPSink *This = (TextStoreACPSink *)iface;
559     FIXME("STUB:(%p)\n",This);
560     return E_NOTIMPL;
561 }
562
563 static HRESULT WINAPI TextStoreACPSink_OnLockGranted(ITextStoreACPSink *iface,
564         DWORD dwLockFlags)
565 {
566     TextStoreACPSink *This = (TextStoreACPSink *)iface;
567     FIXME("STUB:(%p)\n",This);
568     return E_NOTIMPL;
569 }
570
571 static HRESULT WINAPI TextStoreACPSink_OnStartEditTransaction(ITextStoreACPSink *iface)
572 {
573     TextStoreACPSink *This = (TextStoreACPSink *)iface;
574     FIXME("STUB:(%p)\n",This);
575     return E_NOTIMPL;
576 }
577
578 static HRESULT WINAPI TextStoreACPSink_OnEndEditTransaction(ITextStoreACPSink *iface)
579 {
580     TextStoreACPSink *This = (TextStoreACPSink *)iface;
581     FIXME("STUB:(%p)\n",This);
582     return E_NOTIMPL;
583 }
584
585 static const ITextStoreACPSinkVtbl TextStoreACPSink_TextStoreACPSinkVtbl =
586 {
587     TextStoreACPSink_QueryInterface,
588     TextStoreACPSink_AddRef,
589     TextStoreACPSink_Release,
590
591     TextStoreACPSink_OnTextChange,
592     TextStoreACPSink_OnSelectionChange,
593     TextStoreACPSink_OnLayoutChange,
594     TextStoreACPSink_OnStatusChange,
595     TextStoreACPSink_OnAttrsChange,
596     TextStoreACPSink_OnLockGranted,
597     TextStoreACPSink_OnStartEditTransaction,
598     TextStoreACPSink_OnEndEditTransaction
599 };
600
601 static HRESULT TextStoreACPSink_Constructor(ITextStoreACPSink **ppOut, Context *pContext)
602 {
603     TextStoreACPSink *This;
604
605     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextStoreACPSink));
606     if (This == NULL)
607         return E_OUTOFMEMORY;
608
609     This->TextStoreACPSinkVtbl= &TextStoreACPSink_TextStoreACPSinkVtbl;
610     This->refCount = 1;
611
612     This->pContext = pContext;
613
614     TRACE("returning %p\n", This);
615     *ppOut = (ITextStoreACPSink*)This;
616     return S_OK;
617 }