xref: /reactos/dll/cpl/desk/theme.c (revision 177ae91b)
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
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
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
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
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
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
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*
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
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
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
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
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
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*
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*
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 
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
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
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
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
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
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
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
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
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, -10, -10);
1001 
1002     hres = DrawNCPreview(hdcMem,
1003                          DNCP_DRAW_ALL,
1004                          prcWindow,
1005                          pSelectedTheme->Theme->ThemeFileName,
1006                          pSelectedTheme->Color->StyleName,
1007                          pSelectedTheme->Size->StyleName,
1008                          &scheme->ncMetrics,
1009                          scheme->crColor);
1010 
1011     return SUCCEEDED(hres);
1012 }
1013 
1014 BOOL ActivateThemeFile(LPCWSTR pwszFile)
1015 {
1016     PTHEME pThemes;
1017     THEME_SELECTION selection;
1018     COLOR_SCHEME scheme;
1019     BOOL ret = FALSE;
1020 
1021     pThemes = LoadThemes();
1022     if (!pThemes)
1023         return FALSE;
1024 
1025     LoadCurrentScheme(&scheme);
1026 
1027     if (pwszFile)
1028     {
1029         ret = FindOrAppendTheme(pThemes, pwszFile, NULL, NULL, &selection);
1030         if (!ret)
1031             goto cleanup;
1032 
1033         ret = LoadSchemeFromTheme(&scheme, &selection);
1034         if (!ret)
1035             goto cleanup;
1036     }
1037     else
1038     {
1039         ret = GetActiveClassicTheme(pThemes, &selection);
1040         if (!ret)
1041             goto cleanup;
1042 
1043         ret = LoadSchemeFromReg(&scheme, &selection);
1044         if (!ret)
1045             goto cleanup;
1046     }
1047 
1048     ret = ActivateTheme(&selection);
1049     if (!ret)
1050         goto cleanup;
1051 
1052     ApplyScheme(&scheme, &selection);
1053 
1054     ret = TRUE;
1055 
1056 cleanup:
1057     CleanupThemes(pThemes);
1058 
1059     return ret;
1060 }
1061 
1062 /* TODO: */
1063 INT_PTR CALLBACK
1064 ThemesPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1065 {
1066     LPNMHDR lpnm;
1067 
1068     switch (uMsg)
1069     {
1070         case WM_INITDIALOG:
1071             break;
1072 
1073         case WM_COMMAND:
1074             break;
1075 
1076         case WM_NOTIFY:
1077             lpnm = (LPNMHDR)lParam;
1078             switch (lpnm->code)
1079             {
1080                 case PSN_APPLY:
1081                     SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)TEXT(""));
1082                     return TRUE;
1083             }
1084             break;
1085 
1086         case WM_DESTROY:
1087             break;
1088     }
1089 
1090     return FALSE;
1091 }
1092