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