xref: /reactos/dll/cpl/input/settings_page.c (revision 80c4856b)
1 /*
2  * PROJECT:         input.dll
3  * FILE:            dll/cpl/input/settings_page.c
4  * PURPOSE:         input.dll
5  * PROGRAMMERS:     Dmitry Chapyshev (dmitry@reactos.org)
6  *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
7  */
8 
9 #include "input.h"
10 #include "layout_list.h"
11 #include "locale_list.h"
12 #include "input_list.h"
13 
14 static INT s_nAliveLeafCount = 0;
15 static INT s_nRootCount = 0;
16 static INT s_iKeyboardImage = -1;
17 static INT s_iDotImage = -1;
18 
19 static HICON
CreateLayoutIcon(LANGID LangID)20 CreateLayoutIcon(LANGID LangID)
21 {
22     WCHAR szBuf[4];
23     HDC hdcScreen, hdc;
24     HBITMAP hbmColor, hbmMono, hBmpOld;
25     HFONT hFont, hFontOld;
26     LOGFONTW lf;
27     RECT rect;
28     ICONINFO IconInfo;
29     HICON hIcon;
30     INT cxIcon = GetSystemMetrics(SM_CXSMICON);
31     INT cyIcon = GetSystemMetrics(SM_CYSMICON);
32 
33     /* Getting "EN", "FR", etc. from English, French, ... */
34     if (GetLocaleInfoW(LangID,
35                        LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
36                        szBuf,
37                        ARRAYSIZE(szBuf)) == 0)
38     {
39         szBuf[0] = szBuf[1] = L'?';
40     }
41     szBuf[2] = UNICODE_NULL; /* Truncate the identifier to two characters: "ENG" --> "EN" etc. */
42 
43     /* Create hdc, hbmColor and hbmMono */
44     hdcScreen = GetDC(NULL);
45     hdc = CreateCompatibleDC(hdcScreen);
46     hbmColor = CreateCompatibleBitmap(hdcScreen, cxIcon, cyIcon);
47     ReleaseDC(NULL, hdcScreen);
48     hbmMono = CreateBitmap(cxIcon, cyIcon, 1, 1, NULL);
49 
50     /* Checking NULL */
51     if (!hdc || !hbmColor || !hbmMono)
52     {
53         if (hbmMono)
54             DeleteObject(hbmMono);
55         if (hbmColor)
56             DeleteObject(hbmColor);
57         if (hdc)
58             DeleteDC(hdc);
59         return NULL;
60     }
61 
62     /* Create a font */
63     hFont = NULL;
64     if (SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0))
65     {
66         /* Override the current size with something manageable */
67         lf.lfHeight = -11;
68         lf.lfWidth = 0;
69         hFont = CreateFontIndirectW(&lf);
70     }
71     if (!hFont)
72         hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
73 
74     SetRect(&rect, 0, 0, cxIcon, cyIcon);
75 
76     /* Draw hbmColor */
77     hBmpOld = SelectObject(hdc, hbmColor);
78     SetDCBrushColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
79     FillRect(hdc, &rect, (HBRUSH)GetStockObject(DC_BRUSH));
80     hFontOld = SelectObject(hdc, hFont);
81     SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
82     SetBkMode(hdc, TRANSPARENT);
83     DrawTextW(hdc, szBuf, 2, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
84     SelectObject(hdc, hFontOld);
85 
86     /* Fill hbmMono with black */
87     SelectObject(hdc, hbmMono);
88     PatBlt(hdc, 0, 0, cxIcon, cyIcon, BLACKNESS);
89     SelectObject(hdc, hBmpOld);
90 
91     /* Create an icon from hbmColor and hbmMono */
92     IconInfo.fIcon = TRUE;
93     IconInfo.xHotspot = IconInfo.yHotspot = 0;
94     IconInfo.hbmColor = hbmColor;
95     IconInfo.hbmMask = hbmMono;
96     hIcon = CreateIconIndirect(&IconInfo);
97 
98     /* Clean up */
99     DeleteObject(hFont);
100     DeleteObject(hbmMono);
101     DeleteObject(hbmColor);
102     DeleteDC(hdc);
103 
104     return hIcon;
105 }
106 
InitDefaultLangComboBox(HWND hwndCombo)107 static VOID InitDefaultLangComboBox(HWND hwndCombo)
108 {
109     WCHAR szText[256];
110     INPUT_LIST_NODE *pNode;
111     INT iIndex, nCount, iDefault = (INT)SendMessageW(hwndCombo, CB_GETCURSEL, 0, 0);
112 
113     SendMessageW(hwndCombo, CB_RESETCONTENT, 0, 0);
114 
115     for (pNode = InputList_GetFirst(); pNode != NULL; pNode = pNode->pNext)
116     {
117         if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
118             continue;
119 
120         StringCchPrintfW(szText, _countof(szText), L"%s - %s",
121                          pNode->pLocale->pszName, pNode->pLayout->pszName);
122         iIndex = (INT)SendMessageW(hwndCombo, CB_ADDSTRING, 0, (LPARAM)szText);
123         SendMessageW(hwndCombo, CB_SETITEMDATA, iIndex, (LPARAM)pNode);
124 
125         if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
126             iDefault = iIndex;
127     }
128 
129     nCount = (INT)SendMessageW(hwndCombo, CB_GETCOUNT, 0, 0);
130     if (iDefault >= nCount)
131         SendMessageW(hwndCombo, CB_SETCURSEL, nCount - 1, 0);
132     else
133         SendMessageW(hwndCombo, CB_SETCURSEL, iDefault, 0);
134 }
135 
136 static VOID
SetControlsState(HWND hwndDlg)137 SetControlsState(HWND hwndDlg)
138 {
139     HWND hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
140     HWND hwndCombo = GetDlgItem(hwndDlg, IDC_DEFAULT_LANGUAGE);
141     BOOL bIsLeaf, bCanRemove, bCanProp;
142     HTREEITEM hSelected = TreeView_GetSelection(hwndList);
143     TV_ITEM item = { TVIF_PARAM | TVIF_HANDLE };
144     item.hItem = hSelected;
145 
146     bIsLeaf = (hSelected && TreeView_GetItem(hwndList, &item) && HIWORD(item.lParam));
147 
148     bCanRemove = (bIsLeaf && (s_nAliveLeafCount > 1)) || (s_nRootCount > 1);
149     bCanProp = bIsLeaf;
150 
151     EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE_BUTTON), bCanRemove);
152     EnableWindow(GetDlgItem(hwndDlg, IDC_PROP_BUTTON), bCanProp);
153 
154     InitDefaultLangComboBox(hwndCombo);
155 }
156 
157 static BOOL CALLBACK
EnumResNameProc(HMODULE hModule,LPCWSTR lpszType,LPWSTR lpszName,LONG_PTR lParam)158 EnumResNameProc(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam)
159 {
160     HICON* phIconSm = (HICON*)lParam;
161     if (*phIconSm)
162         return FALSE;
163 
164     *phIconSm = (HICON)LoadImageW(hModule, lpszName, IMAGE_ICON,
165                                   GetSystemMetrics(SM_CXSMICON),
166                                   GetSystemMetrics(SM_CYSMICON),
167                                   0);
168     return TRUE;
169 }
170 
LoadIMEIcon(LPCTSTR pszImeFile)171 static HICON LoadIMEIcon(LPCTSTR pszImeFile)
172 {
173     WCHAR szSysDir[MAX_PATH], szPath[MAX_PATH];
174     HINSTANCE hImeInst;
175     HICON hIconSm = NULL;
176 
177     GetSystemDirectoryW(szSysDir, _countof(szSysDir));
178     StringCchPrintfW(szPath, _countof(szPath), L"%s\\%s", szSysDir, pszImeFile);
179 
180     hImeInst = LoadLibraryExW(szPath, NULL, DONT_RESOLVE_DLL_REFERENCES);
181     if (hImeInst == NULL)
182         return NULL;
183 
184     EnumResourceNamesW(hImeInst, RT_GROUP_ICON, EnumResNameProc, (LPARAM)&hIconSm);
185     FreeLibrary(hImeInst);
186 
187     return hIconSm;
188 }
189 
FindLanguageInList(HWND hwndList,LPCTSTR pszLangName)190 static HTREEITEM FindLanguageInList(HWND hwndList, LPCTSTR pszLangName)
191 {
192     TV_ITEM item;
193     TCHAR szText[128];
194     HTREEITEM hItem;
195 
196     hItem = TreeView_GetRoot(hwndList);
197     while (hItem)
198     {
199         szText[0] = 0;
200         item.mask       = TVIF_TEXT | TVIF_HANDLE;
201         item.pszText    = szText;
202         item.cchTextMax = _countof(szText);
203         item.hItem      = hItem;
204         TreeView_GetItem(hwndList, &item);
205         if (_wcsicmp(szText, pszLangName) == 0)
206             return hItem;
207 
208         hItem = TreeView_GetNextSibling(hwndList, hItem);
209     }
210 
211     return NULL;
212 }
213 
214 static VOID
AddToInputListView(HWND hwndList,INPUT_LIST_NODE * pInputNode)215 AddToInputListView(HWND hwndList, INPUT_LIST_NODE *pInputNode)
216 {
217     TV_ITEM item;
218     TV_INSERTSTRUCT insert;
219     HIMAGELIST hImageList = TreeView_GetImageList(hwndList, TVSIL_NORMAL);
220     WCHAR szKeyboard[64];
221     HTREEITEM hItem;
222     BOOL bBold = !!(pInputNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT);
223 
224     hItem = FindLanguageInList(hwndList, pInputNode->pLocale->pszName);
225     if (hItem == NULL)
226     {
227         // Language icon
228         INT LangImageIndex = -1;
229         HICON hLangIcon = CreateLayoutIcon(LOWORD(pInputNode->pLocale->dwId));
230         if (hLangIcon)
231         {
232             LangImageIndex = ImageList_AddIcon(hImageList, hLangIcon);
233             DestroyIcon(hLangIcon);
234         }
235 
236         // Language
237         ZeroMemory(&item, sizeof(item));
238         item.mask           = TVIF_TEXT | TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE;
239         item.pszText        = pInputNode->pLocale->pszName;
240         item.iImage         = LangImageIndex;
241         item.iSelectedImage = LangImageIndex;
242         item.lParam         = LOWORD(pInputNode->pLocale->dwId); // HIWORD(item.lParam) == 0
243         if (bBold)
244         {
245             item.state = item.stateMask = TVIS_BOLD;
246         }
247         insert.hParent      = TVI_ROOT;
248         insert.hInsertAfter = TVI_LAST;
249         insert.item         = item;
250         hItem = TreeView_InsertItem(hwndList, &insert);
251 
252         // The type of input method (currently keyboard only)
253         LoadStringW(hApplet, IDS_KEYBOARD, szKeyboard, _countof(szKeyboard));
254         ZeroMemory(&item, sizeof(item));
255         item.mask           = TVIF_TEXT | TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE;
256         item.pszText        = szKeyboard;
257         item.iImage         = s_iKeyboardImage;
258         item.iSelectedImage = s_iKeyboardImage;
259         item.lParam         = 0;  // HIWORD(item.lParam) == 0
260         insert.hParent      = hItem;
261         insert.hInsertAfter = TVI_LAST;
262         insert.item         = item;
263         hItem = TreeView_InsertItem(hwndList, &insert);
264     }
265     else
266     {
267         // Language
268         ZeroMemory(&item, sizeof(item));
269         item.mask           = TVIF_STATE | TVIF_HANDLE;
270         item.hItem          = hItem;
271         item.stateMask      = TVIS_BOLD;
272         if (TreeView_GetItem(hwndList, &item) && bBold && !(item.state & TVIS_BOLD))
273         {
274             // Make the item bold
275             item.mask = TVIF_STATE | TVIF_HANDLE;
276             item.hItem = hItem;
277             item.state = item.stateMask = TVIS_BOLD;
278             TreeView_SetItem(hwndList, &item);
279         }
280 
281         // The type of input method (currently keyboard only)
282         hItem = TreeView_GetChild(hwndList, hItem);
283     }
284 
285     // Input method
286     if (hItem)
287     {
288         INT ImeImageIndex = s_iDotImage;
289         if (IS_IME_HKL(pInputNode->hkl) && pInputNode->pLayout->pszImeFile) // IME?
290         {
291             HICON hImeIcon = LoadIMEIcon(pInputNode->pLayout->pszImeFile);
292             if (hImeIcon)
293             {
294                 ImeImageIndex = ImageList_AddIcon(hImageList, hImeIcon);
295                 DestroyIcon(hImeIcon);
296             }
297         }
298 
299         ZeroMemory(&item, sizeof(item));
300         item.mask           = TVIF_TEXT | TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE;
301         item.pszText        = pInputNode->pLayout->pszName;
302         item.iImage         = ImeImageIndex;
303         item.iSelectedImage = ImeImageIndex;
304         item.lParam         = (LPARAM)pInputNode; // HIWORD(item.lParam) != 0
305         if (bBold)
306         {
307             item.state = item.stateMask = TVIS_BOLD; // Make the item bold
308         }
309         insert.hParent      = hItem;
310         insert.hInsertAfter = TVI_LAST;
311         insert.item         = item;
312         hItem = TreeView_InsertItem(hwndList, &insert);
313     }
314 }
315 
ExpandTreeItem(HWND hwndTree,HTREEITEM hItem)316 static VOID ExpandTreeItem(HWND hwndTree, HTREEITEM hItem)
317 {
318     TreeView_Expand(hwndTree, hItem, TVE_EXPAND);
319     hItem = TreeView_GetChild(hwndTree, hItem);
320     while (hItem)
321     {
322         ExpandTreeItem(hwndTree, hItem);
323         hItem = TreeView_GetNextSibling(hwndTree, hItem);
324     }
325 }
326 
327 static VOID
UpdateInputListView(HWND hwndList)328 UpdateInputListView(HWND hwndList)
329 {
330     INPUT_LIST_NODE *pNode;
331     HIMAGELIST hImageList = TreeView_GetImageList(hwndList, TVSIL_NORMAL);
332     HTREEITEM hItem;
333     HICON hKeyboardIcon, hDotIcon;
334 
335     ImageList_RemoveAll(hImageList);
336     TreeView_DeleteAllItems(hwndList);
337 
338     // Add keyboard icon
339     s_iKeyboardImage = -1;
340     hKeyboardIcon = (HICON)LoadImageW(hApplet, MAKEINTRESOURCEW(IDI_KEYBOARD), IMAGE_ICON,
341                                       GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
342                                       0);
343     if (hKeyboardIcon)
344     {
345         s_iKeyboardImage = ImageList_AddIcon(hImageList, hKeyboardIcon);
346         DestroyIcon(hKeyboardIcon);
347     }
348 
349     // Add dot icon
350     s_iDotImage = -1;
351     hDotIcon = (HICON)LoadImageW(hApplet, MAKEINTRESOURCEW(IDI_DOT), IMAGE_ICON,
352                                  GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
353                                  0);
354     if (hDotIcon)
355     {
356         s_iDotImage = ImageList_AddIcon(hImageList, hDotIcon);
357         DestroyIcon(hDotIcon);
358     }
359 
360     InputList_Sort();
361 
362     s_nAliveLeafCount = InputList_GetAliveCount();
363 
364     // Add items to the list
365     for (pNode = InputList_GetFirst(); pNode; pNode = pNode->pNext)
366     {
367         if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
368             continue;
369 
370         AddToInputListView(hwndList, pNode);
371     }
372 
373     // Expand all (with counting s_nRootCount)
374     s_nRootCount = 0;
375     hItem = TreeView_GetRoot(hwndList);
376     while (hItem)
377     {
378         ++s_nRootCount;
379         ExpandTreeItem(hwndList, hItem);
380         hItem = TreeView_GetNextSibling(hwndList, hItem);
381     }
382 
383     // Redraw
384     InvalidateRect(hwndList, NULL, TRUE);
385 }
386 
387 static VOID
OnInitSettingsPage(HWND hwndDlg)388 OnInitSettingsPage(HWND hwndDlg)
389 {
390     HWND hwndInputList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
391     HIMAGELIST hLayoutImageList, hOldImageList;
392 
393     LayoutList_Create();
394     LocaleList_Create();
395     InputList_Create();
396 
397     EnableWindow(GetDlgItem(hwndDlg, IDC_LANGUAGE_BAR), FALSE);
398 
399     hLayoutImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
400                                         GetSystemMetrics(SM_CYSMICON),
401                                         ILC_COLOR8 | ILC_MASK, 0, 0);
402     if (hLayoutImageList != NULL)
403     {
404         hOldImageList = TreeView_SetImageList(hwndInputList, hLayoutImageList, TVSIL_NORMAL);
405         ImageList_Destroy(hOldImageList);
406     }
407 
408     UpdateInputListView(hwndInputList);
409 
410     SetControlsState(hwndDlg);
411 }
412 
413 
414 static VOID
OnDestroySettingsPage(HWND hwndDlg)415 OnDestroySettingsPage(HWND hwndDlg)
416 {
417     LayoutList_Destroy();
418     LocaleList_Destroy();
419     InputList_Destroy();
420 }
421 
422 
423 VOID
OnCommandSettingsPage(HWND hwndDlg,WPARAM wParam)424 OnCommandSettingsPage(HWND hwndDlg, WPARAM wParam)
425 {
426     switch (LOWORD(wParam))
427     {
428         case IDC_ADD_BUTTON:
429         {
430             if (DialogBoxW(hApplet,
431                            MAKEINTRESOURCEW(IDD_ADD),
432                            hwndDlg,
433                            AddDialogProc) == IDOK)
434             {
435                 UpdateInputListView(GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST));
436                 SetControlsState(hwndDlg);
437                 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
438             }
439         }
440         break;
441 
442         case IDC_REMOVE_BUTTON:
443         {
444             HWND hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
445             if (hwndList)
446             {
447                 HTREEITEM hItem = TreeView_GetSelection(hwndList);
448                 TV_ITEM item = { TVIF_HANDLE | TVIF_PARAM };
449                 item.hItem = hItem;
450 
451                 if (hItem && TreeView_GetItem(hwndList, &item))
452                 {
453                     if (item.lParam == 0) // Branch? (currently branch is keyboard only)
454                     {
455                         // Get root of branch
456                         item.hItem = TreeView_GetParent(hwndList, hItem);
457                         TreeView_GetItem(hwndList, &item);
458                     }
459 
460                     if (HIWORD(item.lParam)) // Leaf?
461                     {
462                         if (InputList_Remove((INPUT_LIST_NODE*)item.lParam))
463                             g_bRebootNeeded = TRUE;
464                     }
465                     else // Root?
466                     {
467                         if (InputList_RemoveByLang(LOWORD(item.lParam)))
468                             g_bRebootNeeded = TRUE;
469                     }
470 
471                     UpdateInputListView(hwndList);
472                     SetControlsState(hwndDlg);
473                     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
474                 }
475             }
476         }
477         break;
478 
479         case IDC_PROP_BUTTON:
480         {
481             HWND hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
482             if (hwndList)
483             {
484                 HTREEITEM hItem = TreeView_GetSelection(hwndList);
485                 TV_ITEM item = { TVIF_HANDLE | TVIF_PARAM };
486                 item.hItem = hItem;
487 
488                 if (hItem && TreeView_GetItem(hwndList, &item) && HIWORD(item.lParam))
489                 {
490                     if (DialogBoxParamW(hApplet,
491                                         MAKEINTRESOURCEW(IDD_INPUT_LANG_PROP),
492                                         hwndDlg,
493                                         EditDialogProc,
494                                         item.lParam) == IDOK)
495                     {
496                         UpdateInputListView(hwndList);
497                         SetControlsState(hwndDlg);
498                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
499                     }
500                 }
501             }
502         }
503         break;
504 
505         case IDC_KEY_SET_BTN:
506         {
507             DialogBoxW(hApplet,
508                        MAKEINTRESOURCEW(IDD_KEYSETTINGS),
509                        hwndDlg,
510                        KeySettingsDialogProc);
511         }
512         break;
513 
514         case IDC_LANGUAGE_BAR:
515         {
516             // FIXME
517             break;
518         }
519 
520         case IDC_DEFAULT_LANGUAGE:
521         {
522             if (HIWORD(wParam) == CBN_SELENDOK)
523             {
524                 HWND hwndList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT_LIST);
525                 HWND hwndCombo = GetDlgItem(hwndDlg, IDC_DEFAULT_LANGUAGE);
526                 INT iSelected = (INT)SendMessageW(hwndCombo, CB_GETCURSEL, 0, 0);
527                 if (iSelected != CB_ERR)
528                 {
529                     LPARAM lParam = SendMessageW(hwndCombo, CB_GETITEMDATA, iSelected, 0);
530                     if (lParam)
531                     {
532                         INPUT_LIST_NODE* pNode = (INPUT_LIST_NODE*)lParam;
533                         if (!(pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT))
534                         {
535                             g_bRebootNeeded = TRUE;
536                             InputList_SetDefault(pNode);
537                             UpdateInputListView(hwndList);
538                             SetControlsState(hwndDlg);
539                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
540                         }
541                     }
542                 }
543             }
544         }
545     }
546 }
547 
EnableProcessPrivileges(LPCWSTR lpPrivilegeName,BOOL bEnable)548 BOOL EnableProcessPrivileges(LPCWSTR lpPrivilegeName, BOOL bEnable)
549 {
550     HANDLE hToken;
551     LUID luid;
552     TOKEN_PRIVILEGES tokenPrivileges;
553     BOOL Ret;
554 
555     Ret = OpenProcessToken(GetCurrentProcess(),
556                            TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
557                            &hToken);
558     if (!Ret)
559         return Ret;     // failure
560 
561     Ret = LookupPrivilegeValueW(NULL, lpPrivilegeName, &luid);
562     if (Ret)
563     {
564         tokenPrivileges.PrivilegeCount = 1;
565         tokenPrivileges.Privileges[0].Luid = luid;
566         tokenPrivileges.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
567 
568         Ret = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, 0, 0);
569     }
570 
571     CloseHandle(hToken);
572     return Ret;
573 }
574 
575 static INT_PTR
OnNotifySettingsPage(HWND hwndDlg,LPARAM lParam)576 OnNotifySettingsPage(HWND hwndDlg, LPARAM lParam)
577 {
578     LPNMHDR header = (LPNMHDR)lParam;
579 
580     switch (header->code)
581     {
582         case TVN_SELCHANGED:
583         {
584             SetControlsState(hwndDlg);
585             break;
586         }
587 
588         case TVN_ITEMEXPANDING:
589         {
590             // FIXME: Prevent collapse (COMCTL32 is buggy)
591             // https://bugs.winehq.org/show_bug.cgi?id=53727
592             NM_TREEVIEW* pTreeView = (NM_TREEVIEW*)lParam;
593             if ((pTreeView->action & TVE_TOGGLE) == TVE_COLLAPSE)
594             {
595                 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
596                 return TRUE;
597             }
598             break;
599         }
600 
601         case PSN_APPLY:
602         {
603             /* Write Input Methods list to registry */
604             g_bRebootNeeded |= InputList_Process();
605             break;
606         }
607     }
608 
609     return 0;
610 }
611 
612 INT_PTR CALLBACK
SettingsPageProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)613 SettingsPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
614 {
615     switch (uMsg)
616     {
617         case WM_INITDIALOG:
618             OnInitSettingsPage(hwndDlg);
619             return TRUE;
620 
621         case WM_DESTROY:
622             OnDestroySettingsPage(hwndDlg);
623             break;
624 
625         case WM_COMMAND:
626             OnCommandSettingsPage(hwndDlg, wParam);
627             break;
628 
629         case WM_NOTIFY:
630             return OnNotifySettingsPage(hwndDlg, lParam);
631     }
632 
633     return FALSE;
634 }
635