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