2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Display Control Panel
4 * FILE: lib/cpl/desk/theme.c
5 * PURPOSE: Handling themes
7 * PROGRAMMERS: Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
13 static BOOL g_PresetLoaded
= FALSE
;
14 static INT g_TemplateCount
= 0;
16 static INT g_ColorList
[NUM_COLORS
];
18 static const TCHAR g_CPColors
[] = TEXT("Control Panel\\Colors");
19 static const TCHAR g_CPANewSchemes
[] = TEXT("Control Panel\\Appearance\\New Schemes");
20 static const TCHAR g_SelectedStyle
[] = TEXT("SelectedStyle");
22 /******************************************************************************/
24 THEME_PRESET g_ThemeTemplates
[MAX_TEMPLATES
];
26 /* This is the list of names for the colors stored in the registry */
27 const TCHAR g_RegColorNames
[NUM_COLORS
][MAX_COLORNAMELENGTH
] =
28 {TEXT("Scrollbar"), /* 00 = COLOR_SCROLLBAR */
29 TEXT("Background"), /* 01 = COLOR_DESKTOP */
30 TEXT("ActiveTitle"), /* 02 = COLOR_ACTIVECAPTION */
31 TEXT("InactiveTitle"), /* 03 = COLOR_INACTIVECAPTION */
32 TEXT("Menu"), /* 04 = COLOR_MENU */
33 TEXT("Window"), /* 05 = COLOR_WINDOW */
34 TEXT("WindowFrame"), /* 06 = COLOR_WINDOWFRAME */
35 TEXT("MenuText"), /* 07 = COLOR_MENUTEXT */
36 TEXT("WindowText"), /* 08 = COLOR_WINDOWTEXT */
37 TEXT("TitleText"), /* 09 = COLOR_CAPTIONTEXT */
38 TEXT("ActiveBorder"), /* 10 = COLOR_ACTIVEBORDER */
39 TEXT("InactiveBorder"), /* 11 = COLOR_INACTIVEBORDER */
40 TEXT("AppWorkSpace"), /* 12 = COLOR_APPWORKSPACE */
41 TEXT("Hilight"), /* 13 = COLOR_HIGHLIGHT */
42 TEXT("HilightText"), /* 14 = COLOR_HIGHLIGHTTEXT */
43 TEXT("ButtonFace"), /* 15 = COLOR_BTNFACE */
44 TEXT("ButtonShadow"), /* 16 = COLOR_BTNSHADOW */
45 TEXT("GrayText"), /* 17 = COLOR_GRAYTEXT */
46 TEXT("ButtonText"), /* 18 = COLOR_BTNTEXT */
47 TEXT("InactiveTitleText"), /* 19 = COLOR_INACTIVECAPTIONTEXT */
48 TEXT("ButtonHilight"), /* 20 = COLOR_BTNHIGHLIGHT */
49 TEXT("ButtonDkShadow"), /* 21 = COLOR_3DDKSHADOW */
50 TEXT("ButtonLight"), /* 22 = COLOR_3DLIGHT */
51 TEXT("InfoText"), /* 23 = COLOR_INFOTEXT */
52 TEXT("InfoWindow"), /* 24 = COLOR_INFOBK */
53 TEXT("ButtonAlternateFace"), /* 25 = COLOR_ALTERNATEBTNFACE */
54 TEXT("HotTrackingColor"), /* 26 = COLOR_HOTLIGHT */
55 TEXT("GradientActiveTitle"), /* 27 = COLOR_GRADIENTACTIVECAPTION */
56 TEXT("GradientInactiveTitle"), /* 28 = COLOR_GRADIENTINACTIVECAPTION */
57 TEXT("MenuHilight"), /* 29 = COLOR_MENUHILIGHT */
58 TEXT("MenuBar"), /* 30 = COLOR_MENUBAR */
61 /* This is the list of used metrics and their numbers */
62 const int g_SizeMetric
[NUM_SIZES
] =
64 SM_CXBORDER
, /* 00: SIZE_BORDER_X */
65 SM_CYBORDER
, /* 01: SIZE_BORDER_Y */
66 SM_CYCAPTION
, /* 02: SIZE_CAPTION_Y */
67 SM_CXICON
, /* 03: SIZE_ICON_X */
68 SM_CYICON
, /* 04: SIZE_ICON_Y */
69 SM_CXICONSPACING
, /* 05: SIZE_ICON_SPC_X */
70 SM_CYICONSPACING
, /* 06: SIZE_ICON_SPC_Y */
71 SM_CXMENUSIZE
, /* 07: SIZE_MENU_SIZE_X */
72 SM_CYMENU
, /* 08: SIZE_MENU_Y */
73 SM_CXVSCROLL
, /* 09: SIZE_SCROLL_X */
74 SM_CYHSCROLL
, /* 10: SIZE_SCROLL_Y */
75 SM_CYSMCAPTION
, /* 11: SIZE_SMCAPTION_Y */
76 SM_CXEDGE
, /* 12: SIZE_EDGE_X */
77 SM_CYEDGE
, /* 13: SIZE_EDGE_Y */
78 SM_CYSIZEFRAME
, /* 14: SIZE_FRAME_Y */
79 SM_CXMENUCHECK
, /* 15: SIZE_MENU_CHECK_X */
80 SM_CYMENUCHECK
, /* 16: SIZE_MENU_CHECK_Y */
81 SM_CYMENUSIZE
, /* 17: SIZE_MENU_SIZE_Y */
82 SM_CXSIZE
, /* 18: SIZE_SIZE_X */
83 SM_CYSIZE
/* 19: SIZE_SIZE_Y */
86 /******************************************************************************/
88 VOID
LoadCurrentTheme(THEME
* theme
)
91 NONCLIENTMETRICS NonClientMetrics
;
94 for (i
= 0; i
< NUM_COLORS
; i
++)
97 theme
->crColor
[i
] = (COLORREF
)GetSysColor(i
);
101 for (i
= 0; i
< NUM_SIZES
; i
++)
103 theme
->Size
[i
] = GetSystemMetrics(g_SizeMetric
[i
]);
107 NonClientMetrics
.cbSize
= sizeof(NONCLIENTMETRICS
);
108 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICS
), &NonClientMetrics
, 0);
109 theme
->lfFont
[FONT_CAPTION
] = NonClientMetrics
.lfCaptionFont
;
110 theme
->lfFont
[FONT_SMCAPTION
] = NonClientMetrics
.lfSmCaptionFont
;
111 theme
->lfFont
[FONT_MENU
] = NonClientMetrics
.lfMenuFont
;
112 theme
->lfFont
[FONT_INFO
] = NonClientMetrics
.lfStatusFont
;
113 theme
->lfFont
[FONT_DIALOG
] = NonClientMetrics
.lfMessageFont
;
114 SystemParametersInfo(SPI_GETICONTITLELOGFONT
, sizeof(LOGFONT
), &theme
->lfFont
[FONT_ICON
], 0);
117 /* "Use the following transition effect for menus and tooltips" */
118 SystemParametersInfo(SPI_GETMENUANIMATION
, sizeof(BOOL
), &theme
->Effects
.bMenuAnimation
, 0);
119 SystemParametersInfo(SPI_GETMENUFADE
, sizeof(BOOL
), &theme
->Effects
.bMenuFade
, 0);
120 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings
121 * Just keep them in sync for now:
123 theme
->Effects
.bTooltipAnimation
= theme
->Effects
.bMenuAnimation
;
124 theme
->Effects
.bTooltipFade
= theme
->Effects
.bMenuFade
;
126 /* show content of windows during dragging */
127 SystemParametersInfo(SPI_GETDRAGFULLWINDOWS
, 0, &theme
->Effects
.bDragFullWindows
, 0);
129 /* "Hide underlined letters for keyboard navigation until I press the Alt key" */
130 SystemParametersInfo(SPI_GETKEYBOARDCUES
, 0, &theme
->Effects
.bKeyboardCues
, 0);
133 BOOL
LoadThemeFromReg(THEME
* theme
, INT ThemeId
)
136 TCHAR strSelectedStyle
[4];
137 TCHAR strSizeName
[20] = {TEXT("Sizes\\0")};
138 TCHAR strValueName
[10];
139 HKEY hkNewSchemes
, hkScheme
, hkSize
;
140 DWORD dwType
, dwLength
;
144 LoadThemePresetEntries(strSelectedStyle
);
149 if (RegOpenKeyEx(HKEY_CURRENT_USER
, g_CPANewSchemes
, 0, KEY_READ
, &hkNewSchemes
) == ERROR_SUCCESS
)
151 if (RegOpenKeyEx(hkNewSchemes
, g_ThemeTemplates
[ThemeId
].strKeyName
, 0, KEY_READ
, &hkScheme
) == ERROR_SUCCESS
)
153 lstrcpyn(&strSizeName
[6], g_ThemeTemplates
[ThemeId
].strSizeName
, 3);
154 if (RegOpenKeyEx(hkScheme
, strSizeName
, 0, KEY_READ
, &hkSize
) == ERROR_SUCCESS
)
158 dwLength
= sizeof(DWORD
);
159 if (RegQueryValueEx(hkSize
, TEXT("FlatMenus"), NULL
, &dwType
, (LPBYTE
)&theme
->bFlatMenus
, &dwLength
) != ERROR_SUCCESS
||
162 /* Failed to read registry value */
163 theme
->bFlatMenus
= FALSE
;
166 for (i
= 0; i
< NUM_COLORS
; i
++)
168 wsprintf(strValueName
, TEXT("Color #%d"), i
);
169 dwLength
= sizeof(COLORREF
);
170 if (RegQueryValueEx(hkSize
, strValueName
, NULL
, &dwType
, (LPBYTE
)&theme
->crColor
[i
], &dwLength
) != ERROR_SUCCESS
||
173 /* Failed to read registry value, initialize with current setting for now */
174 theme
->crColor
[i
] = GetSysColor(i
);
178 for (i
= 0; i
< NUM_FONTS
; i
++)
180 wsprintf(strValueName
, TEXT("Font #%d"), i
);
181 dwLength
= sizeof(LOGFONT
);
182 if (RegQueryValueEx(hkSize
, strValueName
, NULL
, &dwType
, (LPBYTE
)&theme
->lfFont
[i
], &dwLength
) != ERROR_SUCCESS
||
183 dwType
!= REG_BINARY
|| dwLength
!= sizeof(LOGFONT
))
185 /* Failed to read registry value */
190 for (i
= 0; i
< NUM_SIZES
; i
++)
192 wsprintf(strValueName
, TEXT("Size #%d"), i
);
193 dwLength
= sizeof(UINT64
);
194 if (RegQueryValueEx(hkSize
, strValueName
, NULL
, &dwType
, (LPBYTE
)&theme
->Size
[i
], &dwLength
) != ERROR_SUCCESS
||
195 dwType
!= REG_QWORD
|| dwLength
!= sizeof(UINT64
))
197 /* Failed to read registry value, initialize with current setting for now */
198 theme
->Size
[i
] = GetSystemMetrics(g_SizeMetric
[i
]);
201 RegCloseKey(hkScheme
);
203 RegCloseKey(hkScheme
);
205 RegCloseKey(hkNewSchemes
);
212 _UpdateUserPref(UINT SpiGet
, UINT SpiSet
, BOOL
*pbFlag
)
214 SystemParametersInfo(SpiSet
, 0, (PVOID
)pbFlag
, SPIF_UPDATEINIFILE
|SPIF_SENDCHANGE
);
216 #define UPDATE_USERPREF(NAME,pbFlag) _UpdateUserPref(SPI_GET ## NAME, SPI_SET ## NAME, pbFlag)
218 VOID
ApplyTheme(THEME
* theme
, INT ThemeId
)
224 NONCLIENTMETRICS NonClientMetrics
;
225 ICONMETRICS IconMetrics
;
227 /* Apply Colors from global variable */
228 SetSysColors(NUM_COLORS
, g_ColorList
, theme
->crColor
);
230 /* Save colors to registry */
231 Result
= RegOpenKeyEx(HKEY_CURRENT_USER
, g_CPColors
, 0, KEY_ALL_ACCESS
, &hKey
);
232 if (Result
!= ERROR_SUCCESS
)
234 /* Could not open the key, try to create it */
235 Result
= RegCreateKeyEx(HKEY_CURRENT_USER
, g_CPColors
, 0, NULL
, 0, KEY_ALL_ACCESS
, NULL
, &hKey
, &dwDisposition
);
238 if (Result
== ERROR_SUCCESS
)
240 for (i
= 0; i
< NUM_COLORS
; i
++)
242 DWORD red
= GetRValue(theme
->crColor
[i
]);
243 DWORD green
= GetGValue(theme
->crColor
[i
]);
244 DWORD blue
= GetBValue(theme
->crColor
[i
]);
245 wsprintf(clText
, TEXT("%d %d %d"), red
, green
, blue
);
246 RegSetValueEx(hKey
, g_RegColorNames
[i
], 0, REG_SZ
, (BYTE
*)clText
, (lstrlen(clText
) + 1) * sizeof(TCHAR
));
251 /* Apply non client metrics */
252 NonClientMetrics
.cbSize
= sizeof(NONCLIENTMETRICS
);
253 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICS
), &NonClientMetrics
, 0);
254 NonClientMetrics
.lfCaptionFont
= theme
->lfFont
[FONT_CAPTION
];
255 NonClientMetrics
.lfSmCaptionFont
= theme
->lfFont
[FONT_SMCAPTION
];
256 NonClientMetrics
.lfMenuFont
= theme
->lfFont
[FONT_MENU
];
257 NonClientMetrics
.lfStatusFont
= theme
->lfFont
[FONT_INFO
];
258 NonClientMetrics
.lfMessageFont
= theme
->lfFont
[FONT_DIALOG
];
259 NonClientMetrics
.iBorderWidth
= theme
->Size
[SIZE_BORDER_X
];
260 NonClientMetrics
.iScrollWidth
= theme
->Size
[SIZE_SCROLL_X
];
261 NonClientMetrics
.iScrollHeight
= theme
->Size
[SIZE_SCROLL_Y
];
262 NonClientMetrics
.iCaptionWidth
= theme
->Size
[SIZE_CAPTION_Y
];
263 NonClientMetrics
.iCaptionHeight
= theme
->Size
[SIZE_CAPTION_Y
];
264 NonClientMetrics
.iSmCaptionWidth
= theme
->Size
[SIZE_SMCAPTION_Y
];
265 NonClientMetrics
.iSmCaptionHeight
= theme
->Size
[SIZE_SMCAPTION_Y
];
266 NonClientMetrics
.iMenuWidth
= theme
->Size
[SIZE_MENU_SIZE_X
];
267 NonClientMetrics
.iMenuHeight
= theme
->Size
[SIZE_MENU_Y
];
268 SystemParametersInfo(SPI_SETNONCLIENTMETRICS
,
269 sizeof(NONCLIENTMETRICS
),
271 SPIF_UPDATEINIFILE
| SPIF_SENDCHANGE
);
273 /* Apply icon metrics */
274 IconMetrics
.cbSize
= sizeof(ICONMETRICS
);
275 SystemParametersInfo(SPI_GETICONMETRICS
, sizeof(ICONMETRICS
), &IconMetrics
, 0);
276 IconMetrics
.iHorzSpacing
= theme
->Size
[SIZE_ICON_SPC_X
];
277 IconMetrics
.iVertSpacing
= theme
->Size
[SIZE_ICON_SPC_Y
];
278 IconMetrics
.lfFont
= theme
->lfFont
[FONT_ICON
];
279 SystemParametersInfo(SPI_SETICONMETRICS
,
282 SPIF_UPDATEINIFILE
| SPIF_SENDCHANGE
);
284 /* Effects, save only when needed: */
285 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings
286 * Just keep them in sync for now.
288 theme
->Effects
.bTooltipAnimation
= theme
->Effects
.bMenuAnimation
;
289 theme
->Effects
.bTooltipFade
= theme
->Effects
.bMenuFade
;
290 SystemParametersInfo(SPI_SETDRAGFULLWINDOWS
, theme
->Effects
.bDragFullWindows
, (PVOID
)&theme
->Effects
.bDragFullWindows
, SPIF_SENDCHANGE
| SPIF_UPDATEINIFILE
);
291 SystemParametersInfo(SPI_SETKEYBOARDCUES
, 0, IntToPtr(theme
->Effects
.bKeyboardCues
), SPIF_SENDCHANGE
| SPIF_UPDATEINIFILE
);
292 //UPDATE_USERPREF(ACTIVEWINDOWTRACKING, &theme->Effects.bActiveWindowTracking);
293 //UPDATE_USERPREF(MENUANIMATION, &theme->Effects.bMenuAnimation);
294 //UPDATE_USERPREF(COMBOBOXANIMATION, &theme->Effects.bComboBoxAnimation);
295 //UPDATE_USERPREF(LISTBOXSMOOTHSCROLLING, &theme->Effects.bListBoxSmoothScrolling);
296 //UPDATE_USERPREF(GRADIENTCAPTIONS, &theme->Effects.bGradientCaptions);
297 //UPDATE_USERPREF(ACTIVEWNDTRKZORDER, &theme->Effects.bActiveWndTrkZorder);
298 //UPDATE_USERPREF(HOTTRACKING, &theme->Effects.bHotTracking);
299 UPDATE_USERPREF(MENUFADE
, &theme
->Effects
.bMenuFade
);
300 //UPDATE_USERPREF(SELECTIONFADE, &theme->Effects.bSelectionFade);
301 UPDATE_USERPREF(TOOLTIPANIMATION
, &theme
->Effects
.bTooltipAnimation
);
302 UPDATE_USERPREF(TOOLTIPFADE
, &theme
->Effects
.bTooltipFade
);
303 //UPDATE_USERPREF(CURSORSHADOW, &theme->Effects.bCursorShadow);
304 //UPDATE_USERPREF(UIEFFECTS, &theme->Effects.bUiEffects);
307 Result
= RegOpenKeyEx(HKEY_CURRENT_USER
, g_CPANewSchemes
, 0, KEY_ALL_ACCESS
, &hKey
);
308 if (Result
== ERROR_SUCCESS
)
311 clText
[0] = TEXT('\0');
313 lstrcpy(clText
, g_ThemeTemplates
[ThemeId
].strKeyName
);
314 RegSetValueEx(hKey
, g_SelectedStyle
, 0, REG_SZ
, (BYTE
*)clText
, (lstrlen(clText
) + 1) * sizeof(TCHAR
));
319 BOOL
SaveTheme(THEME
* theme
, LPCTSTR strLegacyName
)
321 /* FIXME: implement */
325 INT
LoadThemePresetEntries(LPTSTR pszSelectedStyle
)
327 HKEY hkNewSchemes
, hkScheme
, hkSizes
, hkSize
;
328 FILETIME ftLastWriteTime
;
329 DWORD dwLength
, dwType
;
331 INT iStyle
, iSize
, iTemplateIndex
;
334 lstrcpy(pszSelectedStyle
, TEXT(""));
337 Result
= RegCreateKeyEx(HKEY_CURRENT_USER
, g_CPANewSchemes
, 0, NULL
, 0, KEY_ALL_ACCESS
, NULL
, &hkNewSchemes
, &dwDisposition
);
338 if (Result
== ERROR_SUCCESS
)
340 /* First find out the currently selected template */
341 dwLength
= 4 * sizeof(TCHAR
);
342 RegQueryValueEx(hkNewSchemes
, g_SelectedStyle
, NULL
, &dwType
, (LPBYTE
)pszSelectedStyle
, &dwLength
);
344 /* Check if already loaded */
347 RegCloseKey(hkNewSchemes
);
348 return g_TemplateCount
;
352 dwLength
= MAX_TEMPLATENAMELENTGH
;
353 while((RegEnumKeyEx(hkNewSchemes
, iStyle
, g_ThemeTemplates
[iTemplateIndex
].strKeyName
, &dwLength
,
354 NULL
, NULL
, NULL
, &ftLastWriteTime
) == ERROR_SUCCESS
) && (iTemplateIndex
< MAX_TEMPLATES
))
356 /* is it really a template or one of the other entries */
359 if (RegOpenKeyEx(hkNewSchemes
, g_ThemeTemplates
[iTemplateIndex
].strKeyName
, 0, KEY_READ
, &hkScheme
) == ERROR_SUCCESS
)
361 if (RegOpenKeyEx(hkScheme
, TEXT("Sizes"), 0, KEY_READ
, &hkSizes
) == ERROR_SUCCESS
)
365 while((RegEnumKeyEx(hkSizes
, iSize
, g_ThemeTemplates
[iTemplateIndex
].strSizeName
, &dwLength
,
366 NULL
, NULL
, NULL
, &ftLastWriteTime
) == ERROR_SUCCESS
) && (iSize
<= 4))
368 if(RegOpenKeyEx(hkSizes
, g_ThemeTemplates
[iTemplateIndex
].strSizeName
, 0, KEY_READ
, &hkSize
) == ERROR_SUCCESS
)
370 dwLength
= MAX_TEMPLATENAMELENTGH
;
371 RegQueryValueEx(hkSize
, TEXT("DisplayName"), NULL
, &dwType
, (LPBYTE
)&g_ThemeTemplates
[iTemplateIndex
].strDisplayName
, &dwLength
);
372 dwLength
= MAX_TEMPLATENAMELENTGH
;
373 RegQueryValueEx(hkSize
, TEXT("LegacyName"), NULL
, &dwType
, (LPBYTE
)&g_ThemeTemplates
[iTemplateIndex
].strLegacyName
, &dwLength
);
380 RegCloseKey(hkSizes
);
382 RegCloseKey(hkScheme
);
386 dwLength
= MAX_TEMPLATENAMELENTGH
;
388 RegCloseKey(hkNewSchemes
);
389 g_PresetLoaded
= TRUE
;
390 g_TemplateCount
= iTemplateIndex
;
392 return iTemplateIndex
;