xref: /reactos/dll/win32/msctf/documentmgr.c (revision c2c66aff)
1 /*
2  *  ITfDocumentMgr 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 "msctf_internal.h"
22 
23 typedef struct tagDocumentMgr {
24     ITfDocumentMgr ITfDocumentMgr_iface;
25     ITfSource ITfSource_iface;
26     LONG refCount;
27 
28     /* Aggregation */
29     ITfCompartmentMgr  *CompartmentMgr;
30 
31     ITfContext*  contextStack[2]; /* limit of 2 contexts */
32     ITfThreadMgrEventSink* ThreadMgrSink;
33 
34     struct list TransitoryExtensionSink;
35 } DocumentMgr;
36 
37 typedef struct tagEnumTfContext {
38     IEnumTfContexts IEnumTfContexts_iface;
39     LONG refCount;
40 
41     DWORD   index;
42     DocumentMgr *docmgr;
43 } EnumTfContext;
44 
45 static HRESULT EnumTfContext_Constructor(DocumentMgr* mgr, IEnumTfContexts **ppOut);
46 
47 static inline DocumentMgr *impl_from_ITfDocumentMgr(ITfDocumentMgr *iface)
48 {
49     return CONTAINING_RECORD(iface, DocumentMgr, ITfDocumentMgr_iface);
50 }
51 
52 static inline DocumentMgr *impl_from_ITfSource(ITfSource *iface)
53 {
54     return CONTAINING_RECORD(iface, DocumentMgr, ITfSource_iface);
55 }
56 
57 static inline EnumTfContext *impl_from_IEnumTfContexts(IEnumTfContexts *iface)
58 {
59     return CONTAINING_RECORD(iface, EnumTfContext, IEnumTfContexts_iface);
60 }
61 
62 static void DocumentMgr_Destructor(DocumentMgr *This)
63 {
64     ITfThreadMgr *tm;
65     TRACE("destroying %p\n", This);
66 
67     TF_GetThreadMgr(&tm);
68     ThreadMgr_OnDocumentMgrDestruction(tm, &This->ITfDocumentMgr_iface);
69 
70     if (This->contextStack[0])
71         ITfContext_Release(This->contextStack[0]);
72     if (This->contextStack[1])
73         ITfContext_Release(This->contextStack[1]);
74     free_sinks(&This->TransitoryExtensionSink);
75     CompartmentMgr_Destructor(This->CompartmentMgr);
76     HeapFree(GetProcessHeap(),0,This);
77 }
78 
79 static HRESULT WINAPI DocumentMgr_QueryInterface(ITfDocumentMgr *iface, REFIID iid, LPVOID *ppvOut)
80 {
81     DocumentMgr *This = impl_from_ITfDocumentMgr(iface);
82     *ppvOut = NULL;
83 
84     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfDocumentMgr))
85     {
86         *ppvOut = &This->ITfDocumentMgr_iface;
87     }
88     else if (IsEqualIID(iid, &IID_ITfSource))
89     {
90         *ppvOut = &This->ITfSource_iface;
91     }
92     else if (IsEqualIID(iid, &IID_ITfCompartmentMgr))
93     {
94         *ppvOut = This->CompartmentMgr;
95     }
96 
97     if (*ppvOut)
98     {
99         ITfDocumentMgr_AddRef(iface);
100         return S_OK;
101     }
102 
103     WARN("unsupported interface: %s\n", debugstr_guid(iid));
104     return E_NOINTERFACE;
105 }
106 
107 static ULONG WINAPI DocumentMgr_AddRef(ITfDocumentMgr *iface)
108 {
109     DocumentMgr *This = impl_from_ITfDocumentMgr(iface);
110     return InterlockedIncrement(&This->refCount);
111 }
112 
113 static ULONG WINAPI DocumentMgr_Release(ITfDocumentMgr *iface)
114 {
115     DocumentMgr *This = impl_from_ITfDocumentMgr(iface);
116     ULONG ret;
117 
118     ret = InterlockedDecrement(&This->refCount);
119     if (ret == 0)
120         DocumentMgr_Destructor(This);
121     return ret;
122 }
123 
124 /*****************************************************
125  * ITfDocumentMgr functions
126  *****************************************************/
127 static HRESULT WINAPI DocumentMgr_CreateContext(ITfDocumentMgr *iface,
128         TfClientId tidOwner,
129         DWORD dwFlags, IUnknown *punk, ITfContext **ppic,
130         TfEditCookie *pecTextStore)
131 {
132     DocumentMgr *This = impl_from_ITfDocumentMgr(iface);
133     TRACE("(%p) 0x%x 0x%x %p %p %p\n",This,tidOwner,dwFlags,punk,ppic,pecTextStore);
134     return Context_Constructor(tidOwner, punk, iface, ppic, pecTextStore);
135 }
136 
137 static HRESULT WINAPI DocumentMgr_Push(ITfDocumentMgr *iface, ITfContext *pic)
138 {
139     DocumentMgr *This = impl_from_ITfDocumentMgr(iface);
140     ITfContext *check;
141 
142     TRACE("(%p) %p\n",This,pic);
143 
144     if (This->contextStack[1])  /* FUll */
145         return TF_E_STACKFULL;
146 
147     if (!pic || FAILED(ITfContext_QueryInterface(pic,&IID_ITfContext,(LPVOID*) &check)))
148         return E_INVALIDARG;
149 
150     if (This->contextStack[0] == NULL)
151         ITfThreadMgrEventSink_OnInitDocumentMgr(This->ThreadMgrSink,iface);
152 
153     This->contextStack[1] = This->contextStack[0];
154     This->contextStack[0] = check;
155 
156     Context_Initialize(check, iface);
157     ITfThreadMgrEventSink_OnPushContext(This->ThreadMgrSink,check);
158 
159     return S_OK;
160 }
161 
162 static HRESULT WINAPI DocumentMgr_Pop(ITfDocumentMgr *iface, DWORD dwFlags)
163 {
164     DocumentMgr *This = impl_from_ITfDocumentMgr(iface);
165     TRACE("(%p) 0x%x\n",This,dwFlags);
166 
167     if (dwFlags == TF_POPF_ALL)
168     {
169         int i;
170 
171         for (i = 0; i < sizeof(This->contextStack)/sizeof(This->contextStack[0]); i++)
172             if (This->contextStack[i])
173             {
174                 ITfThreadMgrEventSink_OnPopContext(This->ThreadMgrSink, This->contextStack[i]);
175                 Context_Uninitialize(This->contextStack[i]);
176                 ITfContext_Release(This->contextStack[i]);
177                 This->contextStack[i] = NULL;
178             }
179 
180         ITfThreadMgrEventSink_OnUninitDocumentMgr(This->ThreadMgrSink, iface);
181         return S_OK;
182     }
183 
184     if (dwFlags)
185         return E_INVALIDARG;
186 
187     if (This->contextStack[1] == NULL) /* Cannot pop last context */
188         return E_FAIL;
189 
190     ITfThreadMgrEventSink_OnPopContext(This->ThreadMgrSink,This->contextStack[0]);
191     Context_Uninitialize(This->contextStack[0]);
192     ITfContext_Release(This->contextStack[0]);
193     This->contextStack[0] = This->contextStack[1];
194     This->contextStack[1] = NULL;
195 
196     if (This->contextStack[0] == NULL)
197         ITfThreadMgrEventSink_OnUninitDocumentMgr(This->ThreadMgrSink, iface);
198 
199     return S_OK;
200 }
201 
202 static HRESULT WINAPI DocumentMgr_GetTop(ITfDocumentMgr *iface, ITfContext **ppic)
203 {
204     DocumentMgr *This = impl_from_ITfDocumentMgr(iface);
205     TRACE("(%p)\n",This);
206     if (!ppic)
207         return E_INVALIDARG;
208 
209     if (This->contextStack[0])
210         ITfContext_AddRef(This->contextStack[0]);
211 
212     *ppic = This->contextStack[0];
213 
214     return S_OK;
215 }
216 
217 static HRESULT WINAPI DocumentMgr_GetBase(ITfDocumentMgr *iface, ITfContext **ppic)
218 {
219     DocumentMgr *This = impl_from_ITfDocumentMgr(iface);
220     ITfContext *tgt;
221 
222     TRACE("(%p)\n",This);
223     if (!ppic)
224         return E_INVALIDARG;
225 
226     if (This->contextStack[1])
227         tgt = This->contextStack[1];
228     else
229         tgt = This->contextStack[0];
230 
231     if (tgt)
232         ITfContext_AddRef(tgt);
233 
234     *ppic = tgt;
235 
236     return S_OK;
237 }
238 
239 static HRESULT WINAPI DocumentMgr_EnumContexts(ITfDocumentMgr *iface, IEnumTfContexts **ppEnum)
240 {
241     DocumentMgr *This = impl_from_ITfDocumentMgr(iface);
242     TRACE("(%p) %p\n",This,ppEnum);
243     return EnumTfContext_Constructor(This, ppEnum);
244 }
245 
246 static const ITfDocumentMgrVtbl DocumentMgrVtbl =
247 {
248     DocumentMgr_QueryInterface,
249     DocumentMgr_AddRef,
250     DocumentMgr_Release,
251     DocumentMgr_CreateContext,
252     DocumentMgr_Push,
253     DocumentMgr_Pop,
254     DocumentMgr_GetTop,
255     DocumentMgr_GetBase,
256     DocumentMgr_EnumContexts
257 };
258 
259 static HRESULT WINAPI DocumentMgrSource_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
260 {
261     DocumentMgr *This = impl_from_ITfSource(iface);
262     return ITfDocumentMgr_QueryInterface(&This->ITfDocumentMgr_iface, iid, ppvOut);
263 }
264 
265 static ULONG WINAPI DocumentMgrSource_AddRef(ITfSource *iface)
266 {
267     DocumentMgr *This = impl_from_ITfSource(iface);
268     return ITfDocumentMgr_AddRef(&This->ITfDocumentMgr_iface);
269 }
270 
271 static ULONG WINAPI DocumentMgrSource_Release(ITfSource *iface)
272 {
273     DocumentMgr *This = impl_from_ITfSource(iface);
274     return ITfDocumentMgr_Release(&This->ITfDocumentMgr_iface);
275 }
276 
277 /*****************************************************
278  * ITfSource functions
279  *****************************************************/
280 static HRESULT WINAPI DocumentMgrSource_AdviseSink(ITfSource *iface,
281         REFIID riid, IUnknown *punk, DWORD *pdwCookie)
282 {
283     DocumentMgr *This = impl_from_ITfSource(iface);
284 
285     TRACE("(%p) %s %p %p\n", This, debugstr_guid(riid), punk, pdwCookie);
286 
287     if (!riid || !punk || !pdwCookie)
288         return E_INVALIDARG;
289 
290     if (IsEqualIID(riid, &IID_ITfTransitoryExtensionSink))
291     {
292         WARN("semi-stub for ITfTransitoryExtensionSink: callback won't be used.\n");
293         return advise_sink(&This->TransitoryExtensionSink, &IID_ITfTransitoryExtensionSink,
294                            COOKIE_MAGIC_DMSINK, punk, pdwCookie);
295     }
296 
297     FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
298     return E_NOTIMPL;
299 }
300 
301 static HRESULT WINAPI DocumentMgrSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
302 {
303     DocumentMgr *This = impl_from_ITfSource(iface);
304 
305     TRACE("(%p) %x\n",This,pdwCookie);
306 
307     if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_DMSINK)
308         return E_INVALIDARG;
309 
310     return unadvise_sink(pdwCookie);
311 }
312 
313 static const ITfSourceVtbl DocumentMgrSourceVtbl =
314 {
315     DocumentMgrSource_QueryInterface,
316     DocumentMgrSource_AddRef,
317     DocumentMgrSource_Release,
318     DocumentMgrSource_AdviseSink,
319     DocumentMgrSource_UnadviseSink,
320 };
321 
322 HRESULT DocumentMgr_Constructor(ITfThreadMgrEventSink *ThreadMgrSink, ITfDocumentMgr **ppOut)
323 {
324     DocumentMgr *This;
325 
326     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DocumentMgr));
327     if (This == NULL)
328         return E_OUTOFMEMORY;
329 
330     This->ITfDocumentMgr_iface.lpVtbl = &DocumentMgrVtbl;
331     This->ITfSource_iface.lpVtbl = &DocumentMgrSourceVtbl;
332     This->refCount = 1;
333     This->ThreadMgrSink = ThreadMgrSink;
334     list_init(&This->TransitoryExtensionSink);
335 
336     CompartmentMgr_Constructor((IUnknown*)&This->ITfDocumentMgr_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr);
337 
338     *ppOut = &This->ITfDocumentMgr_iface;
339     TRACE("returning %p\n", *ppOut);
340     return S_OK;
341 }
342 
343 /**************************************************
344  * IEnumTfContexts implementation
345  **************************************************/
346 static void EnumTfContext_Destructor(EnumTfContext *This)
347 {
348     TRACE("destroying %p\n", This);
349     HeapFree(GetProcessHeap(),0,This);
350 }
351 
352 static HRESULT WINAPI EnumTfContext_QueryInterface(IEnumTfContexts *iface, REFIID iid, LPVOID *ppvOut)
353 {
354     EnumTfContext *This = impl_from_IEnumTfContexts(iface);
355     *ppvOut = NULL;
356 
357     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumTfContexts))
358     {
359         *ppvOut = &This->IEnumTfContexts_iface;
360     }
361 
362     if (*ppvOut)
363     {
364         IEnumTfContexts_AddRef(iface);
365         return S_OK;
366     }
367 
368     WARN("unsupported interface: %s\n", debugstr_guid(iid));
369     return E_NOINTERFACE;
370 }
371 
372 static ULONG WINAPI EnumTfContext_AddRef(IEnumTfContexts *iface)
373 {
374     EnumTfContext *This = impl_from_IEnumTfContexts(iface);
375     return InterlockedIncrement(&This->refCount);
376 }
377 
378 static ULONG WINAPI EnumTfContext_Release(IEnumTfContexts *iface)
379 {
380     EnumTfContext *This = impl_from_IEnumTfContexts(iface);
381     ULONG ret;
382 
383     ret = InterlockedDecrement(&This->refCount);
384     if (ret == 0)
385         EnumTfContext_Destructor(This);
386     return ret;
387 }
388 
389 static HRESULT WINAPI EnumTfContext_Next(IEnumTfContexts *iface,
390     ULONG ulCount, ITfContext **rgContext, ULONG *pcFetched)
391 {
392     EnumTfContext *This = impl_from_IEnumTfContexts(iface);
393     ULONG fetched = 0;
394 
395     TRACE("(%p)\n",This);
396 
397     if (rgContext == NULL) return E_POINTER;
398 
399     while (fetched < ulCount)
400     {
401         if (This->index > 1)
402             break;
403 
404         if (!This->docmgr->contextStack[This->index])
405             break;
406 
407         *rgContext = This->docmgr->contextStack[This->index];
408         ITfContext_AddRef(*rgContext);
409 
410         ++This->index;
411         ++fetched;
412         ++rgContext;
413     }
414 
415     if (pcFetched) *pcFetched = fetched;
416     return fetched == ulCount ? S_OK : S_FALSE;
417 }
418 
419 static HRESULT WINAPI EnumTfContext_Skip( IEnumTfContexts* iface, ULONG celt)
420 {
421     EnumTfContext *This = impl_from_IEnumTfContexts(iface);
422     TRACE("(%p)\n",This);
423     This->index += celt;
424     return S_OK;
425 }
426 
427 static HRESULT WINAPI EnumTfContext_Reset( IEnumTfContexts* iface)
428 {
429     EnumTfContext *This = impl_from_IEnumTfContexts(iface);
430     TRACE("(%p)\n",This);
431     This->index = 0;
432     return S_OK;
433 }
434 
435 static HRESULT WINAPI EnumTfContext_Clone( IEnumTfContexts *iface,
436     IEnumTfContexts **ppenum)
437 {
438     EnumTfContext *This = impl_from_IEnumTfContexts(iface);
439     HRESULT res;
440 
441     TRACE("(%p)\n",This);
442 
443     if (ppenum == NULL) return E_POINTER;
444 
445     res = EnumTfContext_Constructor(This->docmgr, ppenum);
446     if (SUCCEEDED(res))
447     {
448         EnumTfContext *new_This = impl_from_IEnumTfContexts(*ppenum);
449         new_This->index = This->index;
450     }
451     return res;
452 }
453 
454 static const IEnumTfContextsVtbl IEnumTfContexts_Vtbl ={
455     EnumTfContext_QueryInterface,
456     EnumTfContext_AddRef,
457     EnumTfContext_Release,
458 
459     EnumTfContext_Clone,
460     EnumTfContext_Next,
461     EnumTfContext_Reset,
462     EnumTfContext_Skip
463 };
464 
465 static HRESULT EnumTfContext_Constructor(DocumentMgr *mgr, IEnumTfContexts **ppOut)
466 {
467     EnumTfContext *This;
468 
469     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(EnumTfContext));
470     if (This == NULL)
471         return E_OUTOFMEMORY;
472 
473     This->IEnumTfContexts_iface.lpVtbl = &IEnumTfContexts_Vtbl;
474     This->refCount = 1;
475     This->docmgr = mgr;
476 
477     *ppOut = &This->IEnumTfContexts_iface;
478     TRACE("returning %p\n", *ppOut);
479     return S_OK;
480 }
481