[DESK] Fix theme preview for Win7. Patch by Carlo Bramini. CORE-10076 #resolve #comme...
[reactos.git] / reactos / 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_SETACTIVEWINDOWTRACKING, 0, IntToPtr(scheme->Effects.bActiveWindowTracking));
415 // SYS_CONFIG(SPI_SETCOMBOBOXANIMATION, 0, IntToPtr(scheme->Effects.bComboBoxAnimation));
416 // SYS_CONFIG(SPI_SETLISTBOXSMOOTHSCROLLING, 0, IntToPtr(scheme->Effects.bListBoxSmoothScrolling));
417 // SYS_CONFIG(SPI_SETGRADIENTCAPTIONS, 0, IntToPtr(scheme->Effects.bGradientCaptions));
418 // SYS_CONFIG(SPI_SETACTIVEWNDTRKZORDER, 0, IntToPtr(scheme->Effects.bActiveWndTrkZorder));
419 // SYS_CONFIG(SPI_SETHOTTRACKING, 0, IntToPtr(scheme->Effects.bHotTracking));
420 // SYS_CONFIG(SPI_SETSELECTIONFADE, 0, IntToPtr(scheme->Effects.bSelectionFade));
421 SYS_CONFIG(SPI_SETTOOLTIPANIMATION, 0, IntToPtr(scheme->Effects.bTooltipAnimation));
422 SYS_CONFIG(SPI_SETTOOLTIPFADE, 0, IntToPtr(scheme->Effects.bTooltipFade));
423 // SYS_CONFIG(SPI_SETCURSORSHADOW, 0, IntToPtr(scheme->Effects.bCursorShadow));
424 // SYS_CONFIG(SPI_SETUIEFFECTS, 0, IntToPtr(scheme->Effects.bUiEffects));
425
426 #undef SYS_CONFIG
427
428 /* Save SchemeId in the registry */
429 if (pSelectedTheme->Theme != NULL && pSelectedTheme->ThemeActive == FALSE)
430 {
431 StyleName = pSelectedTheme->Color->StyleName;
432 SHSetValueW(HKEY_CURRENT_USER,
433 g_CPANewSchemes,
434 g_SelectedStyle,
435 REG_SZ,
436 StyleName,
437 (lstrlenW(StyleName) + 1) * sizeof(WCHAR));
438 }
439 }
440
441 static THEME*
442 CreateTheme(LPCWSTR pszName, LPCWSTR pszDisplayName)
443 {
444 PTHEME pTheme;
445
446 pTheme = (PTHEME) malloc(sizeof(THEME));
447 if (pTheme == NULL) return NULL;
448
449 pTheme->DisplayName = _wcsdup(pszDisplayName);
450 if (pTheme->DisplayName == NULL)
451 {
452 free(pTheme);
453 return NULL;
454 }
455
456 pTheme->ColoursList = NULL;
457 pTheme->NextTheme = NULL;
458 pTheme->SizesList = NULL;
459
460 if (pszName == NULL)
461 {
462 pTheme->ThemeFileName = NULL;
463 return pTheme;
464 }
465
466 pTheme->ThemeFileName = _wcsdup(pszName);
467 if (pTheme->ThemeFileName == NULL)
468 {
469 free(pTheme->DisplayName);
470 free(pTheme);
471 return NULL;
472 }
473
474 return pTheme;
475 }
476
477 static PTHEME_STYLE
478 CreateStyle(LPCWSTR pszName, LPCWSTR pszDisplayName)
479 {
480 PTHEME_STYLE pStyle;
481
482 pStyle = (PTHEME_STYLE) malloc(sizeof(THEME_STYLE));
483 if (pStyle == NULL) return NULL;
484
485 pStyle->StyleName = _wcsdup(pszName);
486 if (pStyle->StyleName == NULL)
487 {
488 free(pStyle);
489 return NULL;
490 }
491
492 pStyle->DisplayName = _wcsdup(pszDisplayName);
493 if (pStyle->DisplayName == NULL)
494 {
495 free(pStyle->StyleName);
496 free(pStyle);
497 return NULL;
498 }
499
500 pStyle->ChildStyle = NULL;
501 pStyle->NextStyle = NULL;
502
503 return pStyle;
504 }
505
506 static void
507 CleanupStyles(IN PTHEME_STYLE pStylesList)
508 {
509 PTHEME_STYLE pStyle, pStyleOld;
510
511 pStyle = pStylesList;
512 while (pStyle)
513 {
514 if (pStyle->ChildStyle) CleanupStyles(pStyle->ChildStyle);
515 if (pStyle->DisplayName) free(pStyle->DisplayName);
516 if (pStyle->StyleName) free(pStyle->StyleName);
517
518 pStyleOld = pStyle;
519 pStyle = pStyle->NextStyle;
520 free(pStyleOld);
521 }
522 }
523
524 void
525 CleanupThemes(IN PTHEME pThemeList)
526 {
527 PTHEME pTheme, pThemeOld;
528
529 pTheme = pThemeList;
530 while (pTheme)
531 {
532 CleanupStyles(pTheme->ColoursList);
533 if (pTheme->SizesList) CleanupStyles(pTheme->SizesList);
534 if (pTheme->DisplayName) free(pTheme->DisplayName);
535 if (pTheme->ThemeFileName) free(pTheme->ThemeFileName);
536
537 pThemeOld = pTheme;
538 pTheme = pTheme->NextTheme;
539 free(pThemeOld);
540 }
541 }
542
543 static PTHEME_STYLE
544 FindStyle(IN PTHEME_STYLE pStylesList, IN PCWSTR StyleName)
545 {
546 PTHEME_STYLE pStyle;
547
548 for (pStyle = pStylesList; pStyle; pStyle = pStyle->NextStyle)
549 {
550 if (_wcsicmp(pStyle->StyleName, StyleName) == 0)
551 {
552 return pStyle;
553 }
554 }
555
556 /* If we can't find the style requested, return the first one */
557 return pStylesList;
558 }
559
560 /*
561 * LoadSchemeSizes: Returns a list of sizes from the registry key of a scheme
562 */
563 static PTHEME_STYLE
564 LoadSchemeSizes(IN HKEY hkScheme)
565 {
566 HKEY hkSizes, hkSize;
567 INT Result;
568 INT iStyle;
569 WCHAR wstrSizeName[5], wstrDisplayName[50];
570 THEME_STYLE *List = NULL, *pCurrentStyle;
571
572 Result = RegOpenKeyW(hkScheme, L"Sizes", &hkSizes);
573 if (Result != ERROR_SUCCESS) return NULL;
574
575 iStyle = 0;
576 while ((RegEnumKeyW(hkSizes, iStyle, wstrSizeName, 5) == ERROR_SUCCESS))
577 {
578 iStyle++;
579
580 Result = RegOpenKeyW(hkSizes, wstrSizeName, &hkSize);
581 if (Result != ERROR_SUCCESS) continue;
582
583 Result = RegLoadMUIStringW(hkSize,
584 L"DisplayName",
585 wstrDisplayName,
586 sizeof(wstrDisplayName),
587 NULL,
588 0,
589 NULL);
590 if (Result != ERROR_SUCCESS)
591 {
592 Result = RegLoadMUIStringW(hkSize,
593 L"LegacyName",
594 wstrDisplayName,
595 sizeof(wstrDisplayName),
596 NULL,
597 0,
598 NULL);
599 }
600
601 if (Result == ERROR_SUCCESS)
602 pCurrentStyle = CreateStyle(wstrSizeName, wstrDisplayName);
603 else
604 pCurrentStyle = CreateStyle(wstrSizeName, wstrSizeName);
605
606 if (pCurrentStyle != NULL)
607 {
608 pCurrentStyle->NextStyle = List;
609 List = pCurrentStyle;
610 }
611
612 RegCloseKey(hkSize);
613 }
614
615 RegCloseKey(hkSizes);
616 return List;
617 }
618
619 /*
620 * LoadClassicColorSchemes: Returns a list of classic theme colours from the registry key of a scheme
621 */
622 static THEME_STYLE*
623 LoadClassicColorSchemes(VOID)
624 {
625 INT Result;
626 HKEY hkNewSchemes, hkScheme;
627 INT iStyle;
628 WCHAR wstrStyleName[5], wstrDisplayName[50];
629 THEME_STYLE *List = NULL, *pCurrentStyle;
630
631 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes);
632 if (Result != ERROR_SUCCESS) return NULL;
633
634 iStyle = 0;
635 while ((RegEnumKeyW(hkNewSchemes, iStyle, wstrStyleName, 5) == ERROR_SUCCESS))
636 {
637 iStyle++;
638
639 Result = RegOpenKeyW(hkNewSchemes, wstrStyleName, &hkScheme);
640 if (Result != ERROR_SUCCESS) continue;
641
642 Result = RegLoadMUIStringW(hkScheme,
643 L"DisplayName",
644 wstrDisplayName,
645 sizeof(wstrDisplayName),
646 NULL,
647 0,
648 NULL);
649 if (Result != ERROR_SUCCESS)
650 {
651 Result = RegLoadMUIStringW(hkScheme,
652 L"LegacyName",
653 wstrDisplayName,
654 sizeof(wstrDisplayName),
655 NULL,
656 0,
657 NULL);
658 }
659
660 if (Result == ERROR_SUCCESS)
661 pCurrentStyle = CreateStyle(wstrStyleName, wstrDisplayName);
662 else
663 pCurrentStyle = CreateStyle(wstrStyleName, wstrStyleName);
664
665 if (pCurrentStyle != NULL)
666 {
667 pCurrentStyle->NextStyle = List;
668 pCurrentStyle->ChildStyle = LoadSchemeSizes(hkScheme);
669 if(pCurrentStyle->ChildStyle == NULL)
670 CleanupStyles(pCurrentStyle);
671 else
672 List = pCurrentStyle;
673 }
674
675 RegCloseKey(hkScheme);
676 }
677
678 RegCloseKey(hkNewSchemes);
679 return List;
680 }
681
682 typedef HRESULT (WINAPI *ENUMTHEMESTYLE) (LPCWSTR, LPWSTR, DWORD, PTHEMENAMES);
683
684 static THEME_STYLE*
685 EnumThemeStyles(IN LPCWSTR pszThemeFileName, IN ENUMTHEMESTYLE pfnEnumTheme)
686 {
687 DWORD index = 0;
688 THEMENAMES names;
689 THEME_STYLE *List = NULL, **ppPrevStyle, *pCurrentStyle;
690
691 ppPrevStyle = &List;
692
693 while (SUCCEEDED(pfnEnumTheme (pszThemeFileName, NULL, index++, &names)))
694 {
695 pCurrentStyle = CreateStyle(names.szName, names.szDisplayName);
696 if(pCurrentStyle == NULL) break;
697
698 *ppPrevStyle = pCurrentStyle;
699 ppPrevStyle = &pCurrentStyle->NextStyle;
700 }
701
702 return List;
703 }
704
705 BOOL CALLBACK
706 EnumThemeProc(IN LPVOID lpReserved,
707 IN LPCWSTR pszThemeFileName,
708 IN LPCWSTR pszThemeName,
709 IN LPCWSTR pszToolTip,
710 IN LPVOID lpReserved2,
711 IN OUT LPVOID lpData)
712 {
713 PTHEME *List, pTheme;
714
715 List = (PTHEME*)lpData;
716
717 pTheme = CreateTheme(pszThemeFileName, pszThemeName);
718 if (pTheme == NULL) return FALSE;
719
720 pTheme->SizesList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeSizes);
721 pTheme->ColoursList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeColors);
722 if(pTheme->SizesList == NULL || pTheme->ColoursList == NULL)
723 {
724 CleanupThemes(pTheme);
725 return FALSE;
726 }
727
728 pTheme->NextTheme = *List;
729 *List = pTheme;
730
731 return TRUE;
732 }
733
734 /*
735 * LoadThemes: Returns a list that contains tha classic theme and
736 * the visual styles of the system
737 */
738 PTHEME
739 LoadThemes(VOID)
740 {
741 HRESULT hret;
742 PTHEME pClassicTheme;
743 WCHAR strClassicTheme[40];
744 WCHAR szThemesPath[MAX_PATH], *pszClassicTheme;
745 int res;
746
747 /* Insert the classic theme */
748 res = LoadString(hApplet, IDS_CLASSIC_THEME, strClassicTheme, 40);
749 pszClassicTheme = (res > 0 ? strClassicTheme : L"Classic Theme");
750 pClassicTheme = CreateTheme(NULL, pszClassicTheme);
751 if (pClassicTheme == NULL) return NULL;
752 pClassicTheme->ColoursList = LoadClassicColorSchemes();
753
754 /* Get path to themes folder */
755 ZeroMemory(szThemesPath, sizeof(szThemesPath));
756 hret = SHGetFolderPathW (NULL, CSIDL_RESOURCES, NULL, SHGFP_TYPE_DEFAULT, szThemesPath);
757 if (FAILED(hret)) return pClassicTheme;
758 lstrcatW (szThemesPath, L"\\Themes");
759
760 /* Enumerate themes */
761 hret = EnumThemes( szThemesPath, EnumThemeProc, &pClassicTheme->NextTheme);
762 if (FAILED(hret))
763 {
764 pClassicTheme->NextTheme = NULL;
765 if (pClassicTheme->ColoursList == NULL)
766 {
767 free(pClassicTheme->DisplayName);
768 free(pClassicTheme);
769 return NULL;
770 }
771 }
772
773 return pClassicTheme;
774 }
775
776 /*
777 * GetActiveTheme: Gets the active theme and populates pSelectedTheme
778 * with entries from the list of loaded themes
779 */
780 BOOL
781 GetActiveTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme)
782 {
783 WCHAR szThemeFileName[MAX_PATH];
784 WCHAR szColorBuff[MAX_PATH];
785 WCHAR szSizeBuff[MAX_PATH];
786 PTHEME pTheme;
787 HRESULT hret;
788
789 ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION));
790
791 /* Retrieve the name of the current theme */
792 hret = GetCurrentThemeName(szThemeFileName,
793 MAX_PATH,
794 szColorBuff,
795 MAX_PATH,
796 szSizeBuff,
797 MAX_PATH);
798 if (FAILED(hret)) return FALSE;
799
800 for (pTheme = pThemeList; pTheme; pTheme = pTheme->NextTheme)
801 {
802 if (pTheme->ThemeFileName &&
803 _wcsicmp(pTheme->ThemeFileName, szThemeFileName) == 0)
804 {
805 break;
806 }
807 }
808
809 if (pTheme == NULL) return FALSE;
810
811 pSelectedTheme->ThemeActive = TRUE;
812 pSelectedTheme->Theme = pTheme;
813 pSelectedTheme->Color = FindStyle(pTheme->ColoursList, szColorBuff);
814 pSelectedTheme->Size = FindStyle(pTheme->SizesList, szSizeBuff);
815
816 return TRUE;
817 }
818
819 /*
820 * GetActiveTheme: Gets the active classic theme and populates pSelectedTheme
821 * with entries from the list of loaded themes
822 */
823 BOOL
824 GetActiveClassicTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme)
825 {
826 INT Result;
827 WCHAR szSelectedClassicScheme[5], szSelectedClassicSize[5];
828 HKEY hkNewSchemes;
829 DWORD dwType, dwDisplayNameLength;
830 PTHEME_STYLE pCurrentStyle, pCurrentSize;
831
832 ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION));
833
834 /* Assume failure */
835 szSelectedClassicScheme[0] = 0;
836 szSelectedClassicSize[0] = 0;
837
838 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes);
839 if (Result != ERROR_SUCCESS) return FALSE;
840
841 dwType = REG_SZ;
842 dwDisplayNameLength = sizeof(szSelectedClassicScheme);
843 Result = RegQueryValueEx(hkNewSchemes, L"SelectedStyle", NULL, &dwType,
844 (LPBYTE)&szSelectedClassicScheme, &dwDisplayNameLength);
845 if (Result == ERROR_SUCCESS)
846 {
847 dwType = REG_SZ;
848 dwDisplayNameLength = sizeof(szSelectedClassicSize);
849 Result = SHGetValue(hkNewSchemes, szSelectedClassicScheme, L"SelectedSize",
850 &dwType, szSelectedClassicSize, &dwDisplayNameLength);
851 }
852
853 RegCloseKey(hkNewSchemes);
854
855 pCurrentStyle = FindStyle(pThemeList->ColoursList, szSelectedClassicScheme);
856 pCurrentSize = FindStyle(pCurrentStyle->ChildStyle, szSelectedClassicSize);
857
858 pSelectedTheme->Theme = pThemeList;
859 pSelectedTheme->Color = pCurrentStyle;
860 pSelectedTheme->Size = pCurrentSize;
861
862 return TRUE;
863 }
864
865 BOOL
866 ActivateTheme(IN PTHEME_SELECTION pSelectedTheme)
867 {
868 HTHEMEFILE hThemeFile = 0;
869 HRESULT hret;
870
871 if (pSelectedTheme->ThemeActive)
872 {
873 hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName,
874 pSelectedTheme->Color->StyleName,
875 pSelectedTheme->Size->StyleName,
876 &hThemeFile,
877 0);
878
879 if (!SUCCEEDED(hret)) return FALSE;
880 }
881
882 hret = ApplyTheme(hThemeFile, "", 0);
883
884 if (pSelectedTheme->ThemeActive)
885 {
886 CloseThemeFile(hThemeFile);
887 }
888
889 return SUCCEEDED(hret);
890 }
891
892 BOOL
893 LoadSchemeFromTheme(OUT PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme)
894 {
895 HTHEMEFILE hThemeFile = 0;
896 HRESULT hret;
897 HTHEME hTheme;
898 int i;
899
900 hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName,
901 pSelectedTheme->Color->StyleName,
902 pSelectedTheme->Size->StyleName,
903 &hThemeFile,
904 0);
905
906 if (!SUCCEEDED(hret)) return FALSE;
907
908 hTheme = OpenThemeDataFromFile(hThemeFile, hCPLWindow, L"WINDOW", 0);
909 if (hTheme == NULL) return FALSE;
910
911 /* Load colors */
912 for (i = 0; i < NUM_COLORS; i++)
913 {
914 scheme->crColor[i] = GetThemeSysColor(hTheme,i);
915 }
916
917 /* Load sizes */
918 /* I wonder why GetThemeSysInt doesn't work here */
919 scheme->ncMetrics.iBorderWidth = GetThemeSysSize(hTheme, SM_CXFRAME);
920 scheme->ncMetrics.iScrollWidth = GetThemeSysSize(hTheme, SM_CXVSCROLL);
921 scheme->ncMetrics.iScrollHeight = GetThemeSysSize(hTheme, SM_CYHSCROLL);
922 scheme->ncMetrics.iCaptionWidth = GetThemeSysSize(hTheme, SM_CXSIZE);
923 scheme->ncMetrics.iCaptionHeight = GetThemeSysSize(hTheme, SM_CYSIZE);
924 scheme->ncMetrics.iSmCaptionWidth = GetThemeSysSize(hTheme, SM_CXSMSIZE);
925 scheme->ncMetrics.iSmCaptionHeight = GetThemeSysSize(hTheme, SM_CYSMSIZE);
926 scheme->ncMetrics.iMenuWidth = GetThemeSysSize(hTheme, SM_CXMENUSIZE);
927 scheme->ncMetrics.iMenuHeight = GetThemeSysSize(hTheme, SM_CYMENUSIZE);
928
929 /* Load fonts */
930 GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &scheme->ncMetrics.lfCaptionFont);
931 GetThemeSysFont(hTheme, TMT_SMALLCAPTIONFONT, &scheme->ncMetrics.lfSmCaptionFont);
932 GetThemeSysFont(hTheme, TMT_MENUFONT, &scheme->ncMetrics.lfMenuFont );
933 GetThemeSysFont(hTheme, TMT_STATUSFONT, &scheme->ncMetrics.lfStatusFont);
934 GetThemeSysFont(hTheme, TMT_MSGBOXFONT, &scheme->ncMetrics.lfMessageFont);
935 GetThemeSysFont(hTheme, TMT_ICONTITLEFONT, &scheme->icMetrics.lfFont);
936
937 CloseThemeData(hTheme);
938
939 return TRUE;
940 }
941
942 BOOL
943 DrawThemePreview(IN HDC hdcMem, IN PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme, IN PRECT prcWindow)
944 {
945 HBRUSH hbrBack;
946 HRESULT hres;
947
948 hbrBack = CreateSolidBrush(scheme->crColor[COLOR_DESKTOP]);
949
950 FillRect(hdcMem, prcWindow, hbrBack);
951 DeleteObject(hbrBack);
952
953 InflateRect(prcWindow, -10, -10);
954
955 hres = DrawNCPreview(hdcMem,
956 DNCP_DRAW_ALL,
957 prcWindow,
958 pSelectedTheme->Theme->ThemeFileName,
959 pSelectedTheme->Color->StyleName,
960 pSelectedTheme->Size->StyleName,
961 &scheme->ncMetrics,
962 scheme->crColor);
963
964 return SUCCEEDED(hres);
965 }