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