ole32: Only fail on missing Implemented Categories key if we actually have categories...
[wine] / dlls / msctf / compartmentmgr.c
1 /*
2  *  ITfCompartmentMgr 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 "oleauto.h"
36 #include "olectl.h"
37
38 #include "wine/unicode.h"
39 #include "wine/list.h"
40
41 #include "msctf.h"
42 #include "msctf_internal.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
45
46 typedef struct tagCompartmentValue {
47     struct list entry;
48     GUID guid;
49     TfClientId owner;
50     ITfCompartment *compartment;
51 } CompartmentValue;
52
53 typedef struct tagCompartmentMgr {
54     const ITfCompartmentMgrVtbl *CompartmentMgrVtbl;
55     LONG refCount;
56
57     IUnknown *pUnkOuter;
58
59     struct list values;
60 } CompartmentMgr;
61
62 typedef struct tagCompartmentEnumGuid {
63     const IEnumGUIDVtbl *Vtbl;
64     LONG refCount;
65
66     struct list *values;
67     struct list *cursor;
68 } CompartmentEnumGuid;
69
70
71 typedef struct tagCompartmentSink {
72     struct list         entry;
73     union {
74         IUnknown            *pIUnknown;
75         ITfCompartmentEventSink *pITfCompartmentEventSink;
76     } interfaces;
77 } CompartmentSink;
78
79 typedef struct tagCompartment {
80     const ITfCompartmentVtbl *Vtbl;
81     const ITfSourceVtbl *SourceVtbl;
82     LONG refCount;
83
84     /* Only VT_I4, VT_UNKNOWN and VT_BSTR data types are allowed */
85     VARIANT variant;
86     CompartmentValue *valueData;
87     struct list CompartmentEventSink;
88 } Compartment;
89
90 static HRESULT CompartmentEnumGuid_Constructor(struct list* values, IEnumGUID **ppOut);
91 static HRESULT Compartment_Constructor(CompartmentValue *value, ITfCompartment **ppOut);
92
93 static inline Compartment *impl_from_ITfSourceVtbl(ITfSource *iface)
94 {
95     return (Compartment *)((char *)iface - FIELD_OFFSET(Compartment,SourceVtbl));
96 }
97
98 HRESULT CompartmentMgr_Destructor(ITfCompartmentMgr *iface)
99 {
100     CompartmentMgr *This = (CompartmentMgr *)iface;
101     struct list *cursor, *cursor2;
102
103     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->values)
104     {
105         CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
106         list_remove(cursor);
107         ITfCompartment_Release(value->compartment);
108         HeapFree(GetProcessHeap(),0,value);
109     }
110
111     HeapFree(GetProcessHeap(),0,This);
112     return S_OK;
113 }
114
115 /*****************************************************
116  * ITfCompartmentMgr functions
117  *****************************************************/
118 static HRESULT WINAPI CompartmentMgr_QueryInterface(ITfCompartmentMgr *iface, REFIID iid, LPVOID *ppvOut)
119 {
120     CompartmentMgr *This = (CompartmentMgr *)iface;
121     if (This->pUnkOuter)
122         return IUnknown_QueryInterface(This->pUnkOuter, iid, *ppvOut);
123     else
124     {
125         *ppvOut = NULL;
126
127         if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartmentMgr))
128         {
129             *ppvOut = This;
130         }
131
132         if (*ppvOut)
133         {
134             IUnknown_AddRef(iface);
135             return S_OK;
136         }
137
138         WARN("unsupported interface: %s\n", debugstr_guid(iid));
139         return E_NOINTERFACE;
140     }
141 }
142
143 static ULONG WINAPI CompartmentMgr_AddRef(ITfCompartmentMgr *iface)
144 {
145     CompartmentMgr *This = (CompartmentMgr *)iface;
146     if (This->pUnkOuter)
147         return IUnknown_AddRef(This->pUnkOuter);
148     else
149         return InterlockedIncrement(&This->refCount);
150 }
151
152 static ULONG WINAPI CompartmentMgr_Release(ITfCompartmentMgr *iface)
153 {
154     CompartmentMgr *This = (CompartmentMgr *)iface;
155     if (This->pUnkOuter)
156         return IUnknown_Release(This->pUnkOuter);
157     else
158     {
159         ULONG ret;
160
161         ret = InterlockedDecrement(&This->refCount);
162         if (ret == 0)
163             CompartmentMgr_Destructor(iface);
164         return ret;
165     }
166 }
167
168 static HRESULT WINAPI CompartmentMgr_GetCompartment(ITfCompartmentMgr *iface,
169         REFGUID rguid, ITfCompartment **ppcomp)
170 {
171     CompartmentMgr *This = (CompartmentMgr *)iface;
172     CompartmentValue* value;
173     struct list *cursor;
174     HRESULT hr;
175
176     TRACE("(%p) %s  %p\n",This,debugstr_guid(rguid),ppcomp);
177
178     LIST_FOR_EACH(cursor, &This->values)
179     {
180         value = LIST_ENTRY(cursor,CompartmentValue,entry);
181         if (IsEqualGUID(rguid,&value->guid))
182         {
183             ITfCompartment_AddRef(value->compartment);
184             *ppcomp = value->compartment;
185             return S_OK;
186         }
187     }
188
189     value = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentValue));
190     value->guid = *rguid;
191     value->owner = 0;
192     hr = Compartment_Constructor(value,&value->compartment);
193     if (SUCCEEDED(hr))
194     {
195         list_add_head(&This->values,&value->entry);
196         ITfCompartment_AddRef(value->compartment);
197         *ppcomp = value->compartment;
198     }
199     else
200     {
201         HeapFree(GetProcessHeap(),0,value);
202         *ppcomp = NULL;
203     }
204     return hr;
205 }
206
207 static HRESULT WINAPI CompartmentMgr_ClearCompartment(ITfCompartmentMgr *iface,
208     TfClientId tid, REFGUID rguid)
209 {
210     struct list *cursor;
211     CompartmentMgr *This = (CompartmentMgr *)iface;
212     TRACE("(%p) %i %s\n",This,tid,debugstr_guid(rguid));
213
214     LIST_FOR_EACH(cursor, &This->values)
215     {
216         CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
217         if (IsEqualGUID(rguid,&value->guid))
218         {
219             if (value->owner && tid != value->owner)
220                 return E_UNEXPECTED;
221             list_remove(cursor);
222             ITfCompartment_Release(value->compartment);
223             HeapFree(GetProcessHeap(),0,value);
224             return S_OK;
225         }
226     }
227
228     return CONNECT_E_NOCONNECTION;
229 }
230
231 static HRESULT WINAPI CompartmentMgr_EnumCompartments(ITfCompartmentMgr *iface,
232  IEnumGUID **ppEnum)
233 {
234     CompartmentMgr *This = (CompartmentMgr *)iface;
235     TRACE("(%p) %p\n",This,ppEnum);
236     if (!ppEnum)
237         return E_INVALIDARG;
238     return CompartmentEnumGuid_Constructor(&This->values, ppEnum);
239 }
240
241 static const ITfCompartmentMgrVtbl CompartmentMgr_CompartmentMgrVtbl =
242 {
243     CompartmentMgr_QueryInterface,
244     CompartmentMgr_AddRef,
245     CompartmentMgr_Release,
246
247     CompartmentMgr_GetCompartment,
248     CompartmentMgr_ClearCompartment,
249     CompartmentMgr_EnumCompartments
250 };
251
252 HRESULT CompartmentMgr_Constructor(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut)
253 {
254     CompartmentMgr *This;
255
256     if (!ppOut)
257         return E_POINTER;
258
259     if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown))
260         return CLASS_E_NOAGGREGATION;
261
262     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentMgr));
263     if (This == NULL)
264         return E_OUTOFMEMORY;
265
266     This->CompartmentMgrVtbl = &CompartmentMgr_CompartmentMgrVtbl;
267     This->pUnkOuter = pUnkOuter;
268     list_init(&This->values);
269
270     if (pUnkOuter)
271     {
272         TRACE("returning %p\n", This);
273         *ppOut = (IUnknown*)This;
274         return S_OK;
275     }
276     else
277     {
278         HRESULT hr;
279         hr = IUnknown_QueryInterface((IUnknown*)This, riid, (LPVOID*)ppOut);
280         if (FAILED(hr))
281             HeapFree(GetProcessHeap(),0,This);
282         return hr;
283     }
284 }
285
286 /**************************************************
287  * IEnumGUID implementaion for ITfCompartmentMgr::EnumCompartments
288  **************************************************/
289 static void CompartmentEnumGuid_Destructor(CompartmentEnumGuid *This)
290 {
291     TRACE("destroying %p\n", This);
292     HeapFree(GetProcessHeap(),0,This);
293 }
294
295 static HRESULT WINAPI CompartmentEnumGuid_QueryInterface(IEnumGUID *iface, REFIID iid, LPVOID *ppvOut)
296 {
297     CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
298     *ppvOut = NULL;
299
300     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumGUID))
301     {
302         *ppvOut = This;
303     }
304
305     if (*ppvOut)
306     {
307         IUnknown_AddRef(iface);
308         return S_OK;
309     }
310
311     WARN("unsupported interface: %s\n", debugstr_guid(iid));
312     return E_NOINTERFACE;
313 }
314
315 static ULONG WINAPI CompartmentEnumGuid_AddRef(IEnumGUID *iface)
316 {
317     CompartmentEnumGuid *This = (CompartmentEnumGuid*)iface;
318     return InterlockedIncrement(&This->refCount);
319 }
320
321 static ULONG WINAPI CompartmentEnumGuid_Release(IEnumGUID *iface)
322 {
323     CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
324     ULONG ret;
325
326     ret = InterlockedDecrement(&This->refCount);
327     if (ret == 0)
328         CompartmentEnumGuid_Destructor(This);
329     return ret;
330 }
331
332 /*****************************************************
333  * IEnumGuid functions
334  *****************************************************/
335 static HRESULT WINAPI CompartmentEnumGuid_Next( LPENUMGUID iface,
336     ULONG celt, GUID *rgelt, ULONG *pceltFetched)
337 {
338     CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
339     ULONG fetched = 0;
340
341     TRACE("(%p)\n",This);
342
343     if (rgelt == NULL) return E_POINTER;
344
345     while (fetched < celt && This->cursor)
346     {
347         CompartmentValue* value = LIST_ENTRY(This->cursor,CompartmentValue,entry);
348         if (!value)
349             break;
350
351         This->cursor = list_next(This->values,This->cursor);
352         *rgelt = value->guid;
353
354         ++fetched;
355         ++rgelt;
356     }
357
358     if (pceltFetched) *pceltFetched = fetched;
359     return fetched == celt ? S_OK : S_FALSE;
360 }
361
362 static HRESULT WINAPI CompartmentEnumGuid_Skip( LPENUMGUID iface, ULONG celt)
363 {
364     CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
365     TRACE("(%p)\n",This);
366
367     This->cursor = list_next(This->values,This->cursor);
368     return S_OK;
369 }
370
371 static HRESULT WINAPI CompartmentEnumGuid_Reset( LPENUMGUID iface)
372 {
373     CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
374     TRACE("(%p)\n",This);
375     This->cursor = list_head(This->values);
376     return S_OK;
377 }
378
379 static HRESULT WINAPI CompartmentEnumGuid_Clone( LPENUMGUID iface,
380     IEnumGUID **ppenum)
381 {
382     CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
383     HRESULT res;
384
385     TRACE("(%p)\n",This);
386
387     if (ppenum == NULL) return E_POINTER;
388
389     res = CompartmentEnumGuid_Constructor(This->values, ppenum);
390     if (SUCCEEDED(res))
391     {
392         CompartmentEnumGuid *new_This = (CompartmentEnumGuid *)*ppenum;
393         new_This->cursor = This->cursor;
394     }
395     return res;
396 }
397
398 static const IEnumGUIDVtbl IEnumGUID_Vtbl ={
399     CompartmentEnumGuid_QueryInterface,
400     CompartmentEnumGuid_AddRef,
401     CompartmentEnumGuid_Release,
402
403     CompartmentEnumGuid_Next,
404     CompartmentEnumGuid_Skip,
405     CompartmentEnumGuid_Reset,
406     CompartmentEnumGuid_Clone
407 };
408
409 static HRESULT CompartmentEnumGuid_Constructor(struct list *values, IEnumGUID **ppOut)
410 {
411     CompartmentEnumGuid *This;
412
413     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentEnumGuid));
414     if (This == NULL)
415         return E_OUTOFMEMORY;
416
417     This->Vtbl= &IEnumGUID_Vtbl;
418     This->refCount = 1;
419
420     This->values = values;
421     This->cursor = list_head(values);
422
423     TRACE("returning %p\n", This);
424     *ppOut = (IEnumGUID*)This;
425     return S_OK;
426 }
427
428 /**************************************************
429  * ITfCompartment
430  **************************************************/
431 static void free_sink(CompartmentSink *sink)
432 {
433         IUnknown_Release(sink->interfaces.pIUnknown);
434         HeapFree(GetProcessHeap(),0,sink);
435 }
436
437 static void Compartment_Destructor(Compartment *This)
438 {
439     struct list *cursor, *cursor2;
440     TRACE("destroying %p\n", This);
441     VariantClear(&This->variant);
442     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->CompartmentEventSink)
443     {
444         CompartmentSink* sink = LIST_ENTRY(cursor,CompartmentSink,entry);
445         list_remove(cursor);
446         free_sink(sink);
447     }
448     HeapFree(GetProcessHeap(),0,This);
449 }
450
451 static HRESULT WINAPI Compartment_QueryInterface(ITfCompartment *iface, REFIID iid, LPVOID *ppvOut)
452 {
453     Compartment *This = (Compartment *)iface;
454     *ppvOut = NULL;
455
456     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartment))
457     {
458         *ppvOut = This;
459     }
460     else if (IsEqualIID(iid, &IID_ITfSource))
461     {
462         *ppvOut = &This->SourceVtbl;
463     }
464
465     if (*ppvOut)
466     {
467         IUnknown_AddRef(iface);
468         return S_OK;
469     }
470
471     WARN("unsupported interface: %s\n", debugstr_guid(iid));
472     return E_NOINTERFACE;
473 }
474
475 static ULONG WINAPI Compartment_AddRef(ITfCompartment *iface)
476 {
477     Compartment *This = (Compartment*)iface;
478     return InterlockedIncrement(&This->refCount);
479 }
480
481 static ULONG WINAPI Compartment_Release(ITfCompartment *iface)
482 {
483     Compartment *This = (Compartment *)iface;
484     ULONG ret;
485
486     ret = InterlockedDecrement(&This->refCount);
487     if (ret == 0)
488         Compartment_Destructor(This);
489     return ret;
490 }
491
492 static HRESULT WINAPI Compartment_SetValue(ITfCompartment *iface,
493     TfClientId tid, const VARIANT *pvarValue)
494 {
495     Compartment *This = (Compartment *)iface;
496     struct list *cursor;
497
498     TRACE("(%p) %i %p\n",This,tid,pvarValue);
499
500     if (!pvarValue)
501         return E_INVALIDARG;
502
503     if (!(V_VT(pvarValue) == VT_BSTR || V_VT(pvarValue) == VT_I4 ||
504           V_VT(pvarValue) == VT_UNKNOWN))
505         return E_INVALIDARG;
506
507     if (!This->valueData->owner)
508         This->valueData->owner = tid;
509
510     VariantClear(&This->variant);
511
512     /* Shallow copy of value and type */
513     This->variant = *pvarValue;
514
515     if (V_VT(pvarValue) == VT_BSTR)
516         V_BSTR(&This->variant) = SysAllocStringByteLen((char*)V_BSTR(pvarValue),
517                 SysStringByteLen(V_BSTR(pvarValue)));
518     else if (V_VT(pvarValue) == VT_UNKNOWN)
519         IUnknown_AddRef(V_UNKNOWN(&This->variant));
520
521     LIST_FOR_EACH(cursor, &This->CompartmentEventSink)
522     {
523         CompartmentSink* sink = LIST_ENTRY(cursor,CompartmentSink,entry);
524         ITfCompartmentEventSink_OnChange(sink->interfaces.pITfCompartmentEventSink,&This->valueData->guid);
525     }
526
527     return S_OK;
528 }
529
530 static HRESULT WINAPI Compartment_GetValue(ITfCompartment *iface,
531     VARIANT *pvarValue)
532 {
533     Compartment *This = (Compartment *)iface;
534     TRACE("(%p) %p\n",This, pvarValue);
535
536     if (!pvarValue)
537         return E_INVALIDARG;
538
539     VariantInit(pvarValue);
540     if (V_VT(&This->variant) == VT_EMPTY) return S_FALSE;
541     return VariantCopy(pvarValue,&This->variant);
542 }
543
544 static const ITfCompartmentVtbl ITfCompartment_Vtbl ={
545     Compartment_QueryInterface,
546     Compartment_AddRef,
547     Compartment_Release,
548
549     Compartment_SetValue,
550     Compartment_GetValue
551 };
552
553 /*****************************************************
554  * ITfSource functions
555  *****************************************************/
556
557 static HRESULT WINAPI Source_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
558 {
559     Compartment *This = impl_from_ITfSourceVtbl(iface);
560     return Compartment_QueryInterface((ITfCompartment *)This, iid, *ppvOut);
561 }
562
563 static ULONG WINAPI Source_AddRef(ITfSource *iface)
564 {
565     Compartment *This = impl_from_ITfSourceVtbl(iface);
566     return Compartment_AddRef((ITfCompartment*)This);
567 }
568
569 static ULONG WINAPI Source_Release(ITfSource *iface)
570 {
571     Compartment *This = impl_from_ITfSourceVtbl(iface);
572     return Compartment_Release((ITfCompartment *)This);
573 }
574
575 static HRESULT WINAPI CompartmentSource_AdviseSink(ITfSource *iface,
576         REFIID riid, IUnknown *punk, DWORD *pdwCookie)
577 {
578     CompartmentSink *cs;
579     Compartment *This = impl_from_ITfSourceVtbl(iface);
580
581     TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);
582
583     if (!riid || !punk || !pdwCookie)
584         return E_INVALIDARG;
585
586     if (IsEqualIID(riid, &IID_ITfCompartmentEventSink))
587     {
588         cs = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentSink));
589         if (!cs)
590             return E_OUTOFMEMORY;
591         if (FAILED(IUnknown_QueryInterface(punk, riid, (LPVOID *)&cs->interfaces.pITfCompartmentEventSink)))
592         {
593             HeapFree(GetProcessHeap(),0,cs);
594             return CONNECT_E_CANNOTCONNECT;
595         }
596         list_add_head(&This->CompartmentEventSink,&cs->entry);
597         *pdwCookie = generate_Cookie(COOKIE_MAGIC_COMPARTMENTSINK , cs);
598     }
599     else
600     {
601         FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
602         return E_NOTIMPL;
603     }
604
605     TRACE("cookie %x\n",*pdwCookie);
606
607     return S_OK;
608 }
609
610 static HRESULT WINAPI CompartmentSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
611 {
612     CompartmentSink *sink;
613     Compartment *This = impl_from_ITfSourceVtbl(iface);
614
615     TRACE("(%p) %x\n",This,pdwCookie);
616
617     if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_COMPARTMENTSINK)
618         return E_INVALIDARG;
619
620     sink = (CompartmentSink*)remove_Cookie(pdwCookie);
621     if (!sink)
622         return CONNECT_E_NOCONNECTION;
623
624     list_remove(&sink->entry);
625     free_sink(sink);
626
627     return S_OK;
628 }
629
630 static const ITfSourceVtbl Compartment_SourceVtbl =
631 {
632     Source_QueryInterface,
633     Source_AddRef,
634     Source_Release,
635
636     CompartmentSource_AdviseSink,
637     CompartmentSource_UnadviseSink,
638 };
639
640 static HRESULT Compartment_Constructor(CompartmentValue *valueData, ITfCompartment **ppOut)
641 {
642     Compartment *This;
643
644     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Compartment));
645     if (This == NULL)
646         return E_OUTOFMEMORY;
647
648     This->Vtbl= &ITfCompartment_Vtbl;
649     This->SourceVtbl = &Compartment_SourceVtbl;
650     This->refCount = 1;
651
652     This->valueData = valueData;
653     VariantInit(&This->variant);
654
655     list_init(&This->CompartmentEventSink);
656
657     TRACE("returning %p\n", This);
658     *ppOut = (ITfCompartment*)This;
659     return S_OK;
660 }