xref: /reactos/dll/cpl/desk/theme.c (revision 118869f6)
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