1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Display Control Panel 4 * FILE: dll/cpl/desk/theme.c 5 * PURPOSE: Handling themes and visual effects 6 * 7 * PROGRAMMERS: Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 8 * Ismael Ferreras Morezuelas (swyterzone+reactos@gmail.com) 9 */ 10 11 #include "desk.h" 12 13 #include <shlwapi.h> 14 #include <uxtheme.h> 15 #include <uxundoc.h> 16 #include <vssym32.h> 17 18 static const WCHAR g_CPColors[] = L"Control Panel\\Colors"; 19 static const WCHAR g_CPANewSchemes[] = L"Control Panel\\Appearance\\New Schemes"; 20 static const WCHAR g_CPMetrics[] = L"Control Panel\\Desktop\\WindowMetrics"; 21 static const WCHAR g_SelectedStyle[] = L"SelectedStyle"; 22 23 /******************************************************************************/ 24 25 /* This is the list of names for the colors stored in the registry */ 26 static const WCHAR *g_RegColorNames[NUM_COLORS] = { 27 L"Scrollbar", /* 00 = COLOR_SCROLLBAR */ 28 L"Background", /* 01 = COLOR_DESKTOP */ 29 L"ActiveTitle", /* 02 = COLOR_ACTIVECAPTION */ 30 L"InactiveTitle", /* 03 = COLOR_INACTIVECAPTION */ 31 L"Menu", /* 04 = COLOR_MENU */ 32 L"Window", /* 05 = COLOR_WINDOW */ 33 L"WindowFrame", /* 06 = COLOR_WINDOWFRAME */ 34 L"MenuText", /* 07 = COLOR_MENUTEXT */ 35 L"WindowText", /* 08 = COLOR_WINDOWTEXT */ 36 L"TitleText", /* 09 = COLOR_CAPTIONTEXT */ 37 L"ActiveBorder", /* 10 = COLOR_ACTIVEBORDER */ 38 L"InactiveBorder", /* 11 = COLOR_INACTIVEBORDER */ 39 L"AppWorkSpace", /* 12 = COLOR_APPWORKSPACE */ 40 L"Hilight", /* 13 = COLOR_HIGHLIGHT */ 41 L"HilightText", /* 14 = COLOR_HIGHLIGHTTEXT */ 42 L"ButtonFace", /* 15 = COLOR_BTNFACE */ 43 L"ButtonShadow", /* 16 = COLOR_BTNSHADOW */ 44 L"GrayText", /* 17 = COLOR_GRAYTEXT */ 45 L"ButtonText", /* 18 = COLOR_BTNTEXT */ 46 L"InactiveTitleText", /* 19 = COLOR_INACTIVECAPTIONTEXT */ 47 L"ButtonHilight", /* 20 = COLOR_BTNHIGHLIGHT */ 48 L"ButtonDkShadow", /* 21 = COLOR_3DDKSHADOW */ 49 L"ButtonLight", /* 22 = COLOR_3DLIGHT */ 50 L"InfoText", /* 23 = COLOR_INFOTEXT */ 51 L"InfoWindow", /* 24 = COLOR_INFOBK */ 52 L"ButtonAlternateFace", /* 25 = COLOR_ALTERNATEBTNFACE */ 53 L"HotTrackingColor", /* 26 = COLOR_HOTLIGHT */ 54 L"GradientActiveTitle", /* 27 = COLOR_GRADIENTACTIVECAPTION */ 55 L"GradientInactiveTitle", /* 28 = COLOR_GRADIENTINACTIVECAPTION */ 56 L"MenuHilight", /* 29 = COLOR_MENUHILIGHT */ 57 L"MenuBar", /* 30 = COLOR_MENUBAR */ 58 }; 59 60 /******************************************************************************/ 61 62 VOID 63 SchemeSetMetric(IN COLOR_SCHEME *scheme, int id, int value) 64 { 65 switch (id) 66 { 67 case SIZE_BORDER_WIDTH: scheme->ncMetrics.iBorderWidth = value; break; 68 case SIZE_SCROLL_WIDTH: scheme->ncMetrics.iScrollWidth = value; break; 69 case SIZE_SCROLL_HEIGHT: scheme->ncMetrics.iScrollHeight = value; break; 70 case SIZE_CAPTION_WIDTH: scheme->ncMetrics.iCaptionWidth = value; break; 71 case SIZE_CAPTION_HEIGHT: scheme->ncMetrics.iCaptionHeight = value; break; 72 case SIZE_SM_CAPTION_WIDTH: scheme->ncMetrics.iSmCaptionWidth = value; break; 73 case SIZE_SM_CAPTION_HEIGHT: scheme->ncMetrics.iSmCaptionHeight = value; break; 74 case SIZE_MENU_WIDTH: scheme->ncMetrics.iMenuWidth = value; break; 75 case SIZE_MENU_HEIGHT: scheme->ncMetrics.iMenuHeight = value; break; 76 case SIZE_ICON: scheme->iIconSize = value; break; 77 case SIZE_ICON_SPACE_X: scheme->icMetrics.iHorzSpacing = value; break; 78 case SIZE_ICON_SPACE_Y: scheme->icMetrics.iVertSpacing = value; break; 79 } 80 } 81 82 int 83 SchemeGetMetric(IN COLOR_SCHEME *scheme, int id) 84 { 85 switch (id) 86 { 87 case SIZE_BORDER_WIDTH: return scheme->ncMetrics.iBorderWidth; 88 case SIZE_SCROLL_WIDTH: return scheme->ncMetrics.iScrollWidth; 89 case SIZE_SCROLL_HEIGHT: return scheme->ncMetrics.iScrollHeight; 90 case SIZE_CAPTION_WIDTH: return scheme->ncMetrics.iCaptionWidth; 91 case SIZE_CAPTION_HEIGHT: return scheme->ncMetrics.iCaptionHeight; 92 case SIZE_SM_CAPTION_WIDTH: return scheme->ncMetrics.iSmCaptionWidth; 93 case SIZE_SM_CAPTION_HEIGHT: return scheme->ncMetrics.iSmCaptionHeight; 94 case SIZE_MENU_WIDTH: return scheme->ncMetrics.iMenuWidth; 95 case SIZE_MENU_HEIGHT: return scheme->ncMetrics.iMenuHeight; 96 case SIZE_ICON: return scheme->iIconSize; 97 case SIZE_ICON_SPACE_X: return scheme->icMetrics.iHorzSpacing; 98 case SIZE_ICON_SPACE_Y: return scheme->icMetrics.iVertSpacing; 99 } 100 return 0; 101 } 102 103 PLOGFONTW 104 SchemeGetFont(IN COLOR_SCHEME *scheme, int id) 105 { 106 switch (id) 107 { 108 case FONT_CAPTION: return &scheme->ncMetrics.lfCaptionFont; 109 case FONT_SMCAPTION: return &scheme->ncMetrics.lfSmCaptionFont; 110 case FONT_MENU: return &scheme->ncMetrics.lfMenuFont; 111 case FONT_STATUS: return &scheme->ncMetrics.lfStatusFont; 112 case FONT_MESSAGE: return &scheme->ncMetrics.lfMessageFont; 113 case FONT_ICON: return &scheme->icMetrics.lfFont; 114 } 115 return NULL; 116 } 117 118 /* 119 * LoadCurrentScheme: Populates the passed scheme based on the current system settings 120 */ 121 BOOL 122 LoadCurrentScheme(OUT COLOR_SCHEME *scheme) 123 { 124 INT i, Result; 125 HKEY hKey; 126 BOOL ret; 127 #if (WINVER >= 0x0600) 128 OSVERSIONINFO osvi; 129 #endif 130 131 /* Load colors */ 132 for (i = 0; i < NUM_COLORS; i++) 133 { 134 scheme->crColor[i] = (COLORREF)GetSysColor(i); 135 } 136 137 /* Load non client metrics */ 138 scheme->ncMetrics.cbSize = sizeof(NONCLIENTMETRICSW); 139 140 #if (WINVER >= 0x0600) 141 /* Size of NONCLIENTMETRICSA/W depends on current version of the OS. 142 * see: 143 * https://msdn.microsoft.com/en-us/library/windows/desktop/ff729175%28v=vs.85%29.aspx 144 */ 145 if (GetVersionEx(&osvi)) 146 { 147 /* Windows XP and earlier */ 148 if (osvi.dwMajorVersion <= 5) 149 scheme->ncMetrics.cbSize -= sizeof(scheme->ncMetrics.iPaddedBorderWidth); 150 } 151 #endif 152 153 ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 154 sizeof(NONCLIENTMETRICSW), 155 &scheme->ncMetrics, 156 0); 157 if (!ret) return FALSE; 158 159 /* Load icon metrics */ 160 scheme->icMetrics.cbSize = sizeof(ICONMETRICSW); 161 ret = SystemParametersInfoW(SPI_GETICONMETRICS, 162 sizeof(ICONMETRICSW), 163 &scheme->icMetrics, 164 0); 165 if (!ret) return FALSE; 166 167 /* Load flat menu style */ 168 ret = SystemParametersInfoW(SPI_GETFLATMENU, 169 0, 170 &scheme->bFlatMenus, 171 0); 172 if (!ret) return FALSE; 173 174 /* Effects */ 175 /* Use the following transition effect for menus and tooltips */ 176 ret = SystemParametersInfoW(SPI_GETMENUANIMATION, 177 0, 178 &scheme->Effects.bMenuAnimation, 179 0); 180 if (!ret) return FALSE; 181 182 ret = SystemParametersInfoW(SPI_GETMENUFADE, 183 0, 184 &scheme->Effects.bMenuFade, 185 0); 186 if (!ret) return FALSE; 187 188 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings 189 * Just keep them in sync for now: 190 */ 191 scheme->Effects.bTooltipAnimation = scheme->Effects.bMenuAnimation; 192 scheme->Effects.bTooltipFade = scheme->Effects.bMenuFade; 193 194 /* Use the following transition effect for menus and tooltips */ 195 ret = SystemParametersInfoW(SPI_GETFONTSMOOTHING, 196 0, 197 &scheme->Effects.bFontSmoothing, 198 0); 199 if (!ret) return FALSE; 200 201 ret = SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 202 0, 203 &scheme->Effects.uiFontSmoothingType, 204 0); 205 if (!ret) return FALSE; 206 207 /* Show shadows under menus */ 208 ret = SystemParametersInfoW(SPI_GETDROPSHADOW, 209 0, 210 &scheme->Effects.bDropShadow, 211 0); 212 if (!ret) return FALSE; 213 214 /* Show content of windows during dragging */ 215 ret = SystemParametersInfoW(SPI_GETDRAGFULLWINDOWS, 216 0, 217 &scheme->Effects.bDragFullWindows, 218 0); 219 if (!ret) return FALSE; 220 221 /* Hide underlined letters for keyboard navigation until the Alt key is pressed */ 222 ret = SystemParametersInfoW(SPI_GETKEYBOARDCUES, 223 0, 224 &scheme->Effects.bKeyboardCues, 225 0); 226 if (!ret) return FALSE; 227 228 /* Read the icon size from registry */ 229 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPMetrics, &hKey); 230 if(Result == ERROR_SUCCESS) 231 { 232 scheme->iIconSize = SHRegGetIntW(hKey, L"Shell Icon Size", 32); 233 RegCloseKey(hKey); 234 } 235 236 return TRUE; 237 } 238 239 /* 240 * LoadSchemeFromReg: Populates the passed scheme with values retrieved from registry 241 */ 242 BOOL 243 LoadSchemeFromReg(OUT COLOR_SCHEME *scheme, IN PTHEME_SELECTION pSelectedTheme) 244 { 245 INT i; 246 WCHAR strValueName[10], strSchemeKey[MAX_PATH]; 247 HKEY hkScheme = NULL; 248 DWORD dwType, dwLength; 249 UINT64 iSize; 250 BOOL Ret = TRUE; 251 LONG result; 252 253 wsprintf(strSchemeKey, L"%s\\%s\\Sizes\\%s", 254 g_CPANewSchemes, 255 pSelectedTheme->Color->StyleName, 256 pSelectedTheme->Size->StyleName); 257 258 result = RegOpenKeyW(HKEY_CURRENT_USER, strSchemeKey, &hkScheme); 259 if (result != ERROR_SUCCESS) return FALSE; 260 261 scheme->bFlatMenus = SHRegGetIntW(hkScheme, L"FlatMenus", 0); 262 263 for (i = 0; i < NUM_COLORS; i++) 264 { 265 wsprintf(strValueName, L"Color #%d", i); 266 dwLength = sizeof(COLORREF); 267 result = RegQueryValueExW(hkScheme, 268 strValueName, 269 NULL, 270 &dwType, 271 (LPBYTE)&scheme->crColor[i], 272 &dwLength); 273 if (result != ERROR_SUCCESS || dwType != REG_DWORD) 274 { 275 /* Failed to read registry value, initialize with current setting for now */ 276 scheme->crColor[i] = GetSysColor(i); 277 } 278 } 279 280 for (i = 0; i < NUM_FONTS; i++) 281 { 282 PLOGFONTW lpfFont = SchemeGetFont(scheme, i); 283 284 wsprintf(strValueName, L"Font #%d", i); 285 dwLength = sizeof(LOGFONT); 286 result = RegQueryValueExW(hkScheme, 287 strValueName, 288 NULL, 289 &dwType, 290 (LPBYTE)lpfFont, 291 &dwLength); 292 if (result != ERROR_SUCCESS || dwType != REG_BINARY || 293 dwLength != sizeof(LOGFONT)) 294 { 295 /* Failed to read registry value */ 296 Ret = FALSE; 297 } 298 } 299 300 for (i = 0; i < NUM_SIZES; i++) 301 { 302 wsprintf(strValueName, L"Size #%d", i); 303 dwLength = sizeof(UINT64); 304 result = RegQueryValueExW(hkScheme, 305 strValueName, 306 NULL, 307 &dwType, 308 (LPBYTE)&iSize, 309 &dwLength); 310 if (result != ERROR_SUCCESS || dwType != REG_QWORD || 311 dwLength != sizeof(UINT64)) 312 { 313 /* Failed to read registry value, initialize with current setting for now */ 314 } 315 else 316 { 317 SchemeSetMetric(scheme, i, (int)iSize); 318 } 319 } 320 321 RegCloseKey(hkScheme); 322 323 return Ret; 324 } 325 326 /* 327 * ApplyScheme: Applies the selected scheme and stores its id in the registry if needed 328 */ 329 VOID 330 ApplyScheme(IN COLOR_SCHEME *scheme, IN PTHEME_SELECTION pSelectedTheme) 331 { 332 INT i, Result; 333 HKEY hKey; 334 WCHAR clText[16], *StyleName; 335 INT ColorList[NUM_COLORS]; 336 337 /* Apply system colors */ 338 for (i = 0; i < NUM_COLORS; i++) 339 ColorList[i] = i; 340 SetSysColors(NUM_COLORS, ColorList, scheme->crColor); 341 342 /* Save colors to registry */ 343 Result = RegCreateKeyW(HKEY_CURRENT_USER, g_CPColors, &hKey); 344 if (Result == ERROR_SUCCESS) 345 { 346 for (i = 0; i < NUM_COLORS; i++) 347 { 348 wsprintf(clText, 349 L"%d %d %d", 350 GetRValue(scheme->crColor[i]), 351 GetGValue(scheme->crColor[i]), 352 GetBValue(scheme->crColor[i])); 353 354 RegSetValueExW(hKey, 355 g_RegColorNames[i], 356 0, 357 REG_SZ, 358 (BYTE *)clText, 359 (lstrlen(clText) + 1) * sizeof(WCHAR)); 360 } 361 RegCloseKey(hKey); 362 } 363 364 /* Apply non client metrics */ 365 SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, 366 sizeof(NONCLIENTMETRICS), 367 &scheme->ncMetrics, 368 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); 369 370 /* Apply icon metrics */ 371 SystemParametersInfoW(SPI_SETICONMETRICS, 372 sizeof(ICONMETRICS), 373 &scheme->icMetrics, 374 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); 375 376 /* Effects, save only when needed: */ 377 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings 378 * Just keep them in sync for now. 379 */ 380 381 #define SYS_CONFIG(__uiAction, __uiParam, __pvParam) \ 382 SystemParametersInfoW(__uiAction, __uiParam, __pvParam, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE) 383 384 scheme->Effects.bTooltipAnimation = scheme->Effects.bMenuAnimation; 385 scheme->Effects.bTooltipFade = scheme->Effects.bMenuFade; 386 387 /* Use the following transition effect for menus and tooltips */ 388 SYS_CONFIG(SPI_SETMENUANIMATION, 0, IntToPtr(scheme->Effects.bMenuAnimation)); 389 SYS_CONFIG(SPI_SETMENUFADE, 0, IntToPtr(scheme->Effects.bMenuFade)); 390 391 /* Use the following method to smooth edges of screen fonts */ 392 SYS_CONFIG(SPI_SETFONTSMOOTHING, scheme->Effects.bFontSmoothing, 0); 393 SYS_CONFIG(SPI_SETFONTSMOOTHINGTYPE, 0, IntToPtr(scheme->Effects.uiFontSmoothingType)); 394 395 /* 396 * Refresh and redraw all the windows, otherwise the font smoothing changes 397 * only appear after any future partial region invalidation. 398 * Not everyone listens for this WM_SETTINGCHANGE, including the shell and most third party programs. 399 */ 400 InvalidateRect(NULL, NULL, TRUE); 401 402 /* Use large icons */ 403 //SYS_CONFIG(SPI_GETDRAGFULLWINDOWS, (PVOID) g->SchemeAdv.Effects.bMenuFade); 404 405 /* Show shadows under menus */ 406 SYS_CONFIG(SPI_SETDROPSHADOW, 0, IntToPtr(scheme->Effects.bDropShadow)); 407 408 /* Show window contents while dragging */ 409 SYS_CONFIG(SPI_SETDRAGFULLWINDOWS, scheme->Effects.bDragFullWindows, 0); 410 411 /* Hide underlined letters for keyboard navigation until I press the Alt key */ 412 SYS_CONFIG(SPI_SETKEYBOARDCUES, 0, IntToPtr(scheme->Effects.bKeyboardCues)); 413 414 SYS_CONFIG(SPI_SETFLATMENU, 0, IntToPtr(scheme->bFlatMenus)); 415 416 // SYS_CONFIG(SPI_SETACTIVEWINDOWTRACKING, 0, IntToPtr(scheme->Effects.bActiveWindowTracking)); 417 // SYS_CONFIG(SPI_SETCOMBOBOXANIMATION, 0, IntToPtr(scheme->Effects.bComboBoxAnimation)); 418 // SYS_CONFIG(SPI_SETLISTBOXSMOOTHSCROLLING, 0, IntToPtr(scheme->Effects.bListBoxSmoothScrolling)); 419 // SYS_CONFIG(SPI_SETGRADIENTCAPTIONS, 0, IntToPtr(scheme->Effects.bGradientCaptions)); 420 // SYS_CONFIG(SPI_SETACTIVEWNDTRKZORDER, 0, IntToPtr(scheme->Effects.bActiveWndTrkZorder)); 421 // SYS_CONFIG(SPI_SETHOTTRACKING, 0, IntToPtr(scheme->Effects.bHotTracking)); 422 // SYS_CONFIG(SPI_SETSELECTIONFADE, 0, IntToPtr(scheme->Effects.bSelectionFade)); 423 SYS_CONFIG(SPI_SETTOOLTIPANIMATION, 0, IntToPtr(scheme->Effects.bTooltipAnimation)); 424 SYS_CONFIG(SPI_SETTOOLTIPFADE, 0, IntToPtr(scheme->Effects.bTooltipFade)); 425 // SYS_CONFIG(SPI_SETCURSORSHADOW, 0, IntToPtr(scheme->Effects.bCursorShadow)); 426 // SYS_CONFIG(SPI_SETUIEFFECTS, 0, IntToPtr(scheme->Effects.bUiEffects)); 427 428 #undef SYS_CONFIG 429 430 /* Save SchemeId in the registry */ 431 if (pSelectedTheme->Theme != NULL && pSelectedTheme->ThemeActive == FALSE) 432 { 433 StyleName = pSelectedTheme->Color->StyleName; 434 SHSetValueW(HKEY_CURRENT_USER, 435 g_CPANewSchemes, 436 g_SelectedStyle, 437 REG_SZ, 438 StyleName, 439 (lstrlenW(StyleName) + 1) * sizeof(WCHAR)); 440 } 441 } 442 443 static THEME* 444 CreateTheme(LPCWSTR pszName, LPCWSTR pszDisplayName) 445 { 446 PTHEME pTheme; 447 448 pTheme = (PTHEME) malloc(sizeof(THEME)); 449 if (pTheme == NULL) return NULL; 450 451 pTheme->DisplayName = _wcsdup(pszDisplayName); 452 if (pTheme->DisplayName == NULL) 453 { 454 free(pTheme); 455 return NULL; 456 } 457 458 pTheme->ColoursList = NULL; 459 pTheme->NextTheme = NULL; 460 pTheme->SizesList = NULL; 461 462 if (pszName == NULL) 463 { 464 pTheme->ThemeFileName = NULL; 465 return pTheme; 466 } 467 468 pTheme->ThemeFileName = _wcsdup(pszName); 469 if (pTheme->ThemeFileName == NULL) 470 { 471 free(pTheme->DisplayName); 472 free(pTheme); 473 return NULL; 474 } 475 476 return pTheme; 477 } 478 479 static PTHEME_STYLE 480 CreateStyle(LPCWSTR pszName, LPCWSTR pszDisplayName) 481 { 482 PTHEME_STYLE pStyle; 483 484 pStyle = (PTHEME_STYLE) malloc(sizeof(THEME_STYLE)); 485 if (pStyle == NULL) return NULL; 486 487 pStyle->StyleName = _wcsdup(pszName); 488 if (pStyle->StyleName == NULL) 489 { 490 free(pStyle); 491 return NULL; 492 } 493 494 pStyle->DisplayName = _wcsdup(pszDisplayName); 495 if (pStyle->DisplayName == NULL) 496 { 497 free(pStyle->StyleName); 498 free(pStyle); 499 return NULL; 500 } 501 502 pStyle->ChildStyle = NULL; 503 pStyle->NextStyle = NULL; 504 505 return pStyle; 506 } 507 508 static void 509 CleanupStyles(IN PTHEME_STYLE pStylesList) 510 { 511 PTHEME_STYLE pStyle, pStyleOld; 512 513 pStyle = pStylesList; 514 while (pStyle) 515 { 516 if (pStyle->ChildStyle) CleanupStyles(pStyle->ChildStyle); 517 if (pStyle->DisplayName) free(pStyle->DisplayName); 518 if (pStyle->StyleName) free(pStyle->StyleName); 519 520 pStyleOld = pStyle; 521 pStyle = pStyle->NextStyle; 522 free(pStyleOld); 523 } 524 } 525 526 void 527 CleanupThemes(IN PTHEME pThemeList) 528 { 529 PTHEME pTheme, pThemeOld; 530 531 pTheme = pThemeList; 532 while (pTheme) 533 { 534 CleanupStyles(pTheme->ColoursList); 535 if (pTheme->SizesList) CleanupStyles(pTheme->SizesList); 536 if (pTheme->DisplayName) free(pTheme->DisplayName); 537 if (pTheme->ThemeFileName) free(pTheme->ThemeFileName); 538 539 pThemeOld = pTheme; 540 pTheme = pTheme->NextTheme; 541 free(pThemeOld); 542 } 543 } 544 545 static PTHEME_STYLE 546 FindStyle(IN PTHEME_STYLE pStylesList, IN PCWSTR StyleName) 547 { 548 PTHEME_STYLE pStyle; 549 550 for (pStyle = pStylesList; pStyle; pStyle = pStyle->NextStyle) 551 { 552 if (_wcsicmp(pStyle->StyleName, StyleName) == 0) 553 { 554 return pStyle; 555 } 556 } 557 558 /* If we can't find the style requested, return the first one */ 559 return pStylesList; 560 } 561 562 /* 563 * LoadSchemeSizes: Returns a list of sizes from the registry key of a scheme 564 */ 565 static PTHEME_STYLE 566 LoadSchemeSizes(IN HKEY hkScheme) 567 { 568 HKEY hkSizes, hkSize; 569 INT Result; 570 INT iStyle; 571 WCHAR wstrSizeName[5], wstrDisplayName[50]; 572 THEME_STYLE *List = NULL, *pCurrentStyle; 573 574 Result = RegOpenKeyW(hkScheme, L"Sizes", &hkSizes); 575 if (Result != ERROR_SUCCESS) return NULL; 576 577 iStyle = 0; 578 while ((RegEnumKeyW(hkSizes, iStyle, wstrSizeName, 5) == ERROR_SUCCESS)) 579 { 580 iStyle++; 581 582 Result = RegOpenKeyW(hkSizes, wstrSizeName, &hkSize); 583 if (Result != ERROR_SUCCESS) continue; 584 585 Result = RegLoadMUIStringW(hkSize, 586 L"DisplayName", 587 wstrDisplayName, 588 sizeof(wstrDisplayName), 589 NULL, 590 0, 591 NULL); 592 if (Result != ERROR_SUCCESS) 593 { 594 Result = RegLoadMUIStringW(hkSize, 595 L"LegacyName", 596 wstrDisplayName, 597 sizeof(wstrDisplayName), 598 NULL, 599 0, 600 NULL); 601 } 602 603 if (Result == ERROR_SUCCESS) 604 pCurrentStyle = CreateStyle(wstrSizeName, wstrDisplayName); 605 else 606 pCurrentStyle = CreateStyle(wstrSizeName, wstrSizeName); 607 608 if (pCurrentStyle != NULL) 609 { 610 pCurrentStyle->NextStyle = List; 611 List = pCurrentStyle; 612 } 613 614 RegCloseKey(hkSize); 615 } 616 617 RegCloseKey(hkSizes); 618 return List; 619 } 620 621 /* 622 * LoadClassicColorSchemes: Returns a list of classic theme colours from the registry key of a scheme 623 */ 624 static THEME_STYLE* 625 LoadClassicColorSchemes(VOID) 626 { 627 INT Result; 628 HKEY hkNewSchemes, hkScheme; 629 INT iStyle; 630 WCHAR wstrStyleName[5], wstrDisplayName[50]; 631 THEME_STYLE *List = NULL, *pCurrentStyle; 632 633 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes); 634 if (Result != ERROR_SUCCESS) return NULL; 635 636 iStyle = 0; 637 while ((RegEnumKeyW(hkNewSchemes, iStyle, wstrStyleName, 5) == ERROR_SUCCESS)) 638 { 639 iStyle++; 640 641 Result = RegOpenKeyW(hkNewSchemes, wstrStyleName, &hkScheme); 642 if (Result != ERROR_SUCCESS) continue; 643 644 Result = RegLoadMUIStringW(hkScheme, 645 L"DisplayName", 646 wstrDisplayName, 647 sizeof(wstrDisplayName), 648 NULL, 649 0, 650 NULL); 651 if (Result != ERROR_SUCCESS) 652 { 653 Result = RegLoadMUIStringW(hkScheme, 654 L"LegacyName", 655 wstrDisplayName, 656 sizeof(wstrDisplayName), 657 NULL, 658 0, 659 NULL); 660 } 661 662 if (Result == ERROR_SUCCESS) 663 pCurrentStyle = CreateStyle(wstrStyleName, wstrDisplayName); 664 else 665 pCurrentStyle = CreateStyle(wstrStyleName, wstrStyleName); 666 667 if (pCurrentStyle != NULL) 668 { 669 pCurrentStyle->NextStyle = List; 670 pCurrentStyle->ChildStyle = LoadSchemeSizes(hkScheme); 671 if(pCurrentStyle->ChildStyle == NULL) 672 CleanupStyles(pCurrentStyle); 673 else 674 List = pCurrentStyle; 675 } 676 677 RegCloseKey(hkScheme); 678 } 679 680 RegCloseKey(hkNewSchemes); 681 return List; 682 } 683 684 typedef HRESULT (WINAPI *ENUMTHEMESTYLE) (LPCWSTR, LPWSTR, DWORD, PTHEMENAMES); 685 686 static THEME_STYLE* 687 EnumThemeStyles(IN LPCWSTR pszThemeFileName, IN ENUMTHEMESTYLE pfnEnumTheme) 688 { 689 DWORD index = 0; 690 THEMENAMES names; 691 THEME_STYLE *List = NULL, **ppPrevStyle, *pCurrentStyle; 692 693 ppPrevStyle = &List; 694 695 while (SUCCEEDED(pfnEnumTheme (pszThemeFileName, NULL, index++, &names))) 696 { 697 pCurrentStyle = CreateStyle(names.szName, names.szDisplayName); 698 if(pCurrentStyle == NULL) break; 699 700 *ppPrevStyle = pCurrentStyle; 701 ppPrevStyle = &pCurrentStyle->NextStyle; 702 } 703 704 return List; 705 } 706 707 PTHEME LoadTheme(IN LPCWSTR pszThemeFileName,IN LPCWSTR pszThemeName) 708 { 709 PTHEME pTheme = CreateTheme(pszThemeFileName, pszThemeName); 710 if (pTheme == NULL) 711 return NULL; 712 713 pTheme->SizesList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeSizes); 714 pTheme->ColoursList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeColors); 715 if(pTheme->SizesList == NULL || pTheme->ColoursList == NULL) 716 { 717 CleanupThemes(pTheme); 718 return NULL; 719 } 720 721 return pTheme; 722 } 723 724 BOOL CALLBACK 725 EnumThemeProc(IN LPVOID lpReserved, 726 IN LPCWSTR pszThemeFileName, 727 IN LPCWSTR pszThemeName, 728 IN LPCWSTR pszToolTip, 729 IN LPVOID lpReserved2, 730 IN OUT LPVOID lpData) 731 { 732 PTHEME *List, pTheme; 733 734 List = (PTHEME*)lpData; 735 pTheme = LoadTheme(pszThemeFileName, pszThemeName); 736 if (pTheme == NULL) return FALSE; 737 738 pTheme->NextTheme = *List; 739 *List = pTheme; 740 741 return TRUE; 742 } 743 744 /* 745 * LoadThemes: Returns a list that contains tha classic theme and 746 * the visual styles of the system 747 */ 748 PTHEME 749 LoadThemes(VOID) 750 { 751 HRESULT hret; 752 PTHEME pClassicTheme; 753 WCHAR strClassicTheme[40]; 754 WCHAR szThemesPath[MAX_PATH], *pszClassicTheme; 755 int res; 756 757 /* Insert the classic theme */ 758 res = LoadString(hApplet, IDS_CLASSIC_THEME, strClassicTheme, 40); 759 pszClassicTheme = (res > 0 ? strClassicTheme : L"Classic Theme"); 760 pClassicTheme = CreateTheme(NULL, pszClassicTheme); 761 if (pClassicTheme == NULL) return NULL; 762 pClassicTheme->ColoursList = LoadClassicColorSchemes(); 763 764 /* Get path to themes folder */ 765 ZeroMemory(szThemesPath, sizeof(szThemesPath)); 766 hret = SHGetFolderPathW (NULL, CSIDL_RESOURCES, NULL, SHGFP_TYPE_DEFAULT, szThemesPath); 767 if (FAILED(hret)) return pClassicTheme; 768 lstrcatW (szThemesPath, L"\\Themes"); 769 770 /* Enumerate themes */ 771 hret = EnumThemes( szThemesPath, EnumThemeProc, &pClassicTheme->NextTheme); 772 if (FAILED(hret)) 773 { 774 pClassicTheme->NextTheme = NULL; 775 if (pClassicTheme->ColoursList == NULL) 776 { 777 free(pClassicTheme->DisplayName); 778 free(pClassicTheme); 779 return NULL; 780 } 781 } 782 783 return pClassicTheme; 784 } 785 786 /* 787 * FindSelectedTheme: Finds the specified theme in the list of themes 788 * or loads it if it was not loaded already. 789 */ 790 BOOL 791 FindOrAppendTheme(IN PTHEME pThemeList, 792 IN LPCWSTR pwszThemeFileName, 793 IN LPCWSTR pwszColorBuff, 794 IN LPCWSTR pwszSizeBuff, 795 OUT PTHEME_SELECTION pSelectedTheme) 796 { 797 PTHEME pTheme; 798 PTHEME pFoundTheme = NULL; 799 800 ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION)); 801 802 for (pTheme = pThemeList; pTheme; pTheme = pTheme->NextTheme) 803 { 804 if (pTheme->ThemeFileName && 805 _wcsicmp(pTheme->ThemeFileName, pwszThemeFileName) == 0) 806 { 807 pFoundTheme = pTheme; 808 break; 809 } 810 811 if (pTheme->NextTheme == NULL) 812 break; 813 } 814 815 if (!pFoundTheme) 816 { 817 pFoundTheme = LoadTheme(pwszThemeFileName, pwszThemeFileName); 818 if (!pFoundTheme) 819 return FALSE; 820 821 pTheme->NextTheme = pFoundTheme; 822 } 823 824 pSelectedTheme->ThemeActive = TRUE; 825 pSelectedTheme->Theme = pFoundTheme; 826 if (pwszColorBuff) 827 pSelectedTheme->Color = FindStyle(pFoundTheme->ColoursList, pwszColorBuff); 828 else 829 pSelectedTheme->Color = pFoundTheme->ColoursList; 830 831 if (pwszSizeBuff) 832 pSelectedTheme->Size = FindStyle(pFoundTheme->SizesList, pwszSizeBuff); 833 else 834 pSelectedTheme->Size = pFoundTheme->SizesList; 835 836 return TRUE; 837 } 838 839 /* 840 * GetActiveTheme: Gets the active theme and populates pSelectedTheme 841 * with entries from the list of loaded themes. 842 */ 843 BOOL 844 GetActiveTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme) 845 { 846 WCHAR szThemeFileName[MAX_PATH]; 847 WCHAR szColorBuff[MAX_PATH]; 848 WCHAR szSizeBuff[MAX_PATH]; 849 HRESULT hret; 850 851 /* Retrieve the name of the current theme */ 852 hret = GetCurrentThemeName(szThemeFileName, 853 MAX_PATH, 854 szColorBuff, 855 MAX_PATH, 856 szSizeBuff, 857 MAX_PATH); 858 if (FAILED(hret)) 859 return FALSE; 860 861 return FindOrAppendTheme(pThemeList, szThemeFileName, szColorBuff, szSizeBuff, pSelectedTheme); 862 } 863 864 /* 865 * GetActiveTheme: Gets the active classic theme and populates pSelectedTheme 866 * with entries from the list of loaded themes 867 */ 868 BOOL 869 GetActiveClassicTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme) 870 { 871 INT Result; 872 WCHAR szSelectedClassicScheme[5], szSelectedClassicSize[5]; 873 HKEY hkNewSchemes; 874 DWORD dwType, dwDisplayNameLength; 875 PTHEME_STYLE pCurrentStyle, pCurrentSize; 876 877 ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION)); 878 879 /* Assume failure */ 880 szSelectedClassicScheme[0] = 0; 881 szSelectedClassicSize[0] = 0; 882 883 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes); 884 if (Result != ERROR_SUCCESS) return FALSE; 885 886 dwType = REG_SZ; 887 dwDisplayNameLength = sizeof(szSelectedClassicScheme); 888 Result = RegQueryValueEx(hkNewSchemes, L"SelectedStyle", NULL, &dwType, 889 (LPBYTE)&szSelectedClassicScheme, &dwDisplayNameLength); 890 if (Result == ERROR_SUCCESS) 891 { 892 dwType = REG_SZ; 893 dwDisplayNameLength = sizeof(szSelectedClassicSize); 894 Result = SHGetValue(hkNewSchemes, szSelectedClassicScheme, L"SelectedSize", 895 &dwType, szSelectedClassicSize, &dwDisplayNameLength); 896 } 897 898 RegCloseKey(hkNewSchemes); 899 900 pCurrentStyle = FindStyle(pThemeList->ColoursList, szSelectedClassicScheme); 901 pCurrentSize = FindStyle(pCurrentStyle->ChildStyle, szSelectedClassicSize); 902 903 pSelectedTheme->Theme = pThemeList; 904 pSelectedTheme->Color = pCurrentStyle; 905 pSelectedTheme->Size = pCurrentSize; 906 907 return TRUE; 908 } 909 910 BOOL 911 ActivateTheme(IN PTHEME_SELECTION pSelectedTheme) 912 { 913 HTHEMEFILE hThemeFile = 0; 914 HRESULT hret; 915 916 if (pSelectedTheme->ThemeActive) 917 { 918 hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName, 919 pSelectedTheme->Color->StyleName, 920 pSelectedTheme->Size->StyleName, 921 &hThemeFile, 922 0); 923 924 if (!SUCCEEDED(hret)) return FALSE; 925 } 926 927 hret = ApplyTheme(hThemeFile, 0, 0); 928 929 if (pSelectedTheme->ThemeActive) 930 { 931 CloseThemeFile(hThemeFile); 932 } 933 934 return SUCCEEDED(hret); 935 } 936 937 BOOL 938 LoadSchemeFromTheme(OUT PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme) 939 { 940 HTHEMEFILE hThemeFile = 0; 941 HRESULT hret; 942 HTHEME hTheme; 943 int i; 944 945 hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName, 946 pSelectedTheme->Color->StyleName, 947 pSelectedTheme->Size->StyleName, 948 &hThemeFile, 949 0); 950 951 if (!SUCCEEDED(hret)) return FALSE; 952 953 hTheme = OpenThemeDataFromFile(hThemeFile, hCPLWindow, L"WINDOW", 0); 954 if (hTheme == NULL) return FALSE; 955 956 /* Load colors */ 957 for (i = 0; i < NUM_COLORS; i++) 958 { 959 scheme->crColor[i] = GetThemeSysColor(hTheme,i); 960 } 961 962 /* Load sizes */ 963 /* I wonder why GetThemeSysInt doesn't work here */ 964 scheme->ncMetrics.iBorderWidth = GetThemeSysSize(hTheme, SM_CXFRAME); 965 scheme->ncMetrics.iScrollWidth = GetThemeSysSize(hTheme, SM_CXVSCROLL); 966 scheme->ncMetrics.iScrollHeight = GetThemeSysSize(hTheme, SM_CYHSCROLL); 967 scheme->ncMetrics.iCaptionWidth = GetThemeSysSize(hTheme, SM_CXSIZE); 968 scheme->ncMetrics.iCaptionHeight = GetThemeSysSize(hTheme, SM_CYSIZE); 969 scheme->ncMetrics.iSmCaptionWidth = GetThemeSysSize(hTheme, SM_CXSMSIZE); 970 scheme->ncMetrics.iSmCaptionHeight = GetThemeSysSize(hTheme, SM_CYSMSIZE); 971 scheme->ncMetrics.iMenuWidth = GetThemeSysSize(hTheme, SM_CXMENUSIZE); 972 scheme->ncMetrics.iMenuHeight = GetThemeSysSize(hTheme, SM_CYMENUSIZE); 973 974 /* Load fonts */ 975 GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &scheme->ncMetrics.lfCaptionFont); 976 GetThemeSysFont(hTheme, TMT_SMALLCAPTIONFONT, &scheme->ncMetrics.lfSmCaptionFont); 977 GetThemeSysFont(hTheme, TMT_MENUFONT, &scheme->ncMetrics.lfMenuFont ); 978 GetThemeSysFont(hTheme, TMT_STATUSFONT, &scheme->ncMetrics.lfStatusFont); 979 GetThemeSysFont(hTheme, TMT_MSGBOXFONT, &scheme->ncMetrics.lfMessageFont); 980 GetThemeSysFont(hTheme, TMT_ICONTITLEFONT, &scheme->icMetrics.lfFont); 981 982 scheme->bFlatMenus = GetThemeSysBool(hTheme, TMT_FLATMENUS); 983 984 CloseThemeData(hTheme); 985 986 return TRUE; 987 } 988 989 BOOL 990 DrawThemePreview(IN HDC hdcMem, IN PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme, IN PRECT prcWindow) 991 { 992 HBRUSH hbrBack; 993 HRESULT hres; 994 995 hbrBack = CreateSolidBrush(scheme->crColor[COLOR_DESKTOP]); 996 997 FillRect(hdcMem, prcWindow, hbrBack); 998 DeleteObject(hbrBack); 999 1000 InflateRect(prcWindow, -10, -10); 1001 1002 hres = DrawNCPreview(hdcMem, 1003 DNCP_DRAW_ALL, 1004 prcWindow, 1005 pSelectedTheme->Theme->ThemeFileName, 1006 pSelectedTheme->Color->StyleName, 1007 pSelectedTheme->Size->StyleName, 1008 &scheme->ncMetrics, 1009 scheme->crColor); 1010 1011 return SUCCEEDED(hres); 1012 } 1013 1014 BOOL ActivateThemeFile(LPCWSTR pwszFile) 1015 { 1016 PTHEME pThemes; 1017 THEME_SELECTION selection; 1018 COLOR_SCHEME scheme; 1019 BOOL ret = FALSE; 1020 1021 pThemes = LoadThemes(); 1022 if (!pThemes) 1023 return FALSE; 1024 1025 LoadCurrentScheme(&scheme); 1026 1027 if (pwszFile) 1028 { 1029 ret = FindOrAppendTheme(pThemes, pwszFile, NULL, NULL, &selection); 1030 if (!ret) 1031 goto cleanup; 1032 1033 ret = LoadSchemeFromTheme(&scheme, &selection); 1034 if (!ret) 1035 goto cleanup; 1036 } 1037 else 1038 { 1039 ret = GetActiveClassicTheme(pThemes, &selection); 1040 if (!ret) 1041 goto cleanup; 1042 1043 ret = LoadSchemeFromReg(&scheme, &selection); 1044 if (!ret) 1045 goto cleanup; 1046 } 1047 1048 ret = ActivateTheme(&selection); 1049 if (!ret) 1050 goto cleanup; 1051 1052 ApplyScheme(&scheme, &selection); 1053 1054 ret = TRUE; 1055 1056 cleanup: 1057 CleanupThemes(pThemes); 1058 1059 return ret; 1060 } 1061 1062 /* TODO: */ 1063 INT_PTR CALLBACK 1064 ThemesPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 1065 { 1066 LPNMHDR lpnm; 1067 1068 switch (uMsg) 1069 { 1070 case WM_INITDIALOG: 1071 break; 1072 1073 case WM_COMMAND: 1074 break; 1075 1076 case WM_NOTIFY: 1077 lpnm = (LPNMHDR)lParam; 1078 switch (lpnm->code) 1079 { 1080 case PSN_APPLY: 1081 SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)TEXT("")); 1082 return TRUE; 1083 } 1084 break; 1085 1086 case WM_DESTROY: 1087 break; 1088 } 1089 1090 return FALSE; 1091 } 1092