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