[WINHTTP] Sync with Wine Staging 3.9. CORE-14656
[reactos.git] / dll / cpl / desk / theme.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Display Control Panel
4 * FILE: 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 }