msctf: Support advising ITfLanguageProfileNotifySink in ITfInputProcessorProfiles.
[wine] / dlls / msctf / inputprocessor.c
1 /*
2  *  ITfInputProcessorProfiles 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 "olectl.h"
36
37 #include "wine/unicode.h"
38 #include "wine/list.h"
39
40 #include "msctf.h"
41 #include "msctf_internal.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
44
45 static const WCHAR szwLngp[] = {'L','a','n','g','u','a','g','e','P','r','o','f','i','l','e',0};
46 static const WCHAR szwEnabled[] = {'E','n','a','b','l','e','d',0};
47 static const WCHAR szwTipfmt[] = {'%','s','\\','%','s',0};
48 static const WCHAR szwFullLangfmt[] = {'%','s','\\','%','s','\\','%','s','\\','0','x','%','0','8','x','\\','%','s',0};
49
50 typedef struct tagInputProcessorProfilesSink {
51     struct list         entry;
52     union {
53         /* InputProcessorProfile Sinks */
54         IUnknown            *pIUnknown;
55         ITfLanguageProfileNotifySink *pITfLanguageProfileNotifySink;
56     } interfaces;
57 } InputProcessorProfilesSink;
58
59 typedef struct tagInputProcessorProfiles {
60     const ITfInputProcessorProfilesVtbl *InputProcessorProfilesVtbl;
61     const ITfSourceVtbl *SourceVtbl;
62     LONG refCount;
63
64     LANGID  currentLanguage;
65
66     struct list     LanguageProfileNotifySink;
67 } InputProcessorProfiles;
68
69 typedef struct tagProfilesEnumGuid {
70     const IEnumGUIDVtbl *Vtbl;
71     LONG refCount;
72
73     HKEY key;
74     DWORD next_index;
75 } ProfilesEnumGuid;
76
77 typedef struct tagEnumTfLanguageProfiles {
78     const IEnumTfLanguageProfilesVtbl *Vtbl;
79     LONG refCount;
80
81     HKEY    tipkey;
82     DWORD   tip_index;
83     WCHAR   szwCurrentClsid[39];
84
85     HKEY    langkey;
86     DWORD   lang_index;
87
88     LANGID  langid;
89     ITfCategoryMgr *catmgr;
90 } EnumTfLanguageProfiles;
91
92 static HRESULT ProfilesEnumGuid_Constructor(IEnumGUID **ppOut);
93 static HRESULT EnumTfLanguageProfiles_Constructor(LANGID langid, IEnumTfLanguageProfiles **ppOut);
94
95 static inline InputProcessorProfiles *impl_from_ITfSourceVtbl(ITfSource *iface)
96 {
97     return (InputProcessorProfiles *)((char *)iface - FIELD_OFFSET(InputProcessorProfiles,SourceVtbl));
98 }
99
100 static void free_sink(InputProcessorProfilesSink *sink)
101 {
102         IUnknown_Release(sink->interfaces.pIUnknown);
103         HeapFree(GetProcessHeap(),0,sink);
104 }
105
106 static void InputProcessorProfiles_Destructor(InputProcessorProfiles *This)
107 {
108     struct list *cursor, *cursor2;
109     TRACE("destroying %p\n", This);
110
111     /* free sinks */
112     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->LanguageProfileNotifySink)
113     {
114         InputProcessorProfilesSink* sink = LIST_ENTRY(cursor,InputProcessorProfilesSink,entry);
115         list_remove(cursor);
116         free_sink(sink);
117     }
118
119     HeapFree(GetProcessHeap(),0,This);
120 }
121
122 static void add_userkey( REFCLSID rclsid, LANGID langid,
123                                 REFGUID guidProfile)
124 {
125     HKEY key;
126     WCHAR buf[39];
127     WCHAR buf2[39];
128     WCHAR fullkey[168];
129     DWORD disposition = 0;
130     ULONG res;
131
132     TRACE("\n");
133
134     StringFromGUID2(rclsid, buf, 39);
135     StringFromGUID2(guidProfile, buf2, 39);
136     sprintfW(fullkey,szwFullLangfmt,szwSystemTIPKey,buf,szwLngp,langid,buf2);
137
138     res = RegCreateKeyExW(HKEY_CURRENT_USER,fullkey, 0, NULL, 0,
139                    KEY_READ | KEY_WRITE, NULL, &key, &disposition);
140
141     if (!res && disposition == REG_CREATED_NEW_KEY)
142     {
143         DWORD zero = 0x0;
144         RegSetValueExW(key, szwEnabled, 0, REG_DWORD, (LPBYTE)&zero, sizeof(DWORD));
145     }
146
147     if (!res)
148         RegCloseKey(key);
149 }
150
151 static HRESULT WINAPI InputProcessorProfiles_QueryInterface(ITfInputProcessorProfiles *iface, REFIID iid, LPVOID *ppvOut)
152 {
153     InputProcessorProfiles *This = (InputProcessorProfiles *)iface;
154     *ppvOut = NULL;
155
156     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfInputProcessorProfiles))
157     {
158         *ppvOut = This;
159     }
160     else if (IsEqualIID(iid, &IID_ITfSource))
161     {
162         *ppvOut = &This->SourceVtbl;
163     }
164
165     if (*ppvOut)
166     {
167         IUnknown_AddRef(iface);
168         return S_OK;
169     }
170
171     WARN("unsupported interface: %s\n", debugstr_guid(iid));
172     return E_NOINTERFACE;
173 }
174
175 static ULONG WINAPI InputProcessorProfiles_AddRef(ITfInputProcessorProfiles *iface)
176 {
177     InputProcessorProfiles *This = (InputProcessorProfiles *)iface;
178     return InterlockedIncrement(&This->refCount);
179 }
180
181 static ULONG WINAPI InputProcessorProfiles_Release(ITfInputProcessorProfiles *iface)
182 {
183     InputProcessorProfiles *This = (InputProcessorProfiles *)iface;
184     ULONG ret;
185
186     ret = InterlockedDecrement(&This->refCount);
187     if (ret == 0)
188         InputProcessorProfiles_Destructor(This);
189     return ret;
190 }
191
192 /*****************************************************
193  * ITfInputProcessorProfiles functions
194  *****************************************************/
195 static HRESULT WINAPI InputProcessorProfiles_Register(
196         ITfInputProcessorProfiles *iface, REFCLSID rclsid)
197 {
198     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
199     HKEY tipkey;
200     WCHAR buf[39];
201     WCHAR fullkey[68];
202
203     TRACE("(%p) %s\n",This,debugstr_guid(rclsid));
204
205     StringFromGUID2(rclsid, buf, 39);
206     sprintfW(fullkey,szwTipfmt,szwSystemTIPKey,buf);
207
208     if (RegCreateKeyExW(HKEY_LOCAL_MACHINE,fullkey, 0, NULL, 0,
209                     KEY_READ | KEY_WRITE, NULL, &tipkey, NULL) != ERROR_SUCCESS)
210         return E_FAIL;
211
212     RegCloseKey(tipkey);
213
214     return S_OK;
215 }
216
217 static HRESULT WINAPI InputProcessorProfiles_Unregister(
218         ITfInputProcessorProfiles *iface, REFCLSID rclsid)
219 {
220     WCHAR buf[39];
221     WCHAR fullkey[68];
222     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
223
224     TRACE("(%p) %s\n",This,debugstr_guid(rclsid));
225
226     StringFromGUID2(rclsid, buf, 39);
227     sprintfW(fullkey,szwTipfmt,szwSystemTIPKey,buf);
228
229     RegDeleteTreeW(HKEY_LOCAL_MACHINE, fullkey);
230     RegDeleteTreeW(HKEY_CURRENT_USER, fullkey);
231
232     return S_OK;
233 }
234
235 static HRESULT WINAPI InputProcessorProfiles_AddLanguageProfile(
236         ITfInputProcessorProfiles *iface, REFCLSID rclsid,
237         LANGID langid, REFGUID guidProfile, const WCHAR *pchDesc,
238         ULONG cchDesc, const WCHAR *pchIconFile, ULONG cchFile,
239         ULONG uIconIndex)
240 {
241     HKEY tipkey,fmtkey;
242     WCHAR buf[39];
243     WCHAR fullkey[100];
244     ULONG res;
245     DWORD disposition = 0;
246
247     static const WCHAR fmt2[] = {'%','s','\\','0','x','%','0','8','x','\\','%','s',0};
248     static const WCHAR desc[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
249     static const WCHAR icnf[] = {'I','c','o','n','F','i','l','e',0};
250     static const WCHAR icni[] = {'I','c','o','n','I','n','d','e','x',0};
251
252     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
253
254     TRACE("(%p) %s %x %s %s %s %i\n",This,debugstr_guid(rclsid), langid,
255             debugstr_guid(guidProfile), debugstr_wn(pchDesc,cchDesc),
256             debugstr_wn(pchIconFile,cchFile),uIconIndex);
257
258     StringFromGUID2(rclsid, buf, 39);
259     sprintfW(fullkey,szwTipfmt,szwSystemTIPKey,buf);
260
261     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,fullkey, 0, KEY_READ | KEY_WRITE,
262                 &tipkey ) != ERROR_SUCCESS)
263         return E_FAIL;
264
265     StringFromGUID2(guidProfile, buf, 39);
266     sprintfW(fullkey,fmt2,szwLngp,langid,buf);
267
268     res = RegCreateKeyExW(tipkey,fullkey, 0, NULL, 0, KEY_READ | KEY_WRITE,
269             NULL, &fmtkey, &disposition);
270
271     if (!res)
272     {
273         DWORD zero = 0x0;
274         RegSetValueExW(fmtkey, desc, 0, REG_SZ, (LPBYTE)pchDesc, cchDesc * sizeof(WCHAR));
275         RegSetValueExW(fmtkey, icnf, 0, REG_SZ, (LPBYTE)pchIconFile, cchFile * sizeof(WCHAR));
276         RegSetValueExW(fmtkey, icni, 0, REG_DWORD, (LPBYTE)&uIconIndex, sizeof(DWORD));
277         if (disposition == REG_CREATED_NEW_KEY)
278             RegSetValueExW(fmtkey, szwEnabled, 0, REG_DWORD, (LPBYTE)&zero, sizeof(DWORD));
279         RegCloseKey(fmtkey);
280
281         add_userkey(rclsid, langid, guidProfile);
282     }
283     RegCloseKey(tipkey);
284
285     if (!res)
286         return S_OK;
287     else
288         return E_FAIL;
289 }
290
291 static HRESULT WINAPI InputProcessorProfiles_RemoveLanguageProfile(
292         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
293         REFGUID guidProfile)
294 {
295     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
296     FIXME("STUB:(%p)\n",This);
297     return E_NOTIMPL;
298 }
299
300 static HRESULT WINAPI InputProcessorProfiles_EnumInputProcessorInfo(
301         ITfInputProcessorProfiles *iface, IEnumGUID **ppEnum)
302 {
303     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
304     TRACE("(%p) %p\n",This,ppEnum);
305     return ProfilesEnumGuid_Constructor(ppEnum);
306 }
307
308 static HRESULT WINAPI InputProcessorProfiles_GetDefaultLanguageProfile(
309         ITfInputProcessorProfiles *iface, LANGID langid, REFGUID catid,
310         CLSID *pclsid, GUID *pguidProfile)
311 {
312     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
313     FIXME("STUB:(%p)\n",This);
314     return E_NOTIMPL;
315 }
316
317 static HRESULT WINAPI InputProcessorProfiles_SetDefaultLanguageProfile(
318         ITfInputProcessorProfiles *iface, LANGID langid, REFCLSID rclsid,
319         REFGUID guidProfiles)
320 {
321     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
322     FIXME("STUB:(%p)\n",This);
323     return E_NOTIMPL;
324 }
325
326 static HRESULT WINAPI InputProcessorProfiles_ActivateLanguageProfile(
327         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
328         REFGUID guidProfiles)
329 {
330     HRESULT hr;
331     BOOL enabled;
332     TF_LANGUAGEPROFILE LanguageProfile;
333     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
334
335     TRACE("(%p) %s %x %s\n",This,debugstr_guid(rclsid),langid,debugstr_guid(guidProfiles));
336
337     if (langid != This->currentLanguage) return E_INVALIDARG;
338
339     if (get_active_textservice(rclsid,NULL))
340     {
341         TRACE("Already Active\n");
342         return E_FAIL;
343     }
344
345     hr = ITfInputProcessorProfiles_IsEnabledLanguageProfile(iface, rclsid,
346             langid, guidProfiles, &enabled);
347     if (FAILED(hr) || !enabled)
348     {
349         TRACE("Not Enabled\n");
350         return E_FAIL;
351     }
352
353     LanguageProfile.clsid = *rclsid;
354     LanguageProfile.langid = langid;
355     LanguageProfile.guidProfile = *guidProfiles;
356
357     hr = add_active_textservice(&LanguageProfile);
358
359     return hr;
360 }
361
362 static HRESULT WINAPI InputProcessorProfiles_GetActiveLanguageProfile(
363         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID *plangid,
364         GUID *pguidProfile)
365 {
366     TF_LANGUAGEPROFILE profile;
367     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
368
369     TRACE("(%p) %s %p %p\n",This,debugstr_guid(rclsid),plangid,pguidProfile);
370
371     if (!rclsid || !plangid || !pguidProfile)
372         return E_INVALIDARG;
373
374     if (get_active_textservice(rclsid, &profile))
375     {
376         *plangid = profile.langid;
377         *pguidProfile = profile.guidProfile;
378         return S_OK;
379     }
380     else
381     {
382         *pguidProfile = GUID_NULL;
383         return S_FALSE;
384     }
385 }
386
387 static HRESULT WINAPI InputProcessorProfiles_GetLanguageProfileDescription(
388         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
389         REFGUID guidProfile, BSTR *pbstrProfile)
390 {
391     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
392     FIXME("STUB:(%p)\n",This);
393     return E_NOTIMPL;
394 }
395
396 static HRESULT WINAPI InputProcessorProfiles_GetCurrentLanguage(
397         ITfInputProcessorProfiles *iface, LANGID *plangid)
398 {
399     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
400     TRACE("(%p) 0x%x\n",This,This->currentLanguage);
401
402     if (!plangid)
403         return E_INVALIDARG;
404
405     *plangid = This->currentLanguage;
406
407     return S_OK;
408 }
409
410 static HRESULT WINAPI InputProcessorProfiles_ChangeCurrentLanguage(
411         ITfInputProcessorProfiles *iface, LANGID langid)
412 {
413     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
414     FIXME("STUB:(%p)\n",This);
415     return E_NOTIMPL;
416 }
417
418 static HRESULT WINAPI InputProcessorProfiles_GetLanguageList(
419         ITfInputProcessorProfiles *iface, LANGID **ppLangId, ULONG *pulCount)
420 {
421     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
422     FIXME("STUB:(%p)\n",This);
423     return E_NOTIMPL;
424 }
425
426 static HRESULT WINAPI InputProcessorProfiles_EnumLanguageProfiles(
427         ITfInputProcessorProfiles *iface, LANGID langid,
428         IEnumTfLanguageProfiles **ppEnum)
429 {
430     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
431     TRACE("(%p) %x %p\n",This,langid,ppEnum);
432     return EnumTfLanguageProfiles_Constructor(langid, ppEnum);
433 }
434
435 static HRESULT WINAPI InputProcessorProfiles_EnableLanguageProfile(
436         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
437         REFGUID guidProfile, BOOL fEnable)
438 {
439     HKEY key;
440     WCHAR buf[39];
441     WCHAR buf2[39];
442     WCHAR fullkey[168];
443     ULONG res;
444
445     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
446     TRACE("(%p) %s %x %s %i\n",This, debugstr_guid(rclsid), langid, debugstr_guid(guidProfile), fEnable);
447
448     StringFromGUID2(rclsid, buf, 39);
449     StringFromGUID2(guidProfile, buf2, 39);
450     sprintfW(fullkey,szwFullLangfmt,szwSystemTIPKey,buf,szwLngp,langid,buf2);
451
452     res = RegOpenKeyExW(HKEY_CURRENT_USER, fullkey, 0, KEY_READ | KEY_WRITE, &key);
453
454     if (!res)
455     {
456         RegSetValueExW(key, szwEnabled, 0, REG_DWORD, (LPBYTE)&fEnable, sizeof(DWORD));
457         RegCloseKey(key);
458     }
459     else
460         return E_FAIL;
461
462     return S_OK;
463 }
464
465 static HRESULT WINAPI InputProcessorProfiles_IsEnabledLanguageProfile(
466         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
467         REFGUID guidProfile, BOOL *pfEnable)
468 {
469     HKEY key;
470     WCHAR buf[39];
471     WCHAR buf2[39];
472     WCHAR fullkey[168];
473     ULONG res;
474
475     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
476     TRACE("(%p) %s, %i, %s, %p\n",This,debugstr_guid(rclsid),langid,debugstr_guid(guidProfile),pfEnable);
477
478     if (!pfEnable)
479         return E_INVALIDARG;
480
481     StringFromGUID2(rclsid, buf, 39);
482     StringFromGUID2(guidProfile, buf2, 39);
483     sprintfW(fullkey,szwFullLangfmt,szwSystemTIPKey,buf,szwLngp,langid,buf2);
484
485     res = RegOpenKeyExW(HKEY_CURRENT_USER, fullkey, 0, KEY_READ | KEY_WRITE, &key);
486
487     if (!res)
488     {
489         DWORD count = sizeof(DWORD);
490         res = RegQueryValueExW(key, szwEnabled, 0, NULL, (LPBYTE)pfEnable, &count);
491         RegCloseKey(key);
492     }
493
494     if (res)  /* Try Default */
495     {
496         res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, fullkey, 0, KEY_READ | KEY_WRITE, &key);
497
498         if (!res)
499         {
500             DWORD count = sizeof(DWORD);
501             res = RegQueryValueExW(key, szwEnabled, 0, NULL, (LPBYTE)pfEnable, &count);
502             RegCloseKey(key);
503         }
504     }
505
506     if (!res)
507         return S_OK;
508     else
509         return E_FAIL;
510 }
511
512 static HRESULT WINAPI InputProcessorProfiles_EnableLanguageProfileByDefault(
513         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
514         REFGUID guidProfile, BOOL fEnable)
515 {
516     HKEY key;
517     WCHAR buf[39];
518     WCHAR buf2[39];
519     WCHAR fullkey[168];
520     ULONG res;
521
522     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
523     TRACE("(%p) %s %x %s %i\n",This,debugstr_guid(rclsid),langid,debugstr_guid(guidProfile),fEnable);
524
525     StringFromGUID2(rclsid, buf, 39);
526     StringFromGUID2(guidProfile, buf2, 39);
527     sprintfW(fullkey,szwFullLangfmt,szwSystemTIPKey,buf,szwLngp,langid,buf2);
528
529     res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, fullkey, 0, KEY_READ | KEY_WRITE, &key);
530
531     if (!res)
532     {
533         RegSetValueExW(key, szwEnabled, 0, REG_DWORD, (LPBYTE)&fEnable, sizeof(DWORD));
534         RegCloseKey(key);
535     }
536     else
537         return E_FAIL;
538
539     return S_OK;
540 }
541
542 static HRESULT WINAPI InputProcessorProfiles_SubstituteKeyboardLayout(
543         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
544         REFGUID guidProfile, HKL hKL)
545 {
546     InputProcessorProfiles *This = (InputProcessorProfiles*)iface;
547     FIXME("STUB:(%p)\n",This);
548     return E_NOTIMPL;
549 }
550
551
552 static const ITfInputProcessorProfilesVtbl InputProcessorProfiles_InputProcessorProfilesVtbl =
553 {
554     InputProcessorProfiles_QueryInterface,
555     InputProcessorProfiles_AddRef,
556     InputProcessorProfiles_Release,
557
558     InputProcessorProfiles_Register,
559     InputProcessorProfiles_Unregister,
560     InputProcessorProfiles_AddLanguageProfile,
561     InputProcessorProfiles_RemoveLanguageProfile,
562     InputProcessorProfiles_EnumInputProcessorInfo,
563     InputProcessorProfiles_GetDefaultLanguageProfile,
564     InputProcessorProfiles_SetDefaultLanguageProfile,
565     InputProcessorProfiles_ActivateLanguageProfile,
566     InputProcessorProfiles_GetActiveLanguageProfile,
567     InputProcessorProfiles_GetLanguageProfileDescription,
568     InputProcessorProfiles_GetCurrentLanguage,
569     InputProcessorProfiles_ChangeCurrentLanguage,
570     InputProcessorProfiles_GetLanguageList,
571     InputProcessorProfiles_EnumLanguageProfiles,
572     InputProcessorProfiles_EnableLanguageProfile,
573     InputProcessorProfiles_IsEnabledLanguageProfile,
574     InputProcessorProfiles_EnableLanguageProfileByDefault,
575     InputProcessorProfiles_SubstituteKeyboardLayout
576 };
577
578 /*****************************************************
579  * ITfSource functions
580  *****************************************************/
581 static HRESULT WINAPI IPPSource_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
582 {
583     InputProcessorProfiles *This = impl_from_ITfSourceVtbl(iface);
584     return InputProcessorProfiles_QueryInterface((ITfInputProcessorProfiles *)This, iid, *ppvOut);
585 }
586
587 static ULONG WINAPI IPPSource_AddRef(ITfSource *iface)
588 {
589     InputProcessorProfiles *This = impl_from_ITfSourceVtbl(iface);
590     return InputProcessorProfiles_AddRef((ITfInputProcessorProfiles*)This);
591 }
592
593 static ULONG WINAPI IPPSource_Release(ITfSource *iface)
594 {
595     InputProcessorProfiles *This = impl_from_ITfSourceVtbl(iface);
596     return InputProcessorProfiles_Release((ITfInputProcessorProfiles *)This);
597 }
598
599 static WINAPI HRESULT IPPSource_AdviseSink(ITfSource *iface,
600         REFIID riid, IUnknown *punk, DWORD *pdwCookie)
601 {
602     InputProcessorProfilesSink *ipps;
603     InputProcessorProfiles *This = impl_from_ITfSourceVtbl(iface);
604
605     TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);
606
607     if (!riid || !punk || !pdwCookie)
608         return E_INVALIDARG;
609
610     if (IsEqualIID(riid, &IID_ITfLanguageProfileNotifySink))
611     {
612         ipps = HeapAlloc(GetProcessHeap(),0,sizeof(InputProcessorProfilesSink));
613         if (!ipps)
614             return E_OUTOFMEMORY;
615         if (!SUCCEEDED(IUnknown_QueryInterface(punk, riid, (LPVOID*)&ipps->interfaces.pITfLanguageProfileNotifySink)))
616         {
617             HeapFree(GetProcessHeap(),0,ipps);
618             return CONNECT_E_CANNOTCONNECT;
619         }
620         list_add_head(&This->LanguageProfileNotifySink,&ipps->entry);
621         *pdwCookie = generate_Cookie(COOKIE_MAGIC_IPPSINK, ipps);
622     }
623     else
624     {
625         FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
626         return E_NOTIMPL;
627     }
628
629     TRACE("cookie %x\n",*pdwCookie);
630
631     return S_OK;
632 }
633
634 static WINAPI HRESULT IPPSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
635 {
636     InputProcessorProfilesSink *sink;
637     InputProcessorProfiles *This = impl_from_ITfSourceVtbl(iface);
638
639     TRACE("(%p) %x\n",This,pdwCookie);
640
641     if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_IPPSINK)
642         return E_INVALIDARG;
643
644     sink = (InputProcessorProfilesSink*)remove_Cookie(pdwCookie);
645     if (!sink)
646         return CONNECT_E_NOCONNECTION;
647
648     list_remove(&sink->entry);
649     free_sink(sink);
650
651     return S_OK;
652 }
653
654 static const ITfSourceVtbl InputProcessorProfiles_SourceVtbl =
655 {
656     IPPSource_QueryInterface,
657     IPPSource_AddRef,
658     IPPSource_Release,
659
660     IPPSource_AdviseSink,
661     IPPSource_UnadviseSink,
662 };
663
664 HRESULT InputProcessorProfiles_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut)
665 {
666     InputProcessorProfiles *This;
667     if (pUnkOuter)
668         return CLASS_E_NOAGGREGATION;
669
670     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(InputProcessorProfiles));
671     if (This == NULL)
672         return E_OUTOFMEMORY;
673
674     This->InputProcessorProfilesVtbl= &InputProcessorProfiles_InputProcessorProfilesVtbl;
675     This->SourceVtbl = &InputProcessorProfiles_SourceVtbl;
676     This->refCount = 1;
677     This->currentLanguage = GetUserDefaultLCID();
678
679     list_init(&This->LanguageProfileNotifySink);
680
681     TRACE("returning %p\n", This);
682     *ppOut = (IUnknown *)This;
683     return S_OK;
684 }
685
686 /**************************************************
687  * IEnumGUID implementaion for ITfInputProcessorProfiles::EnumInputProcessorInfo
688  **************************************************/
689 static void ProfilesEnumGuid_Destructor(ProfilesEnumGuid *This)
690 {
691     TRACE("destroying %p\n", This);
692     RegCloseKey(This->key);
693     HeapFree(GetProcessHeap(),0,This);
694 }
695
696 static HRESULT WINAPI ProfilesEnumGuid_QueryInterface(IEnumGUID *iface, REFIID iid, LPVOID *ppvOut)
697 {
698     ProfilesEnumGuid *This = (ProfilesEnumGuid *)iface;
699     *ppvOut = NULL;
700
701     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumGUID))
702     {
703         *ppvOut = This;
704     }
705
706     if (*ppvOut)
707     {
708         IUnknown_AddRef(iface);
709         return S_OK;
710     }
711
712     WARN("unsupported interface: %s\n", debugstr_guid(iid));
713     return E_NOINTERFACE;
714 }
715
716 static ULONG WINAPI ProfilesEnumGuid_AddRef(IEnumGUID *iface)
717 {
718     ProfilesEnumGuid *This = (ProfilesEnumGuid*)iface;
719     return InterlockedIncrement(&This->refCount);
720 }
721
722 static ULONG WINAPI ProfilesEnumGuid_Release(IEnumGUID *iface)
723 {
724     ProfilesEnumGuid *This = (ProfilesEnumGuid *)iface;
725     ULONG ret;
726
727     ret = InterlockedDecrement(&This->refCount);
728     if (ret == 0)
729         ProfilesEnumGuid_Destructor(This);
730     return ret;
731 }
732
733 /*****************************************************
734  * IEnumGuid functions
735  *****************************************************/
736 static HRESULT WINAPI ProfilesEnumGuid_Next( LPENUMGUID iface,
737     ULONG celt, GUID *rgelt, ULONG *pceltFetched)
738 {
739     ProfilesEnumGuid *This = (ProfilesEnumGuid *)iface;
740     ULONG fetched = 0;
741
742     TRACE("(%p)\n",This);
743
744     if (rgelt == NULL) return E_POINTER;
745
746     if (This->key) while (fetched < celt)
747     {
748         LSTATUS res;
749         HRESULT hr;
750         WCHAR catid[39];
751         DWORD cName = 39;
752
753         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
754                     NULL, NULL, NULL, NULL);
755         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
756         ++(This->next_index);
757
758         hr = CLSIDFromString(catid, rgelt);
759         if (FAILED(hr)) continue;
760
761         ++fetched;
762         ++rgelt;
763     }
764
765     if (pceltFetched) *pceltFetched = fetched;
766     return fetched == celt ? S_OK : S_FALSE;
767 }
768
769 static HRESULT WINAPI ProfilesEnumGuid_Skip( LPENUMGUID iface, ULONG celt)
770 {
771     ProfilesEnumGuid *This = (ProfilesEnumGuid *)iface;
772     TRACE("(%p)\n",This);
773
774     This->next_index += celt;
775     return S_OK;
776 }
777
778 static HRESULT WINAPI ProfilesEnumGuid_Reset( LPENUMGUID iface)
779 {
780     ProfilesEnumGuid *This = (ProfilesEnumGuid *)iface;
781     TRACE("(%p)\n",This);
782     This->next_index = 0;
783     return S_OK;
784 }
785
786 static HRESULT WINAPI ProfilesEnumGuid_Clone( LPENUMGUID iface,
787     IEnumGUID **ppenum)
788 {
789     ProfilesEnumGuid *This = (ProfilesEnumGuid *)iface;
790     HRESULT res;
791
792     TRACE("(%p)\n",This);
793
794     if (ppenum == NULL) return E_POINTER;
795
796     res = ProfilesEnumGuid_Constructor(ppenum);
797     if (SUCCEEDED(res))
798     {
799         ProfilesEnumGuid *new_This = (ProfilesEnumGuid *)*ppenum;
800         new_This->next_index = This->next_index;
801     }
802     return res;
803 }
804
805 static const IEnumGUIDVtbl IEnumGUID_Vtbl ={
806     ProfilesEnumGuid_QueryInterface,
807     ProfilesEnumGuid_AddRef,
808     ProfilesEnumGuid_Release,
809
810     ProfilesEnumGuid_Next,
811     ProfilesEnumGuid_Skip,
812     ProfilesEnumGuid_Reset,
813     ProfilesEnumGuid_Clone
814 };
815
816 static HRESULT ProfilesEnumGuid_Constructor(IEnumGUID **ppOut)
817 {
818     ProfilesEnumGuid *This;
819
820     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ProfilesEnumGuid));
821     if (This == NULL)
822         return E_OUTOFMEMORY;
823
824     This->Vtbl= &IEnumGUID_Vtbl;
825     This->refCount = 1;
826
827     if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szwSystemTIPKey, 0, NULL, 0,
828                     KEY_READ | KEY_WRITE, NULL, &This->key, NULL) != ERROR_SUCCESS)
829         return E_FAIL;
830
831     TRACE("returning %p\n", This);
832     *ppOut = (IEnumGUID*)This;
833     return S_OK;
834 }
835
836 /**************************************************
837  * IEnumTfLanguageProfiles implementaion
838  **************************************************/
839 static void EnumTfLanguageProfiles_Destructor(EnumTfLanguageProfiles *This)
840 {
841     TRACE("destroying %p\n", This);
842     RegCloseKey(This->tipkey);
843     if (This->langkey)
844         RegCloseKey(This->langkey);
845     ITfCategoryMgr_Release(This->catmgr);
846     HeapFree(GetProcessHeap(),0,This);
847 }
848
849 static HRESULT WINAPI EnumTfLanguageProfiles_QueryInterface(IEnumTfLanguageProfiles *iface, REFIID iid, LPVOID *ppvOut)
850 {
851     EnumTfLanguageProfiles *This = (EnumTfLanguageProfiles *)iface;
852     *ppvOut = NULL;
853
854     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumTfLanguageProfiles))
855     {
856         *ppvOut = This;
857     }
858
859     if (*ppvOut)
860     {
861         IUnknown_AddRef(iface);
862         return S_OK;
863     }
864
865     WARN("unsupported interface: %s\n", debugstr_guid(iid));
866     return E_NOINTERFACE;
867 }
868
869 static ULONG WINAPI EnumTfLanguageProfiles_AddRef(IEnumTfLanguageProfiles *iface)
870 {
871     EnumTfLanguageProfiles *This = (EnumTfLanguageProfiles*)iface;
872     return InterlockedIncrement(&This->refCount);
873 }
874
875 static ULONG WINAPI EnumTfLanguageProfiles_Release(IEnumTfLanguageProfiles *iface)
876 {
877     EnumTfLanguageProfiles *This = (EnumTfLanguageProfiles *)iface;
878     ULONG ret;
879
880     ret = InterlockedDecrement(&This->refCount);
881     if (ret == 0)
882         EnumTfLanguageProfiles_Destructor(This);
883     return ret;
884 }
885
886 /*****************************************************
887  * IEnumGuid functions
888  *****************************************************/
889 static INT next_LanguageProfile(EnumTfLanguageProfiles *This, CLSID clsid, TF_LANGUAGEPROFILE *tflp)
890 {
891     WCHAR fullkey[168];
892     ULONG res;
893     WCHAR profileid[39];
894     DWORD cName = 39;
895     GUID  profile;
896
897     static const WCHAR fmt[] = {'%','s','\\','%','s','\\','0','x','%','0','8','x',0};
898
899     if (This->langkey == NULL)
900     {
901         sprintfW(fullkey,fmt,This->szwCurrentClsid,szwLngp,This->langid);
902         res = RegOpenKeyExW(This->tipkey, fullkey, 0, KEY_READ | KEY_WRITE, &This->langkey);
903         if (res)
904         {
905             This->langkey = NULL;
906             return -1;
907         }
908         This->lang_index = 0;
909     }
910     res = RegEnumKeyExW(This->langkey, This->lang_index, profileid, &cName,
911                 NULL, NULL, NULL, NULL);
912     if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
913     {
914         RegCloseKey(This->langkey);
915         This->langkey = NULL;
916         return -1;
917     }
918     ++(This->lang_index);
919
920     if (tflp)
921     {
922         static const GUID * tipcats[3] = { &GUID_TFCAT_TIP_KEYBOARD,
923                                            &GUID_TFCAT_TIP_SPEECH,
924                                            &GUID_TFCAT_TIP_HANDWRITING };
925         res = CLSIDFromString(profileid, &profile);
926         if (FAILED(res)) return 0;
927
928         tflp->clsid = clsid;
929         tflp->langid = This->langid;
930         tflp->fActive = get_active_textservice(&clsid, NULL);
931         tflp->guidProfile = profile;
932         if (ITfCategoryMgr_FindClosestCategory(This->catmgr, &clsid,
933                 &tflp->catid, tipcats, 3) != S_OK)
934             ITfCategoryMgr_FindClosestCategory(This->catmgr, &clsid,
935                     &tflp->catid, NULL, 0);
936     }
937
938     return 1;
939 }
940
941 static HRESULT WINAPI EnumTfLanguageProfiles_Next(IEnumTfLanguageProfiles *iface,
942     ULONG ulCount, TF_LANGUAGEPROFILE *pProfile, ULONG *pcFetch)
943 {
944     EnumTfLanguageProfiles *This = (EnumTfLanguageProfiles *)iface;
945     ULONG fetched = 0;
946
947     TRACE("(%p)\n",This);
948
949     if (pProfile == NULL) return E_POINTER;
950
951     if (This->tipkey) while (fetched < ulCount)
952     {
953         LSTATUS res;
954         HRESULT hr;
955         DWORD cName = 39;
956         GUID clsid;
957
958         res = RegEnumKeyExW(This->tipkey, This->tip_index,
959                     This->szwCurrentClsid, &cName, NULL, NULL, NULL, NULL);
960         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
961         ++(This->tip_index);
962         hr = CLSIDFromString(This->szwCurrentClsid, &clsid);
963         if (FAILED(hr)) continue;
964
965         while ( fetched < ulCount)
966         {
967             INT res = next_LanguageProfile(This, clsid, pProfile);
968             if (res == 1)
969             {
970                 ++fetched;
971                 ++pProfile;
972             }
973             else if (res == -1)
974                 break;
975             else
976                 continue;
977         }
978     }
979
980     if (pcFetch) *pcFetch = fetched;
981     return fetched == ulCount ? S_OK : S_FALSE;
982 }
983
984 static HRESULT WINAPI EnumTfLanguageProfiles_Skip( IEnumTfLanguageProfiles* iface, ULONG celt)
985 {
986     EnumTfLanguageProfiles *This = (EnumTfLanguageProfiles *)iface;
987     FIXME("STUB (%p)\n",This);
988     return E_NOTIMPL;
989 }
990
991 static HRESULT WINAPI EnumTfLanguageProfiles_Reset( IEnumTfLanguageProfiles* iface)
992 {
993     EnumTfLanguageProfiles *This = (EnumTfLanguageProfiles *)iface;
994     TRACE("(%p)\n",This);
995     This->tip_index = 0;
996     if (This->langkey)
997         RegCloseKey(This->langkey);
998     This->langkey = NULL;
999     This->lang_index = 0;
1000     return S_OK;
1001 }
1002
1003 static HRESULT WINAPI EnumTfLanguageProfiles_Clone( IEnumTfLanguageProfiles *iface,
1004     IEnumTfLanguageProfiles **ppenum)
1005 {
1006     EnumTfLanguageProfiles *This = (EnumTfLanguageProfiles *)iface;
1007     HRESULT res;
1008
1009     TRACE("(%p)\n",This);
1010
1011     if (ppenum == NULL) return E_POINTER;
1012
1013     res = EnumTfLanguageProfiles_Constructor(This->langid, ppenum);
1014     if (SUCCEEDED(res))
1015     {
1016         EnumTfLanguageProfiles *new_This = (EnumTfLanguageProfiles *)*ppenum;
1017         new_This->tip_index = This->tip_index;
1018         lstrcpynW(new_This->szwCurrentClsid,This->szwCurrentClsid,39);
1019
1020         if (This->langkey)
1021         {
1022             WCHAR fullkey[168];
1023             static const WCHAR fmt[] = {'%','s','\\','%','s','\\','0','x','%','0','8','x',0};
1024
1025             sprintfW(fullkey,fmt,This->szwCurrentClsid,szwLngp,This->langid);
1026             res = RegOpenKeyExW(new_This->tipkey, fullkey, 0, KEY_READ | KEY_WRITE, &This->langkey);
1027             new_This->lang_index = This->lang_index;
1028         }
1029     }
1030     return res;
1031 }
1032
1033 static const IEnumTfLanguageProfilesVtbl IEnumTfLanguageProfiles_Vtbl ={
1034     EnumTfLanguageProfiles_QueryInterface,
1035     EnumTfLanguageProfiles_AddRef,
1036     EnumTfLanguageProfiles_Release,
1037
1038     EnumTfLanguageProfiles_Clone,
1039     EnumTfLanguageProfiles_Next,
1040     EnumTfLanguageProfiles_Reset,
1041     EnumTfLanguageProfiles_Skip
1042 };
1043
1044 static HRESULT EnumTfLanguageProfiles_Constructor(LANGID langid, IEnumTfLanguageProfiles **ppOut)
1045 {
1046     HRESULT hr;
1047     EnumTfLanguageProfiles *This;
1048
1049     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(EnumTfLanguageProfiles));
1050     if (This == NULL)
1051         return E_OUTOFMEMORY;
1052
1053     This->Vtbl= &IEnumTfLanguageProfiles_Vtbl;
1054     This->refCount = 1;
1055     This->langid = langid;
1056
1057     hr = CategoryMgr_Constructor(NULL,(IUnknown**)&This->catmgr);
1058     if (FAILED(hr))
1059     {
1060         HeapFree(GetProcessHeap(),0,This);
1061         return hr;
1062     }
1063
1064     if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szwSystemTIPKey, 0, NULL, 0,
1065                     KEY_READ | KEY_WRITE, NULL, &This->tipkey, NULL) != ERROR_SUCCESS)
1066         return E_FAIL;
1067
1068     TRACE("returning %p\n", This);
1069     *ppOut = (IEnumTfLanguageProfiles*)This;
1070     return S_OK;
1071 }