/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Display Control Panel * FILE: dll/cpl/desk/theme.c * PURPOSE: Handling themes and visual effects * * PROGRAMMERS: Katayama Hirofumi MZ * Ismael Ferreras Morezuelas (swyterzone+reactos@gmail.com) */ #include "desk.h" #include #include #include #include static const WCHAR g_CPColors[] = L"Control Panel\\Colors"; static const WCHAR g_CPANewSchemes[] = L"Control Panel\\Appearance\\New Schemes"; static const WCHAR g_CPMetrics[] = L"Control Panel\\Desktop\\WindowMetrics"; static const WCHAR g_SelectedStyle[] = L"SelectedStyle"; /******************************************************************************/ /* This is the list of names for the colors stored in the registry */ static const WCHAR *g_RegColorNames[NUM_COLORS] = { L"Scrollbar", /* 00 = COLOR_SCROLLBAR */ L"Background", /* 01 = COLOR_DESKTOP */ L"ActiveTitle", /* 02 = COLOR_ACTIVECAPTION */ L"InactiveTitle", /* 03 = COLOR_INACTIVECAPTION */ L"Menu", /* 04 = COLOR_MENU */ L"Window", /* 05 = COLOR_WINDOW */ L"WindowFrame", /* 06 = COLOR_WINDOWFRAME */ L"MenuText", /* 07 = COLOR_MENUTEXT */ L"WindowText", /* 08 = COLOR_WINDOWTEXT */ L"TitleText", /* 09 = COLOR_CAPTIONTEXT */ L"ActiveBorder", /* 10 = COLOR_ACTIVEBORDER */ L"InactiveBorder", /* 11 = COLOR_INACTIVEBORDER */ L"AppWorkSpace", /* 12 = COLOR_APPWORKSPACE */ L"Hilight", /* 13 = COLOR_HIGHLIGHT */ L"HilightText", /* 14 = COLOR_HIGHLIGHTTEXT */ L"ButtonFace", /* 15 = COLOR_BTNFACE */ L"ButtonShadow", /* 16 = COLOR_BTNSHADOW */ L"GrayText", /* 17 = COLOR_GRAYTEXT */ L"ButtonText", /* 18 = COLOR_BTNTEXT */ L"InactiveTitleText", /* 19 = COLOR_INACTIVECAPTIONTEXT */ L"ButtonHilight", /* 20 = COLOR_BTNHIGHLIGHT */ L"ButtonDkShadow", /* 21 = COLOR_3DDKSHADOW */ L"ButtonLight", /* 22 = COLOR_3DLIGHT */ L"InfoText", /* 23 = COLOR_INFOTEXT */ L"InfoWindow", /* 24 = COLOR_INFOBK */ L"ButtonAlternateFace", /* 25 = COLOR_ALTERNATEBTNFACE */ L"HotTrackingColor", /* 26 = COLOR_HOTLIGHT */ L"GradientActiveTitle", /* 27 = COLOR_GRADIENTACTIVECAPTION */ L"GradientInactiveTitle", /* 28 = COLOR_GRADIENTINACTIVECAPTION */ L"MenuHilight", /* 29 = COLOR_MENUHILIGHT */ L"MenuBar", /* 30 = COLOR_MENUBAR */ }; /******************************************************************************/ VOID SchemeSetMetric(IN COLOR_SCHEME *scheme, int id, int value) { switch (id) { case SIZE_BORDER_WIDTH: scheme->ncMetrics.iBorderWidth = value; break; case SIZE_SCROLL_WIDTH: scheme->ncMetrics.iScrollWidth = value; break; case SIZE_SCROLL_HEIGHT: scheme->ncMetrics.iScrollHeight = value; break; case SIZE_CAPTION_WIDTH: scheme->ncMetrics.iCaptionWidth = value; break; case SIZE_CAPTION_HEIGHT: scheme->ncMetrics.iCaptionHeight = value; break; case SIZE_SM_CAPTION_WIDTH: scheme->ncMetrics.iSmCaptionWidth = value; break; case SIZE_SM_CAPTION_HEIGHT: scheme->ncMetrics.iSmCaptionHeight = value; break; case SIZE_MENU_WIDTH: scheme->ncMetrics.iMenuWidth = value; break; case SIZE_MENU_HEIGHT: scheme->ncMetrics.iMenuHeight = value; break; case SIZE_ICON: scheme->iIconSize = value; break; case SIZE_ICON_SPACE_X: scheme->icMetrics.iHorzSpacing = value; break; case SIZE_ICON_SPACE_Y: scheme->icMetrics.iVertSpacing = value; break; } } int SchemeGetMetric(IN COLOR_SCHEME *scheme, int id) { switch (id) { case SIZE_BORDER_WIDTH: return scheme->ncMetrics.iBorderWidth; case SIZE_SCROLL_WIDTH: return scheme->ncMetrics.iScrollWidth; case SIZE_SCROLL_HEIGHT: return scheme->ncMetrics.iScrollHeight; case SIZE_CAPTION_WIDTH: return scheme->ncMetrics.iCaptionWidth; case SIZE_CAPTION_HEIGHT: return scheme->ncMetrics.iCaptionHeight; case SIZE_SM_CAPTION_WIDTH: return scheme->ncMetrics.iSmCaptionWidth; case SIZE_SM_CAPTION_HEIGHT: return scheme->ncMetrics.iSmCaptionHeight; case SIZE_MENU_WIDTH: return scheme->ncMetrics.iMenuWidth; case SIZE_MENU_HEIGHT: return scheme->ncMetrics.iMenuHeight; case SIZE_ICON: return scheme->iIconSize; case SIZE_ICON_SPACE_X: return scheme->icMetrics.iHorzSpacing; case SIZE_ICON_SPACE_Y: return scheme->icMetrics.iVertSpacing; } return 0; } PLOGFONTW SchemeGetFont(IN COLOR_SCHEME *scheme, int id) { switch (id) { case FONT_CAPTION: return &scheme->ncMetrics.lfCaptionFont; case FONT_SMCAPTION: return &scheme->ncMetrics.lfSmCaptionFont; case FONT_MENU: return &scheme->ncMetrics.lfMenuFont; case FONT_STATUS: return &scheme->ncMetrics.lfStatusFont; case FONT_MESSAGE: return &scheme->ncMetrics.lfMessageFont; case FONT_ICON: return &scheme->icMetrics.lfFont; } return NULL; } /* * LoadCurrentScheme: Populates the passed scheme based on the current system settings */ BOOL LoadCurrentScheme(OUT COLOR_SCHEME *scheme) { INT i, Result; HKEY hKey; BOOL ret; #if (WINVER >= 0x0600) OSVERSIONINFO osvi; #endif /* Load colors */ for (i = 0; i < NUM_COLORS; i++) { scheme->crColor[i] = (COLORREF)GetSysColor(i); } /* Load non client metrics */ scheme->ncMetrics.cbSize = sizeof(NONCLIENTMETRICSW); #if (WINVER >= 0x0600) /* Size of NONCLIENTMETRICSA/W depends on current version of the OS. * see: * https://msdn.microsoft.com/en-us/library/windows/desktop/ff729175%28v=vs.85%29.aspx */ if (GetVersionEx(&osvi)) { /* Windows XP and earlier */ if (osvi.dwMajorVersion <= 5) scheme->ncMetrics.cbSize -= sizeof(scheme->ncMetrics.iPaddedBorderWidth); } #endif ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &scheme->ncMetrics, 0); if (!ret) return FALSE; /* Load icon metrics */ scheme->icMetrics.cbSize = sizeof(ICONMETRICSW); ret = SystemParametersInfoW(SPI_GETICONMETRICS, sizeof(ICONMETRICSW), &scheme->icMetrics, 0); if (!ret) return FALSE; /* Load flat menu style */ ret = SystemParametersInfoW(SPI_GETFLATMENU, 0, &scheme->bFlatMenus, 0); if (!ret) return FALSE; /* Effects */ /* Use the following transition effect for menus and tooltips */ ret = SystemParametersInfoW(SPI_GETMENUANIMATION, 0, &scheme->Effects.bMenuAnimation, 0); if (!ret) return FALSE; ret = SystemParametersInfoW(SPI_GETMENUFADE, 0, &scheme->Effects.bMenuFade, 0); if (!ret) return FALSE; /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings * Just keep them in sync for now: */ scheme->Effects.bTooltipAnimation = scheme->Effects.bMenuAnimation; scheme->Effects.bTooltipFade = scheme->Effects.bMenuFade; /* Use the following transition effect for menus and tooltips */ ret = SystemParametersInfoW(SPI_GETFONTSMOOTHING, 0, &scheme->Effects.bFontSmoothing, 0); if (!ret) return FALSE; ret = SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &scheme->Effects.uiFontSmoothingType, 0); if (!ret) return FALSE; /* Show shadows under menus */ ret = SystemParametersInfoW(SPI_GETDROPSHADOW, 0, &scheme->Effects.bDropShadow, 0); if (!ret) return FALSE; /* Show content of windows during dragging */ ret = SystemParametersInfoW(SPI_GETDRAGFULLWINDOWS, 0, &scheme->Effects.bDragFullWindows, 0); if (!ret) return FALSE; /* Hide underlined letters for keyboard navigation until the Alt key is pressed */ ret = SystemParametersInfoW(SPI_GETKEYBOARDCUES, 0, &scheme->Effects.bKeyboardCues, 0); if (!ret) return FALSE; /* Read the icon size from registry */ Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPMetrics, &hKey); if(Result == ERROR_SUCCESS) { scheme->iIconSize = SHRegGetIntW(hKey, L"Shell Icon Size", 32); RegCloseKey(hKey); } return TRUE; } /* * LoadSchemeFromReg: Populates the passed scheme with values retrieved from registry */ BOOL LoadSchemeFromReg(OUT COLOR_SCHEME *scheme, IN PTHEME_SELECTION pSelectedTheme) { INT i; WCHAR strValueName[10], strSchemeKey[MAX_PATH]; HKEY hkScheme = NULL; DWORD dwType, dwLength; UINT64 iSize; BOOL Ret = TRUE; LONG result; wsprintf(strSchemeKey, L"%s\\%s\\Sizes\\%s", g_CPANewSchemes, pSelectedTheme->Color->StyleName, pSelectedTheme->Size->StyleName); result = RegOpenKeyW(HKEY_CURRENT_USER, strSchemeKey, &hkScheme); if (result != ERROR_SUCCESS) return FALSE; scheme->bFlatMenus = SHRegGetIntW(hkScheme, L"FlatMenus", 0); for (i = 0; i < NUM_COLORS; i++) { wsprintf(strValueName, L"Color #%d", i); dwLength = sizeof(COLORREF); result = RegQueryValueExW(hkScheme, strValueName, NULL, &dwType, (LPBYTE)&scheme->crColor[i], &dwLength); if (result != ERROR_SUCCESS || dwType != REG_DWORD) { /* Failed to read registry value, initialize with current setting for now */ scheme->crColor[i] = GetSysColor(i); } } for (i = 0; i < NUM_FONTS; i++) { PLOGFONTW lpfFont = SchemeGetFont(scheme, i); wsprintf(strValueName, L"Font #%d", i); dwLength = sizeof(LOGFONT); result = RegQueryValueExW(hkScheme, strValueName, NULL, &dwType, (LPBYTE)lpfFont, &dwLength); if (result != ERROR_SUCCESS || dwType != REG_BINARY || dwLength != sizeof(LOGFONT)) { /* Failed to read registry value */ Ret = FALSE; } } for (i = 0; i < NUM_SIZES; i++) { wsprintf(strValueName, L"Size #%d", i); dwLength = sizeof(UINT64); result = RegQueryValueExW(hkScheme, strValueName, NULL, &dwType, (LPBYTE)&iSize, &dwLength); if (result != ERROR_SUCCESS || dwType != REG_QWORD || dwLength != sizeof(UINT64)) { /* Failed to read registry value, initialize with current setting for now */ } else { SchemeSetMetric(scheme, i, (int)iSize); } } RegCloseKey(hkScheme); return Ret; } /* * ApplyScheme: Applies the selected scheme and stores its id in the registry if needed */ VOID ApplyScheme(IN COLOR_SCHEME *scheme, IN PTHEME_SELECTION pSelectedTheme) { INT i, Result; HKEY hKey; WCHAR clText[16], *StyleName; INT ColorList[NUM_COLORS]; /* Apply system colors */ for (i = 0; i < NUM_COLORS; i++) ColorList[i] = i; SetSysColors(NUM_COLORS, ColorList, scheme->crColor); /* Save colors to registry */ Result = RegCreateKeyW(HKEY_CURRENT_USER, g_CPColors, &hKey); if (Result == ERROR_SUCCESS) { for (i = 0; i < NUM_COLORS; i++) { wsprintf(clText, L"%d %d %d", GetRValue(scheme->crColor[i]), GetGValue(scheme->crColor[i]), GetBValue(scheme->crColor[i])); RegSetValueExW(hKey, g_RegColorNames[i], 0, REG_SZ, (BYTE *)clText, (lstrlen(clText) + 1) * sizeof(WCHAR)); } RegCloseKey(hKey); } /* Apply non client metrics */ SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &scheme->ncMetrics, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); /* Apply icon metrics */ SystemParametersInfoW(SPI_SETICONMETRICS, sizeof(ICONMETRICS), &scheme->icMetrics, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); /* Effects, save only when needed: */ /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings * Just keep them in sync for now. */ #define SYS_CONFIG(__uiAction, __uiParam, __pvParam) \ SystemParametersInfoW(__uiAction, __uiParam, __pvParam, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE) scheme->Effects.bTooltipAnimation = scheme->Effects.bMenuAnimation; scheme->Effects.bTooltipFade = scheme->Effects.bMenuFade; /* Use the following transition effect for menus and tooltips */ SYS_CONFIG(SPI_SETMENUANIMATION, 0, IntToPtr(scheme->Effects.bMenuAnimation)); SYS_CONFIG(SPI_SETMENUFADE, 0, IntToPtr(scheme->Effects.bMenuFade)); /* Use the following method to smooth edges of screen fonts */ SYS_CONFIG(SPI_SETFONTSMOOTHING, scheme->Effects.bFontSmoothing, 0); SYS_CONFIG(SPI_SETFONTSMOOTHINGTYPE, 0, IntToPtr(scheme->Effects.uiFontSmoothingType)); /* * Refresh and redraw all the windows, otherwise the font smoothing changes * only appear after any future partial region invalidation. * Not everyone listens for this WM_SETTINGCHANGE, including the shell and most third party programs. */ InvalidateRect(NULL, NULL, TRUE); /* Use large icons */ //SYS_CONFIG(SPI_GETDRAGFULLWINDOWS, (PVOID) g->SchemeAdv.Effects.bMenuFade); /* Show shadows under menus */ SYS_CONFIG(SPI_SETDROPSHADOW, 0, IntToPtr(scheme->Effects.bDropShadow)); /* Show window contents while dragging */ SYS_CONFIG(SPI_SETDRAGFULLWINDOWS, scheme->Effects.bDragFullWindows, 0); /* Hide underlined letters for keyboard navigation until I press the Alt key */ SYS_CONFIG(SPI_SETKEYBOARDCUES, 0, IntToPtr(scheme->Effects.bKeyboardCues)); SYS_CONFIG(SPI_SETFLATMENU, 0, IntToPtr(scheme->bFlatMenus)); // SYS_CONFIG(SPI_SETACTIVEWINDOWTRACKING, 0, IntToPtr(scheme->Effects.bActiveWindowTracking)); // SYS_CONFIG(SPI_SETCOMBOBOXANIMATION, 0, IntToPtr(scheme->Effects.bComboBoxAnimation)); // SYS_CONFIG(SPI_SETLISTBOXSMOOTHSCROLLING, 0, IntToPtr(scheme->Effects.bListBoxSmoothScrolling)); // SYS_CONFIG(SPI_SETGRADIENTCAPTIONS, 0, IntToPtr(scheme->Effects.bGradientCaptions)); // SYS_CONFIG(SPI_SETACTIVEWNDTRKZORDER, 0, IntToPtr(scheme->Effects.bActiveWndTrkZorder)); // SYS_CONFIG(SPI_SETHOTTRACKING, 0, IntToPtr(scheme->Effects.bHotTracking)); // SYS_CONFIG(SPI_SETSELECTIONFADE, 0, IntToPtr(scheme->Effects.bSelectionFade)); SYS_CONFIG(SPI_SETTOOLTIPANIMATION, 0, IntToPtr(scheme->Effects.bTooltipAnimation)); SYS_CONFIG(SPI_SETTOOLTIPFADE, 0, IntToPtr(scheme->Effects.bTooltipFade)); // SYS_CONFIG(SPI_SETCURSORSHADOW, 0, IntToPtr(scheme->Effects.bCursorShadow)); // SYS_CONFIG(SPI_SETUIEFFECTS, 0, IntToPtr(scheme->Effects.bUiEffects)); #undef SYS_CONFIG /* Save SchemeId in the registry */ if (pSelectedTheme->Theme != NULL && pSelectedTheme->ThemeActive == FALSE) { StyleName = pSelectedTheme->Color->StyleName; SHSetValueW(HKEY_CURRENT_USER, g_CPANewSchemes, g_SelectedStyle, REG_SZ, StyleName, (lstrlenW(StyleName) + 1) * sizeof(WCHAR)); } } static THEME* CreateTheme(LPCWSTR pszName, LPCWSTR pszDisplayName) { PTHEME pTheme; pTheme = (PTHEME) malloc(sizeof(THEME)); if (pTheme == NULL) return NULL; pTheme->DisplayName = _wcsdup(pszDisplayName); if (pTheme->DisplayName == NULL) { free(pTheme); return NULL; } pTheme->ColoursList = NULL; pTheme->NextTheme = NULL; pTheme->SizesList = NULL; if (pszName == NULL) { pTheme->ThemeFileName = NULL; return pTheme; } pTheme->ThemeFileName = _wcsdup(pszName); if (pTheme->ThemeFileName == NULL) { free(pTheme->DisplayName); free(pTheme); return NULL; } return pTheme; } static PTHEME_STYLE CreateStyle(LPCWSTR pszName, LPCWSTR pszDisplayName) { PTHEME_STYLE pStyle; pStyle = (PTHEME_STYLE) malloc(sizeof(THEME_STYLE)); if (pStyle == NULL) return NULL; pStyle->StyleName = _wcsdup(pszName); if (pStyle->StyleName == NULL) { free(pStyle); return NULL; } pStyle->DisplayName = _wcsdup(pszDisplayName); if (pStyle->DisplayName == NULL) { free(pStyle->StyleName); free(pStyle); return NULL; } pStyle->ChildStyle = NULL; pStyle->NextStyle = NULL; return pStyle; } static void CleanupStyles(IN PTHEME_STYLE pStylesList) { PTHEME_STYLE pStyle, pStyleOld; pStyle = pStylesList; while (pStyle) { if (pStyle->ChildStyle) CleanupStyles(pStyle->ChildStyle); if (pStyle->DisplayName) free(pStyle->DisplayName); if (pStyle->StyleName) free(pStyle->StyleName); pStyleOld = pStyle; pStyle = pStyle->NextStyle; free(pStyleOld); } } void CleanupThemes(IN PTHEME pThemeList) { PTHEME pTheme, pThemeOld; pTheme = pThemeList; while (pTheme) { CleanupStyles(pTheme->ColoursList); if (pTheme->SizesList) CleanupStyles(pTheme->SizesList); if (pTheme->DisplayName) free(pTheme->DisplayName); if (pTheme->ThemeFileName) free(pTheme->ThemeFileName); pThemeOld = pTheme; pTheme = pTheme->NextTheme; free(pThemeOld); } } static PTHEME_STYLE FindStyle(IN PTHEME_STYLE pStylesList, IN PCWSTR StyleName) { PTHEME_STYLE pStyle; for (pStyle = pStylesList; pStyle; pStyle = pStyle->NextStyle) { if (_wcsicmp(pStyle->StyleName, StyleName) == 0) { return pStyle; } } /* If we can't find the style requested, return the first one */ return pStylesList; } /* * LoadSchemeSizes: Returns a list of sizes from the registry key of a scheme */ static PTHEME_STYLE LoadSchemeSizes(IN HKEY hkScheme) { HKEY hkSizes, hkSize; INT Result; INT iStyle; WCHAR wstrSizeName[5], wstrDisplayName[50]; THEME_STYLE *List = NULL, *pCurrentStyle; Result = RegOpenKeyW(hkScheme, L"Sizes", &hkSizes); if (Result != ERROR_SUCCESS) return NULL; iStyle = 0; while ((RegEnumKeyW(hkSizes, iStyle, wstrSizeName, 5) == ERROR_SUCCESS)) { iStyle++; Result = RegOpenKeyW(hkSizes, wstrSizeName, &hkSize); if (Result != ERROR_SUCCESS) continue; Result = RegLoadMUIStringW(hkSize, L"DisplayName", wstrDisplayName, sizeof(wstrDisplayName), NULL, 0, NULL); if (Result != ERROR_SUCCESS) { Result = RegLoadMUIStringW(hkSize, L"LegacyName", wstrDisplayName, sizeof(wstrDisplayName), NULL, 0, NULL); } if (Result == ERROR_SUCCESS) pCurrentStyle = CreateStyle(wstrSizeName, wstrDisplayName); else pCurrentStyle = CreateStyle(wstrSizeName, wstrSizeName); if (pCurrentStyle != NULL) { pCurrentStyle->NextStyle = List; List = pCurrentStyle; } RegCloseKey(hkSize); } RegCloseKey(hkSizes); return List; } /* * LoadClassicColorSchemes: Returns a list of classic theme colours from the registry key of a scheme */ static THEME_STYLE* LoadClassicColorSchemes(VOID) { INT Result; HKEY hkNewSchemes, hkScheme; INT iStyle; WCHAR wstrStyleName[5], wstrDisplayName[50]; THEME_STYLE *List = NULL, *pCurrentStyle; Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes); if (Result != ERROR_SUCCESS) return NULL; iStyle = 0; while ((RegEnumKeyW(hkNewSchemes, iStyle, wstrStyleName, 5) == ERROR_SUCCESS)) { iStyle++; Result = RegOpenKeyW(hkNewSchemes, wstrStyleName, &hkScheme); if (Result != ERROR_SUCCESS) continue; Result = RegLoadMUIStringW(hkScheme, L"DisplayName", wstrDisplayName, sizeof(wstrDisplayName), NULL, 0, NULL); if (Result != ERROR_SUCCESS) { Result = RegLoadMUIStringW(hkScheme, L"LegacyName", wstrDisplayName, sizeof(wstrDisplayName), NULL, 0, NULL); } if (Result == ERROR_SUCCESS) pCurrentStyle = CreateStyle(wstrStyleName, wstrDisplayName); else pCurrentStyle = CreateStyle(wstrStyleName, wstrStyleName); if (pCurrentStyle != NULL) { pCurrentStyle->NextStyle = List; pCurrentStyle->ChildStyle = LoadSchemeSizes(hkScheme); if(pCurrentStyle->ChildStyle == NULL) CleanupStyles(pCurrentStyle); else List = pCurrentStyle; } RegCloseKey(hkScheme); } RegCloseKey(hkNewSchemes); return List; } typedef HRESULT (WINAPI *ENUMTHEMESTYLE) (LPCWSTR, LPWSTR, DWORD, PTHEMENAMES); static THEME_STYLE* EnumThemeStyles(IN LPCWSTR pszThemeFileName, IN ENUMTHEMESTYLE pfnEnumTheme) { DWORD index = 0; THEMENAMES names; THEME_STYLE *List = NULL, **ppPrevStyle, *pCurrentStyle; ppPrevStyle = &List; while (SUCCEEDED(pfnEnumTheme (pszThemeFileName, NULL, index++, &names))) { pCurrentStyle = CreateStyle(names.szName, names.szDisplayName); if(pCurrentStyle == NULL) break; *ppPrevStyle = pCurrentStyle; ppPrevStyle = &pCurrentStyle->NextStyle; } return List; } PTHEME LoadTheme(IN LPCWSTR pszThemeFileName,IN LPCWSTR pszThemeName) { PTHEME pTheme = CreateTheme(pszThemeFileName, pszThemeName); if (pTheme == NULL) return NULL; pTheme->SizesList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeSizes); pTheme->ColoursList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeColors); if(pTheme->SizesList == NULL || pTheme->ColoursList == NULL) { CleanupThemes(pTheme); return NULL; } return pTheme; } BOOL CALLBACK EnumThemeProc(IN LPVOID lpReserved, IN LPCWSTR pszThemeFileName, IN LPCWSTR pszThemeName, IN LPCWSTR pszToolTip, IN LPVOID lpReserved2, IN OUT LPVOID lpData) { PTHEME *List, pTheme; List = (PTHEME*)lpData; pTheme = LoadTheme(pszThemeFileName, pszThemeName); if (pTheme == NULL) return FALSE; pTheme->NextTheme = *List; *List = pTheme; return TRUE; } /* * LoadThemes: Returns a list that contains tha classic theme and * the visual styles of the system */ PTHEME LoadThemes(VOID) { HRESULT hret; PTHEME pClassicTheme; WCHAR strClassicTheme[40]; WCHAR szThemesPath[MAX_PATH], *pszClassicTheme; int res; /* Insert the classic theme */ res = LoadString(hApplet, IDS_CLASSIC_THEME, strClassicTheme, 40); pszClassicTheme = (res > 0 ? strClassicTheme : L"Classic Theme"); pClassicTheme = CreateTheme(NULL, pszClassicTheme); if (pClassicTheme == NULL) return NULL; pClassicTheme->ColoursList = LoadClassicColorSchemes(); /* Get path to themes folder */ ZeroMemory(szThemesPath, sizeof(szThemesPath)); hret = SHGetFolderPathW (NULL, CSIDL_RESOURCES, NULL, SHGFP_TYPE_DEFAULT, szThemesPath); if (FAILED(hret)) return pClassicTheme; lstrcatW (szThemesPath, L"\\Themes"); /* Enumerate themes */ hret = EnumThemes( szThemesPath, EnumThemeProc, &pClassicTheme->NextTheme); if (FAILED(hret)) { pClassicTheme->NextTheme = NULL; if (pClassicTheme->ColoursList == NULL) { free(pClassicTheme->DisplayName); free(pClassicTheme); return NULL; } } return pClassicTheme; } /* * FindSelectedTheme: Finds the specified theme in the list of themes * or loads it if it was not loaded already. */ BOOL FindOrAppendTheme(IN PTHEME pThemeList, IN LPCWSTR pwszThemeFileName, IN LPCWSTR pwszColorBuff, IN LPCWSTR pwszSizeBuff, OUT PTHEME_SELECTION pSelectedTheme) { PTHEME pTheme; PTHEME pFoundTheme = NULL; ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION)); for (pTheme = pThemeList; pTheme; pTheme = pTheme->NextTheme) { if (pTheme->ThemeFileName && _wcsicmp(pTheme->ThemeFileName, pwszThemeFileName) == 0) { pFoundTheme = pTheme; break; } if (pTheme->NextTheme == NULL) break; } if (!pFoundTheme) { pFoundTheme = LoadTheme(pwszThemeFileName, pwszThemeFileName); if (!pFoundTheme) return FALSE; pTheme->NextTheme = pFoundTheme; } pSelectedTheme->ThemeActive = TRUE; pSelectedTheme->Theme = pFoundTheme; if (pwszColorBuff) pSelectedTheme->Color = FindStyle(pFoundTheme->ColoursList, pwszColorBuff); else pSelectedTheme->Color = pFoundTheme->ColoursList; if (pwszSizeBuff) pSelectedTheme->Size = FindStyle(pFoundTheme->SizesList, pwszSizeBuff); else pSelectedTheme->Size = pFoundTheme->SizesList; return TRUE; } /* * GetActiveTheme: Gets the active theme and populates pSelectedTheme * with entries from the list of loaded themes. */ BOOL GetActiveTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme) { WCHAR szThemeFileName[MAX_PATH]; WCHAR szColorBuff[MAX_PATH]; WCHAR szSizeBuff[MAX_PATH]; HRESULT hret; /* Retrieve the name of the current theme */ hret = GetCurrentThemeName(szThemeFileName, MAX_PATH, szColorBuff, MAX_PATH, szSizeBuff, MAX_PATH); if (FAILED(hret)) return FALSE; return FindOrAppendTheme(pThemeList, szThemeFileName, szColorBuff, szSizeBuff, pSelectedTheme); } /* * GetActiveTheme: Gets the active classic theme and populates pSelectedTheme * with entries from the list of loaded themes */ BOOL GetActiveClassicTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme) { INT Result; WCHAR szSelectedClassicScheme[5], szSelectedClassicSize[5]; HKEY hkNewSchemes; DWORD dwType, dwDisplayNameLength; PTHEME_STYLE pCurrentStyle, pCurrentSize; ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION)); /* Assume failure */ szSelectedClassicScheme[0] = 0; szSelectedClassicSize[0] = 0; Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes); if (Result != ERROR_SUCCESS) return FALSE; dwType = REG_SZ; dwDisplayNameLength = sizeof(szSelectedClassicScheme); Result = RegQueryValueEx(hkNewSchemes, L"SelectedStyle", NULL, &dwType, (LPBYTE)&szSelectedClassicScheme, &dwDisplayNameLength); if (Result == ERROR_SUCCESS) { dwType = REG_SZ; dwDisplayNameLength = sizeof(szSelectedClassicSize); Result = SHGetValue(hkNewSchemes, szSelectedClassicScheme, L"SelectedSize", &dwType, szSelectedClassicSize, &dwDisplayNameLength); } RegCloseKey(hkNewSchemes); pCurrentStyle = FindStyle(pThemeList->ColoursList, szSelectedClassicScheme); pCurrentSize = FindStyle(pCurrentStyle->ChildStyle, szSelectedClassicSize); pSelectedTheme->Theme = pThemeList; pSelectedTheme->Color = pCurrentStyle; pSelectedTheme->Size = pCurrentSize; return TRUE; } BOOL ActivateTheme(IN PTHEME_SELECTION pSelectedTheme) { HTHEMEFILE hThemeFile = 0; HRESULT hret; if (pSelectedTheme->ThemeActive) { hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName, pSelectedTheme->Color->StyleName, pSelectedTheme->Size->StyleName, &hThemeFile, 0); if (!SUCCEEDED(hret)) return FALSE; } hret = ApplyTheme(hThemeFile, 0, 0); if (pSelectedTheme->ThemeActive) { CloseThemeFile(hThemeFile); } return SUCCEEDED(hret); } BOOL LoadSchemeFromTheme(OUT PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme) { HTHEMEFILE hThemeFile = 0; HRESULT hret; HTHEME hTheme; int i; hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName, pSelectedTheme->Color->StyleName, pSelectedTheme->Size->StyleName, &hThemeFile, 0); if (!SUCCEEDED(hret)) return FALSE; hTheme = OpenThemeDataFromFile(hThemeFile, hCPLWindow, L"WINDOW", 0); if (hTheme == NULL) return FALSE; /* Load colors */ for (i = 0; i < NUM_COLORS; i++) { scheme->crColor[i] = GetThemeSysColor(hTheme,i); } /* Load sizes */ /* I wonder why GetThemeSysInt doesn't work here */ scheme->ncMetrics.iBorderWidth = GetThemeSysSize(hTheme, SM_CXFRAME); scheme->ncMetrics.iScrollWidth = GetThemeSysSize(hTheme, SM_CXVSCROLL); scheme->ncMetrics.iScrollHeight = GetThemeSysSize(hTheme, SM_CYHSCROLL); scheme->ncMetrics.iCaptionWidth = GetThemeSysSize(hTheme, SM_CXSIZE); scheme->ncMetrics.iCaptionHeight = GetThemeSysSize(hTheme, SM_CYSIZE); scheme->ncMetrics.iSmCaptionWidth = GetThemeSysSize(hTheme, SM_CXSMSIZE); scheme->ncMetrics.iSmCaptionHeight = GetThemeSysSize(hTheme, SM_CYSMSIZE); scheme->ncMetrics.iMenuWidth = GetThemeSysSize(hTheme, SM_CXMENUSIZE); scheme->ncMetrics.iMenuHeight = GetThemeSysSize(hTheme, SM_CYMENUSIZE); /* Load fonts */ GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &scheme->ncMetrics.lfCaptionFont); GetThemeSysFont(hTheme, TMT_SMALLCAPTIONFONT, &scheme->ncMetrics.lfSmCaptionFont); GetThemeSysFont(hTheme, TMT_MENUFONT, &scheme->ncMetrics.lfMenuFont ); GetThemeSysFont(hTheme, TMT_STATUSFONT, &scheme->ncMetrics.lfStatusFont); GetThemeSysFont(hTheme, TMT_MSGBOXFONT, &scheme->ncMetrics.lfMessageFont); GetThemeSysFont(hTheme, TMT_ICONTITLEFONT, &scheme->icMetrics.lfFont); scheme->bFlatMenus = GetThemeSysBool(hTheme, TMT_FLATMENUS); CloseThemeData(hTheme); return TRUE; } BOOL DrawThemePreview(IN HDC hdcMem, IN PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme, IN PRECT prcWindow) { HBRUSH hbrBack; HRESULT hres; hbrBack = CreateSolidBrush(scheme->crColor[COLOR_DESKTOP]); FillRect(hdcMem, prcWindow, hbrBack); DeleteObject(hbrBack); InflateRect(prcWindow, -10, -10); hres = DrawNCPreview(hdcMem, DNCP_DRAW_ALL, prcWindow, pSelectedTheme->Theme->ThemeFileName, pSelectedTheme->Color->StyleName, pSelectedTheme->Size->StyleName, &scheme->ncMetrics, scheme->crColor); return SUCCEEDED(hres); } BOOL ActivateThemeFile(LPCWSTR pwszFile) { PTHEME pThemes; THEME_SELECTION selection; COLOR_SCHEME scheme; BOOL ret = FALSE; pThemes = LoadThemes(); if (!pThemes) return FALSE; LoadCurrentScheme(&scheme); if (pwszFile) { ret = FindOrAppendTheme(pThemes, pwszFile, NULL, NULL, &selection); if (!ret) goto cleanup; ret = LoadSchemeFromTheme(&scheme, &selection); if (!ret) goto cleanup; } else { ret = GetActiveClassicTheme(pThemes, &selection); if (!ret) goto cleanup; ret = LoadSchemeFromReg(&scheme, &selection); if (!ret) goto cleanup; } ret = ActivateTheme(&selection); if (!ret) goto cleanup; ApplyScheme(&scheme, &selection); ret = TRUE; cleanup: CleanupThemes(pThemes); return ret; } /* TODO: */ INT_PTR CALLBACK ThemesPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { LPNMHDR lpnm; switch (uMsg) { case WM_INITDIALOG: break; case WM_COMMAND: break; case WM_NOTIFY: lpnm = (LPNMHDR)lParam; switch (lpnm->code) { case PSN_APPLY: SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)TEXT("")); return TRUE; } break; case WM_DESTROY: break; } return FALSE; }