- Update to r53061
[reactos.git] / dll / cpl / desk / theme.c
1 /*
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
6 *
7 * PROGRAMMERS: Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
8 */
9
10 #include "desk.h"
11
12 static BOOL g_PresetLoaded = FALSE;
13 static INT g_TemplateCount = 0;
14
15 static INT g_ColorList[NUM_COLORS];
16
17 static const TCHAR g_CPColors[] = TEXT("Control Panel\\Colors");
18 static const TCHAR g_CPANewSchemes[] = TEXT("Control Panel\\Appearance\\New Schemes");
19 static const TCHAR g_SelectedStyle[] = TEXT("SelectedStyle");
20
21 /******************************************************************************/
22
23 THEME_PRESET g_ThemeTemplates[MAX_TEMPLATES];
24
25 /* This is the list of names for the colors stored in the registry */
26 static const TCHAR *g_RegColorNames[NUM_COLORS] =
27 {TEXT("Scrollbar"), /* 00 = COLOR_SCROLLBAR */
28 TEXT("Background"), /* 01 = COLOR_DESKTOP */
29 TEXT("ActiveTitle"), /* 02 = COLOR_ACTIVECAPTION */
30 TEXT("InactiveTitle"), /* 03 = COLOR_INACTIVECAPTION */
31 TEXT("Menu"), /* 04 = COLOR_MENU */
32 TEXT("Window"), /* 05 = COLOR_WINDOW */
33 TEXT("WindowFrame"), /* 06 = COLOR_WINDOWFRAME */
34 TEXT("MenuText"), /* 07 = COLOR_MENUTEXT */
35 TEXT("WindowText"), /* 08 = COLOR_WINDOWTEXT */
36 TEXT("TitleText"), /* 09 = COLOR_CAPTIONTEXT */
37 TEXT("ActiveBorder"), /* 10 = COLOR_ACTIVEBORDER */
38 TEXT("InactiveBorder"), /* 11 = COLOR_INACTIVEBORDER */
39 TEXT("AppWorkSpace"), /* 12 = COLOR_APPWORKSPACE */
40 TEXT("Hilight"), /* 13 = COLOR_HIGHLIGHT */
41 TEXT("HilightText"), /* 14 = COLOR_HIGHLIGHTTEXT */
42 TEXT("ButtonFace"), /* 15 = COLOR_BTNFACE */
43 TEXT("ButtonShadow"), /* 16 = COLOR_BTNSHADOW */
44 TEXT("GrayText"), /* 17 = COLOR_GRAYTEXT */
45 TEXT("ButtonText"), /* 18 = COLOR_BTNTEXT */
46 TEXT("InactiveTitleText"), /* 19 = COLOR_INACTIVECAPTIONTEXT */
47 TEXT("ButtonHilight"), /* 20 = COLOR_BTNHIGHLIGHT */
48 TEXT("ButtonDkShadow"), /* 21 = COLOR_3DDKSHADOW */
49 TEXT("ButtonLight"), /* 22 = COLOR_3DLIGHT */
50 TEXT("InfoText"), /* 23 = COLOR_INFOTEXT */
51 TEXT("InfoWindow"), /* 24 = COLOR_INFOBK */
52 TEXT("ButtonAlternateFace"), /* 25 = COLOR_ALTERNATEBTNFACE */
53 TEXT("HotTrackingColor"), /* 26 = COLOR_HOTLIGHT */
54 TEXT("GradientActiveTitle"), /* 27 = COLOR_GRADIENTACTIVECAPTION */
55 TEXT("GradientInactiveTitle"), /* 28 = COLOR_GRADIENTINACTIVECAPTION */
56 TEXT("MenuHilight"), /* 29 = COLOR_MENUHILIGHT */
57 TEXT("MenuBar"), /* 30 = COLOR_MENUBAR */
58 };
59
60 /* This is the list of used metrics and their numbers */
61 static const int g_SizeMetric[NUM_SIZES] =
62 {
63 SM_CXBORDER, /* 00: SIZE_BORDER_X */
64 SM_CYBORDER, /* 01: SIZE_BORDER_Y */
65 SM_CYSIZE, /* 02: SIZE_CAPTION_Y */
66 SM_CXICON, /* 03: SIZE_ICON_X */
67 SM_CYICON, /* 04: SIZE_ICON_Y */
68 SM_CXICONSPACING, /* 05: SIZE_ICON_SPC_X */
69 SM_CYICONSPACING, /* 06: SIZE_ICON_SPC_Y */
70 SM_CXMENUSIZE, /* 07: SIZE_MENU_SIZE_X */
71 SM_CYMENU, /* 08: SIZE_MENU_Y */
72 SM_CXVSCROLL, /* 09: SIZE_SCROLL_X */
73 SM_CYHSCROLL, /* 10: SIZE_SCROLL_Y */
74 SM_CYSMSIZE, /* 11: SIZE_SMCAPTION_Y */
75 SM_CXEDGE, /* 12: SIZE_EDGE_X */
76 SM_CYEDGE, /* 13: SIZE_EDGE_Y */
77 SM_CYSIZEFRAME, /* 14: SIZE_FRAME_Y */
78 SM_CXMENUCHECK, /* 15: SIZE_MENU_CHECK_X */
79 SM_CYMENUCHECK, /* 16: SIZE_MENU_CHECK_Y */
80 SM_CYMENUSIZE, /* 17: SIZE_MENU_SIZE_Y */
81 SM_CXSIZE, /* 18: SIZE_SIZE_X */
82 SM_CYSIZE /* 19: SIZE_SIZE_Y */
83 };
84
85 /******************************************************************************/
86
87 VOID LoadCurrentTheme(THEME* theme)
88 {
89 INT i;
90 NONCLIENTMETRICS NonClientMetrics;
91
92 /* Load colors */
93 for (i = 0; i < NUM_COLORS; i++)
94 {
95 g_ColorList[i] = i;
96 theme->crColor[i] = (COLORREF)GetSysColor(i);
97 }
98
99 /* Load sizes */
100 for (i = 0; i < NUM_SIZES; i++)
101 {
102 theme->Size[i] = GetSystemMetrics(g_SizeMetric[i]);
103 }
104
105 /* Load fonts */
106 NonClientMetrics.cbSize = sizeof(NONCLIENTMETRICS);
107 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &NonClientMetrics, 0);
108 theme->lfFont[FONT_CAPTION] = NonClientMetrics.lfCaptionFont;
109 theme->lfFont[FONT_SMCAPTION] = NonClientMetrics.lfSmCaptionFont;
110 theme->lfFont[FONT_MENU] = NonClientMetrics.lfMenuFont;
111 theme->lfFont[FONT_INFO] = NonClientMetrics.lfStatusFont;
112 theme->lfFont[FONT_DIALOG] = NonClientMetrics.lfMessageFont;
113 SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &theme->lfFont[FONT_ICON], 0);
114
115 /* Effects */
116 /* "Use the following transition effect for menus and tooltips" */
117 SystemParametersInfo(SPI_GETMENUANIMATION, sizeof(BOOL), &theme->Effects.bMenuAnimation, 0);
118 SystemParametersInfo(SPI_GETMENUFADE, sizeof(BOOL), &theme->Effects.bMenuFade, 0);
119 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings
120 * Just keep them in sync for now:
121 */
122 theme->Effects.bTooltipAnimation = theme->Effects.bMenuAnimation;
123 theme->Effects.bTooltipFade = theme->Effects.bMenuFade;
124
125 /* show content of windows during dragging */
126 SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &theme->Effects.bDragFullWindows, 0);
127
128 /* "Hide underlined letters for keyboard navigation until I press the Alt key" */
129 SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &theme->Effects.bKeyboardCues, 0);
130 }
131
132 BOOL LoadThemeFromReg(THEME* theme, INT ThemeId)
133 {
134 INT i;
135 TCHAR strSelectedStyle[4];
136 TCHAR strSizeName[20] = TEXT("Sizes\\0");
137 TCHAR strValueName[10];
138 HKEY hkNewSchemes, hkScheme, hkSize;
139 DWORD dwType, dwLength;
140 UINT64 iSize;
141 BOOL Ret = FALSE;
142
143 if (!g_PresetLoaded)
144 LoadThemePresetEntries(strSelectedStyle);
145
146 if (ThemeId == -1)
147 return FALSE;
148
149 if (RegOpenKeyEx(HKEY_CURRENT_USER, g_CPANewSchemes, 0, KEY_READ, &hkNewSchemes) == ERROR_SUCCESS)
150 {
151 if (RegOpenKeyEx(hkNewSchemes, g_ThemeTemplates[ThemeId].strKeyName, 0, KEY_READ, &hkScheme) == ERROR_SUCCESS)
152 {
153 lstrcpyn(&strSizeName[6], g_ThemeTemplates[ThemeId].strSizeName, 3);
154 if (RegOpenKeyEx(hkScheme, strSizeName, 0, KEY_READ, &hkSize) == ERROR_SUCCESS)
155 {
156 Ret = TRUE;
157
158 dwLength = sizeof(DWORD);
159 if (RegQueryValueEx(hkSize, TEXT("FlatMenus"), NULL, &dwType, (LPBYTE)&theme->bFlatMenus, &dwLength) != ERROR_SUCCESS ||
160 dwType != REG_DWORD)
161 {
162 /* Failed to read registry value */
163 theme->bFlatMenus = FALSE;
164 }
165
166 for (i = 0; i < NUM_COLORS; i++)
167 {
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 ||
171 dwType != REG_DWORD)
172 {
173 /* Failed to read registry value, initialize with current setting for now */
174 theme->crColor[i] = GetSysColor(i);
175 }
176 }
177
178 for (i = 0; i < NUM_FONTS; i++)
179 {
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))
184 {
185 /* Failed to read registry value */
186 Ret = FALSE;
187 }
188 }
189
190 for (i = 0; i < NUM_SIZES; i++)
191 {
192 wsprintf(strValueName, TEXT("Size #%d"), i);
193 dwLength = sizeof(UINT64);
194 if (RegQueryValueEx(hkSize, strValueName, NULL, &dwType, (LPBYTE)&iSize, &dwLength) != ERROR_SUCCESS ||
195 dwType != REG_QWORD || dwLength != sizeof(UINT64))
196 {
197 /* Failed to read registry value, initialize with current setting for now */
198 theme->Size[i] = GetSystemMetrics(g_SizeMetric[i]);
199 }
200 else
201 theme->Size[i] = (INT)iSize;
202 }
203 RegCloseKey(hkScheme);
204 }
205 RegCloseKey(hkScheme);
206 }
207 RegCloseKey(hkNewSchemes);
208 }
209
210 return Ret;
211 }
212
213 VOID ApplyTheme(THEME* theme, INT ThemeId)
214 {
215 INT i, Result;
216 HKEY hKey;
217 TCHAR clText[16];
218 NONCLIENTMETRICS NonClientMetrics;
219 ICONMETRICS IconMetrics;
220
221 /* Apply Colors from global variable */
222 SetSysColors(NUM_COLORS, g_ColorList, theme->crColor);
223
224 /* Save colors to registry */
225 Result = RegCreateKeyEx(HKEY_CURRENT_USER, g_CPColors, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, NULL);
226 if (Result == ERROR_SUCCESS)
227 {
228 for (i = 0; i < NUM_COLORS; i++)
229 {
230 DWORD red = GetRValue(theme->crColor[i]);
231 DWORD green = GetGValue(theme->crColor[i]);
232 DWORD blue = GetBValue(theme->crColor[i]);
233 wsprintf(clText, TEXT("%d %d %d"), red, green, blue);
234 RegSetValueEx(hKey, g_RegColorNames[i], 0, REG_SZ, (BYTE *)clText, (lstrlen(clText) + 1) * sizeof(TCHAR));
235 }
236 RegCloseKey(hKey);
237 }
238
239 /* Apply non client metrics */
240 NonClientMetrics.cbSize = sizeof(NONCLIENTMETRICS);
241 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &NonClientMetrics, 0);
242 NonClientMetrics.lfCaptionFont = theme->lfFont[FONT_CAPTION];
243 NonClientMetrics.lfSmCaptionFont = theme->lfFont[FONT_SMCAPTION];
244 NonClientMetrics.lfMenuFont = theme->lfFont[FONT_MENU];
245 NonClientMetrics.lfStatusFont = theme->lfFont[FONT_INFO];
246 NonClientMetrics.lfMessageFont = theme->lfFont[FONT_DIALOG];
247 NonClientMetrics.iBorderWidth = theme->Size[SIZE_BORDER_X];
248 NonClientMetrics.iScrollWidth = theme->Size[SIZE_SCROLL_X];
249 NonClientMetrics.iScrollHeight = theme->Size[SIZE_SCROLL_Y];
250 NonClientMetrics.iCaptionWidth = theme->Size[SIZE_CAPTION_Y];
251 NonClientMetrics.iCaptionHeight = theme->Size[SIZE_CAPTION_Y];
252 NonClientMetrics.iSmCaptionWidth = theme->Size[SIZE_SMCAPTION_Y];
253 NonClientMetrics.iSmCaptionHeight = theme->Size[SIZE_SMCAPTION_Y];
254 NonClientMetrics.iMenuWidth = theme->Size[SIZE_MENU_SIZE_X];
255 NonClientMetrics.iMenuHeight = theme->Size[SIZE_MENU_Y];
256 SystemParametersInfo(SPI_SETNONCLIENTMETRICS,
257 sizeof(NONCLIENTMETRICS),
258 &NonClientMetrics,
259 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
260
261 /* Apply icon metrics */
262 IconMetrics.cbSize = sizeof(ICONMETRICS);
263 SystemParametersInfo(SPI_GETICONMETRICS, sizeof(ICONMETRICS), &IconMetrics, 0);
264 IconMetrics.iHorzSpacing = theme->Size[SIZE_ICON_SPC_X];
265 IconMetrics.iVertSpacing = theme->Size[SIZE_ICON_SPC_Y];
266 IconMetrics.lfFont = theme->lfFont[FONT_ICON];
267 SystemParametersInfo(SPI_SETICONMETRICS,
268 sizeof(ICONMETRICS),
269 &IconMetrics,
270 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
271
272 /* Effects, save only when needed: */
273 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings
274 * Just keep them in sync for now.
275 */
276 theme->Effects.bTooltipAnimation = theme->Effects.bMenuAnimation;
277 theme->Effects.bTooltipFade = theme->Effects.bMenuFade;
278 SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, theme->Effects.bDragFullWindows, (PVOID)&theme->Effects.bDragFullWindows, SPIF_SENDCHANGE | SPIF_UPDATEINIFILE);
279 SystemParametersInfo(SPI_SETKEYBOARDCUES, 0, IntToPtr(theme->Effects.bKeyboardCues), SPIF_SENDCHANGE | SPIF_UPDATEINIFILE);
280 //SystemParametersInfo(SPI_SETACTIVEWINDOWTRACKING, 0, (PVOID)&theme->Effects.bActiveWindowTracking, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
281 //SystemParametersInfo(SPI_SETMENUANIMATION, 0, (PVOID)&theme->Effects.bMenuAnimation, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
282 //SystemParametersInfo(SPI_SETCOMBOBOXANIMATION, 0, (PVOID)&theme->Effects.bComboBoxAnimation, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
283 //SystemParametersInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, (PVOID)&theme->Effects.bListBoxSmoothScrolling, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
284 //SystemParametersInfo(SPI_SETGRADIENTCAPTIONS, 0, (PVOID)&theme->Effects.bGradientCaptions, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
285 //SystemParametersInfo(SPI_SETACTIVEWNDTRKZORDER, 0, (PVOID)&theme->Effects.bActiveWndTrkZorder, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
286 //SystemParametersInfo(SPI_SETHOTTRACKING, 0, (PVOID)&theme->Effects.bHotTracking, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
287 SystemParametersInfo(SPI_SETMENUFADE, 0, (PVOID)&theme->Effects.bMenuFade, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
288 //SystemParametersInfo(SPI_SETSELECTIONFADE, 0, (PVOID)&theme->Effects.bSelectionFade, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
289 SystemParametersInfo(SPI_SETTOOLTIPANIMATION, 0, (PVOID)&theme->Effects.bTooltipAnimation, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
290 SystemParametersInfo(SPI_SETTOOLTIPFADE, 0, (PVOID)&theme->Effects.bTooltipFade, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
291 //SystemParametersInfo(SPI_SETCURSORSHADOW, 0, (PVOID)&theme->Effects.bCursorShadow, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
292 //SystemParametersInfo(SPI_SETUIEFFECTS, 0, (PVOID)&theme->Effects.bUiEffects, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
293
294 /* Save ThemeId */
295 Result = RegOpenKeyEx(HKEY_CURRENT_USER, g_CPANewSchemes, 0, KEY_ALL_ACCESS, &hKey);
296 if (Result == ERROR_SUCCESS)
297 {
298 if (ThemeId == -1)
299 clText[0] = TEXT('\0');
300 else
301 lstrcpy(clText, g_ThemeTemplates[ThemeId].strKeyName);
302 RegSetValueEx(hKey, g_SelectedStyle, 0, REG_SZ, (BYTE *)clText, (lstrlen(clText) + 1) * sizeof(TCHAR));
303 RegCloseKey(hKey);
304 }
305 }
306
307 BOOL SaveTheme(THEME* theme, LPCTSTR strLegacyName)
308 {
309 /* FIXME: implement */
310 return FALSE;
311 }
312
313 INT LoadThemePresetEntries(LPTSTR pszSelectedStyle)
314 {
315 HKEY hkNewSchemes, hkScheme, hkSizes, hkSize;
316 FILETIME ftLastWriteTime;
317 DWORD dwLength, dwType;
318 DWORD dwDisposition;
319 INT iStyle, iSize, iTemplateIndex;
320 INT Result;
321
322 lstrcpy(pszSelectedStyle, TEXT(""));
323
324 iTemplateIndex = 0;
325 Result = RegCreateKeyEx(HKEY_CURRENT_USER, g_CPANewSchemes, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkNewSchemes, &dwDisposition);
326 if (Result == ERROR_SUCCESS)
327 {
328 /* First find out the currently selected template */
329 dwLength = 4 * sizeof(TCHAR);
330 RegQueryValueEx(hkNewSchemes, g_SelectedStyle, NULL, &dwType, (LPBYTE)pszSelectedStyle, &dwLength);
331
332 /* Check if already loaded */
333 if (g_PresetLoaded)
334 {
335 RegCloseKey(hkNewSchemes);
336 return g_TemplateCount;
337 }
338
339 iStyle = 0;
340 dwLength = MAX_TEMPLATENAMELENTGH;
341 while((RegEnumKeyEx(hkNewSchemes, iStyle, g_ThemeTemplates[iTemplateIndex].strKeyName, &dwLength,
342 NULL, NULL, NULL, &ftLastWriteTime) == ERROR_SUCCESS) && (iTemplateIndex < MAX_TEMPLATES))
343 {
344 /* is it really a template or one of the other entries */
345 if (dwLength <= 4)
346 {
347 if (RegOpenKeyEx(hkNewSchemes, g_ThemeTemplates[iTemplateIndex].strKeyName, 0, KEY_READ, &hkScheme) == ERROR_SUCCESS)
348 {
349 if (RegOpenKeyEx(hkScheme, TEXT("Sizes"), 0, KEY_READ, &hkSizes) == ERROR_SUCCESS)
350 {
351 iSize = 0;
352 dwLength = 3;
353 while((RegEnumKeyEx(hkSizes, iSize, g_ThemeTemplates[iTemplateIndex].strSizeName, &dwLength,
354 NULL, NULL, NULL, &ftLastWriteTime) == ERROR_SUCCESS) && (iSize <= 4))
355 {
356 if(RegOpenKeyEx(hkSizes, g_ThemeTemplates[iTemplateIndex].strSizeName, 0, KEY_READ, &hkSize) == ERROR_SUCCESS)
357 {
358 dwLength = MAX_TEMPLATENAMELENTGH;
359 RegQueryValueEx(hkSize, TEXT("DisplayName"), NULL, &dwType, (LPBYTE)&g_ThemeTemplates[iTemplateIndex].strDisplayName, &dwLength);
360 dwLength = MAX_TEMPLATENAMELENTGH;
361 RegQueryValueEx(hkSize, TEXT("LegacyName"), NULL, &dwType, (LPBYTE)&g_ThemeTemplates[iTemplateIndex].strLegacyName, &dwLength);
362 RegCloseKey(hkSize);
363 }
364 iSize++;
365 iTemplateIndex++;
366 dwLength = 3;
367 }
368 RegCloseKey(hkSizes);
369 }
370 RegCloseKey(hkScheme);
371 }
372 }
373 iStyle++;
374 dwLength = MAX_TEMPLATENAMELENTGH;
375 }
376 RegCloseKey(hkNewSchemes);
377 g_PresetLoaded = TRUE;
378 g_TemplateCount = iTemplateIndex;
379 }
380 return iTemplateIndex;
381 }