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
SchemeSetMetric(IN COLOR_SCHEME * scheme,int id,int value)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
SchemeGetMetric(IN COLOR_SCHEME * scheme,int id)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
SchemeGetFont(IN COLOR_SCHEME * scheme,int id)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
LoadCurrentScheme(OUT COLOR_SCHEME * scheme)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
LoadSchemeFromReg(OUT COLOR_SCHEME * scheme,IN PTHEME_SELECTION pSelectedTheme)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
ApplyScheme(IN COLOR_SCHEME * scheme,IN PTHEME_SELECTION pSelectedTheme)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*
CreateTheme(LPCWSTR pszName,LPCWSTR pszDisplayName)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
CreateStyle(LPCWSTR pszName,LPCWSTR pszDisplayName)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
CleanupStyles(IN PTHEME_STYLE pStylesList)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
CleanupThemes(IN PTHEME pThemeList)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
FindStyle(IN PTHEME_STYLE pStylesList,IN PCWSTR StyleName)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
LoadSchemeSizes(IN HKEY hkScheme)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*
LoadClassicColorSchemes(VOID)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*
EnumThemeStyles(IN LPCWSTR pszThemeFileName,IN ENUMTHEMESTYLE pfnEnumTheme)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
LoadTheme(IN LPCWSTR pszThemeFileName,IN LPCWSTR pszThemeName)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
EnumThemeProc(IN LPVOID lpReserved,IN LPCWSTR pszThemeFileName,IN LPCWSTR pszThemeName,IN LPCWSTR pszToolTip,IN LPVOID lpReserved2,IN OUT LPVOID lpData)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
LoadThemes(VOID)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
FindOrAppendTheme(IN PTHEME pThemeList,IN LPCWSTR pwszThemeFileName,IN LPCWSTR pwszColorBuff,IN LPCWSTR pwszSizeBuff,OUT PTHEME_SELECTION pSelectedTheme)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
GetActiveTheme(IN PTHEME pThemeList,OUT PTHEME_SELECTION pSelectedTheme)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
GetActiveClassicTheme(IN PTHEME pThemeList,OUT PTHEME_SELECTION pSelectedTheme)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
ActivateTheme(IN PTHEME_SELECTION pSelectedTheme)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
LoadSchemeFromTheme(OUT PCOLOR_SCHEME scheme,IN PTHEME_SELECTION pSelectedTheme)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
DrawThemePreview(IN HDC hdcMem,IN PCOLOR_SCHEME scheme,IN PTHEME_SELECTION pSelectedTheme,IN PRECT prcWindow)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, -8, -8);
1001 prcWindow->bottom -= 12;
1002
1003 hres = DrawNCPreview(hdcMem,
1004 DNCP_DRAW_ALL,
1005 prcWindow,
1006 pSelectedTheme->Theme->ThemeFileName,
1007 pSelectedTheme->Color->StyleName,
1008 pSelectedTheme->Size->StyleName,
1009 &scheme->ncMetrics,
1010 scheme->crColor);
1011
1012 return SUCCEEDED(hres);
1013 }
1014
ActivateThemeFile(LPCWSTR pwszFile)1015 BOOL ActivateThemeFile(LPCWSTR pwszFile)
1016 {
1017 PTHEME pThemes;
1018 THEME_SELECTION selection;
1019 COLOR_SCHEME scheme;
1020 BOOL ret = FALSE;
1021
1022 pThemes = LoadThemes();
1023 if (!pThemes)
1024 return FALSE;
1025
1026 LoadCurrentScheme(&scheme);
1027
1028 if (pwszFile)
1029 {
1030 ret = FindOrAppendTheme(pThemes, pwszFile, NULL, NULL, &selection);
1031 if (!ret)
1032 goto cleanup;
1033
1034 ret = LoadSchemeFromTheme(&scheme, &selection);
1035 if (!ret)
1036 goto cleanup;
1037 }
1038 else
1039 {
1040 ret = GetActiveClassicTheme(pThemes, &selection);
1041 if (!ret)
1042 goto cleanup;
1043
1044 ret = LoadSchemeFromReg(&scheme, &selection);
1045 if (!ret)
1046 goto cleanup;
1047 }
1048
1049 ret = ActivateTheme(&selection);
1050 if (!ret)
1051 goto cleanup;
1052
1053 ApplyScheme(&scheme, &selection);
1054
1055 ret = TRUE;
1056
1057 cleanup:
1058 CleanupThemes(pThemes);
1059
1060 return ret;
1061 }
1062
1063 /* TODO: */
1064 INT_PTR CALLBACK
ThemesPageProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)1065 ThemesPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1066 {
1067 LPNMHDR lpnm;
1068
1069 switch (uMsg)
1070 {
1071 case WM_INITDIALOG:
1072 break;
1073
1074 case WM_COMMAND:
1075 break;
1076
1077 case WM_NOTIFY:
1078 lpnm = (LPNMHDR)lParam;
1079 switch (lpnm->code)
1080 {
1081 case PSN_APPLY:
1082 SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)TEXT(""));
1083 return TRUE;
1084 }
1085 break;
1086
1087 case WM_DESTROY:
1088 break;
1089 }
1090
1091 return FALSE;
1092 }
1093