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