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