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 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 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 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 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 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 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 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 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 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 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 415 OnDestroySettingsPage(HWND hwndDlg) 416 { 417 LayoutList_Destroy(); 418 LocaleList_Destroy(); 419 InputList_Destroy(); 420 } 421 422 423 VOID 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 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 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 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