151913a71442e1febf11c44bcf7cf09352233ecc
[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
128 /* Load colors */
129 for (i = 0; i < NUM_COLORS; i++)
130 {
131 scheme->crColor[i] = (COLORREF)GetSysColor(i);
132 }
133
134 /* Load non client metrics */
135 scheme->ncMetrics.cbSize = sizeof(NONCLIENTMETRICSW);
136 ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
137 sizeof(NONCLIENTMETRICSW),
138 &scheme->ncMetrics,
139 0);
140 if (!ret) return FALSE;
141
142 /* Load icon metrics */
143 scheme->icMetrics.cbSize = sizeof(ICONMETRICSW);
144 ret = SystemParametersInfoW(SPI_GETICONMETRICS,
145 sizeof(ICONMETRICSW),
146 &scheme->icMetrics,
147 0);
148 if (!ret) return FALSE;
149
150 /* Load flat menu style */
151 ret = SystemParametersInfoW(SPI_GETFLATMENU,
152 0,
153 &scheme->bFlatMenus,
154 0);
155 if (!ret) return FALSE;
156
157 /* Effects */
158 /* Use the following transition effect for menus and tooltips */
159 ret = SystemParametersInfoW(SPI_GETMENUANIMATION,
160 0,
161 &scheme->Effects.bMenuAnimation,
162 0);
163 if (!ret) return FALSE;
164
165 ret = SystemParametersInfoW(SPI_GETMENUFADE,
166 0,
167 &scheme->Effects.bMenuFade,
168 0);
169 if (!ret) return FALSE;
170
171 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings
172 * Just keep them in sync for now:
173 */
174 scheme->Effects.bTooltipAnimation = scheme->Effects.bMenuAnimation;
175 scheme->Effects.bTooltipFade = scheme->Effects.bMenuFade;
176
177 /* Use the following transition effect for menus and tooltips */
178 ret = SystemParametersInfoW(SPI_GETFONTSMOOTHING,
179 0,
180 &scheme->Effects.bFontSmoothing,
181 0);
182 if (!ret) return FALSE;
183
184 ret = SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE,
185 0,
186 &scheme->Effects.uiFontSmoothingType,
187 0);
188 if (!ret) return FALSE;
189
190 /* Show shadows under menus */
191 ret = SystemParametersInfoW(SPI_GETDROPSHADOW,
192 0,
193 &scheme->Effects.bDropShadow,
194 0);
195 if (!ret) return FALSE;
196
197 /* Show content of windows during dragging */
198 ret = SystemParametersInfoW(SPI_GETDRAGFULLWINDOWS,
199 0,
200 &scheme->Effects.bDragFullWindows,
201 0);
202 if (!ret) return FALSE;
203
204 /* Hide underlined letters for keyboard navigation until the Alt key is pressed */
205 ret = SystemParametersInfoW(SPI_GETKEYBOARDCUES,
206 0,
207 &scheme->Effects.bKeyboardCues,
208 0);
209 if (!ret) return FALSE;
210
211 /* Read the icon size from registry */
212 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPMetrics, &hKey);
213 if(Result == ERROR_SUCCESS)
214 {
215 scheme->iIconSize = SHRegGetIntW(hKey, L"Shell Icon Size", 32);
216 RegCloseKey(hKey);
217 }
218
219 return TRUE;
220 }
221
222 /*
223 * LoadSchemeFromReg: Populates the passed scheme with values retrieved from registry
224 */
225 BOOL
226 LoadSchemeFromReg(OUT COLOR_SCHEME *scheme, IN PTHEME_SELECTION pSelectedTheme)
227 {
228 INT i;
229 WCHAR strValueName[10], strSchemeKey[MAX_PATH];
230 HKEY hkScheme = NULL;
231 DWORD dwType, dwLength;
232 UINT64 iSize;
233 BOOL Ret = TRUE;
234 LONG result;
235
236 wsprintf(strSchemeKey, L"%s\\%s\\Sizes\\%s",
237 g_CPANewSchemes,
238 pSelectedTheme->Color->StyleName,
239 pSelectedTheme->Size->StyleName);
240
241 result = RegOpenKeyW(HKEY_CURRENT_USER, strSchemeKey, &hkScheme);
242 if (result != ERROR_SUCCESS) return FALSE;
243
244 scheme->bFlatMenus = SHRegGetIntW(hkScheme, L"FlatMenus", 0);
245
246 for (i = 0; i < NUM_COLORS; i++)
247 {
248 wsprintf(strValueName, L"Color #%d", i);
249 dwLength = sizeof(COLORREF);
250 result = RegQueryValueExW(hkScheme,
251 strValueName,
252 NULL,
253 &dwType,
254 (LPBYTE)&scheme->crColor[i],
255 &dwLength);
256 if (result != ERROR_SUCCESS || dwType != REG_DWORD)
257 {
258 /* Failed to read registry value, initialize with current setting for now */
259 scheme->crColor[i] = GetSysColor(i);
260 }
261 }
262
263 for (i = 0; i < NUM_FONTS; i++)
264 {
265 PLOGFONTW lpfFont = SchemeGetFont(scheme, i);
266
267 wsprintf(strValueName, L"Font #%d", i);
268 dwLength = sizeof(LOGFONT);
269 result = RegQueryValueExW(hkScheme,
270 strValueName,
271 NULL,
272 &dwType,
273 (LPBYTE)lpfFont,
274 &dwLength);
275 if (result != ERROR_SUCCESS || dwType != REG_BINARY ||
276 dwLength != sizeof(LOGFONT))
277 {
278 /* Failed to read registry value */
279 Ret = FALSE;
280 }
281 }
282
283 for (i = 0; i < NUM_SIZES; i++)
284 {
285 wsprintf(strValueName, L"Size #%d", i);
286 dwLength = sizeof(UINT64);
287 result = RegQueryValueExW(hkScheme,
288 strValueName,
289 NULL,
290 &dwType,
291 (LPBYTE)&iSize,
292 &dwLength);
293 if (result != ERROR_SUCCESS || dwType != REG_QWORD ||
294 dwLength != sizeof(UINT64))
295 {
296 /* Failed to read registry value, initialize with current setting for now */
297 }
298 else
299 {
300 SchemeSetMetric(scheme, i, (int)iSize);
301 }
302 }
303
304 RegCloseKey(hkScheme);
305
306 return Ret;
307 }
308
309 /*
310 * ApplyScheme: Applies the selected scheme and stores its id in the registry if needed
311 */
312 VOID
313 ApplyScheme(IN COLOR_SCHEME *scheme, IN PTHEME_SELECTION pSelectedTheme)
314 {
315 INT i, Result;
316 HKEY hKey;
317 WCHAR clText[16], *StyleName;
318 INT ColorList[NUM_COLORS];
319
320 /* Apply system colors */
321 for (i = 0; i < NUM_COLORS; i++)
322 ColorList[i] = i;
323 SetSysColors(NUM_COLORS, ColorList, scheme->crColor);
324
325 /* Save colors to registry */
326 Result = RegCreateKeyW(HKEY_CURRENT_USER, g_CPColors, &hKey);
327 if (Result == ERROR_SUCCESS)
328 {
329 for (i = 0; i < NUM_COLORS; i++)
330 {
331 wsprintf(clText,
332 L"%d %d %d",
333 GetRValue(scheme->crColor[i]),
334 GetGValue(scheme->crColor[i]),
335 GetBValue(scheme->crColor[i]));
336
337 RegSetValueExW(hKey,
338 g_RegColorNames[i],
339 0,
340 REG_SZ,
341 (BYTE *)clText,
342 (lstrlen(clText) + 1) * sizeof(WCHAR));
343 }
344 RegCloseKey(hKey);
345 }
346
347 /* Apply non client metrics */
348 SystemParametersInfoW(SPI_SETNONCLIENTMETRICS,
349 sizeof(NONCLIENTMETRICS),
350 &scheme->ncMetrics,
351 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
352
353 /* Apply icon metrics */
354 SystemParametersInfoW(SPI_SETICONMETRICS,
355 sizeof(ICONMETRICS),
356 &scheme->icMetrics,
357 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
358
359 /* Effects, save only when needed: */
360 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings
361 * Just keep them in sync for now.
362 */
363
364 #define SYS_CONFIG(__uiAction, __uiParam, __pvParam) \
365 SystemParametersInfoW(__uiAction, __uiParam, __pvParam, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE)
366
367 scheme->Effects.bTooltipAnimation = scheme->Effects.bMenuAnimation;
368 scheme->Effects.bTooltipFade = scheme->Effects.bMenuFade;
369
370 /* Use the following transition effect for menus and tooltips */
371 SYS_CONFIG(SPI_SETMENUANIMATION, 0, IntToPtr(scheme->Effects.bMenuAnimation));
372 SYS_CONFIG(SPI_SETMENUFADE, 0, IntToPtr(scheme->Effects.bMenuFade));
373
374 /* Use the following method to smooth edges of screen fonts */
375 SYS_CONFIG(SPI_SETFONTSMOOTHING, scheme->Effects.bFontSmoothing, 0);
376 SYS_CONFIG(SPI_SETFONTSMOOTHINGTYPE, 0, IntToPtr(scheme->Effects.uiFontSmoothingType));
377
378 /*
379 * Refresh and redraw all the windows, otherwise the font smoothing changes
380 * only appear after any future partial region invalidation.
381 * Not everyone listens for this WM_SETTINGCHANGE, including the shell and most third party programs.
382 */
383 InvalidateRect(NULL, NULL, TRUE);
384
385 /* Use large icons */
386 //SYS_CONFIG(SPI_GETDRAGFULLWINDOWS, (PVOID) g->SchemeAdv.Effects.bMenuFade);
387
388 /* Show shadows under menus */
389 SYS_CONFIG(SPI_SETDROPSHADOW, 0, IntToPtr(scheme->Effects.bDropShadow));
390
391 /* Show window contents while dragging */
392 SYS_CONFIG(SPI_SETDRAGFULLWINDOWS, scheme->Effects.bDragFullWindows, 0);
393
394 /* Hide underlined letters for keyboard navigation until I press the Alt key */
395 SYS_CONFIG(SPI_SETKEYBOARDCUES, 0, IntToPtr(scheme->Effects.bKeyboardCues));
396
397 // SYS_CONFIG(SPI_SETACTIVEWINDOWTRACKING, 0, IntToPtr(scheme->Effects.bActiveWindowTracking));
398 // SYS_CONFIG(SPI_SETCOMBOBOXANIMATION, 0, IntToPtr(scheme->Effects.bComboBoxAnimation));
399 // SYS_CONFIG(SPI_SETLISTBOXSMOOTHSCROLLING, 0, IntToPtr(scheme->Effects.bListBoxSmoothScrolling));
400 // SYS_CONFIG(SPI_SETGRADIENTCAPTIONS, 0, IntToPtr(scheme->Effects.bGradientCaptions));
401 // SYS_CONFIG(SPI_SETACTIVEWNDTRKZORDER, 0, IntToPtr(scheme->Effects.bActiveWndTrkZorder));
402 // SYS_CONFIG(SPI_SETHOTTRACKING, 0, IntToPtr(scheme->Effects.bHotTracking));
403 // SYS_CONFIG(SPI_SETSELECTIONFADE, 0, IntToPtr(scheme->Effects.bSelectionFade));
404 SYS_CONFIG(SPI_SETTOOLTIPANIMATION, 0, IntToPtr(scheme->Effects.bTooltipAnimation));
405 SYS_CONFIG(SPI_SETTOOLTIPFADE, 0, IntToPtr(scheme->Effects.bTooltipFade));
406 // SYS_CONFIG(SPI_SETCURSORSHADOW, 0, IntToPtr(scheme->Effects.bCursorShadow));
407 // SYS_CONFIG(SPI_SETUIEFFECTS, 0, IntToPtr(scheme->Effects.bUiEffects));
408
409 #undef SYS_CONFIG
410
411 /* Save SchemeId in the registry */
412 if (pSelectedTheme->Theme != NULL && pSelectedTheme->ThemeActive == FALSE)
413 {
414 StyleName = pSelectedTheme->Color->StyleName;
415 SHSetValueW(HKEY_CURRENT_USER,
416 g_CPANewSchemes,
417 g_SelectedStyle,
418 REG_SZ,
419 StyleName,
420 (lstrlenW(StyleName) + 1) * sizeof(WCHAR));
421 }
422 }
423
424 static THEME*
425 CreateTheme(LPCWSTR pszName, LPCWSTR pszDisplayName)
426 {
427 PTHEME pTheme;
428
429 pTheme = (PTHEME) malloc(sizeof(THEME));
430 if (pTheme == NULL) return NULL;
431
432 pTheme->DisplayName = _wcsdup(pszDisplayName);
433 if (pTheme->DisplayName == NULL)
434 {
435 free(pTheme);
436 return NULL;
437 }
438
439 pTheme->ColoursList = NULL;
440 pTheme->NextTheme = NULL;
441 pTheme->SizesList = NULL;
442
443 if (pszName == NULL)
444 {
445 pTheme->ThemeFileName = NULL;
446 return pTheme;
447 }
448
449 pTheme->ThemeFileName = _wcsdup(pszName);
450 if (pTheme->ThemeFileName == NULL)
451 {
452 free(pTheme->DisplayName);
453 free(pTheme);
454 return NULL;
455 }
456
457 return pTheme;
458 }
459
460 static PTHEME_STYLE
461 CreateStyle(LPCWSTR pszName, LPCWSTR pszDisplayName)
462 {
463 PTHEME_STYLE pStyle;
464
465 pStyle = (PTHEME_STYLE) malloc(sizeof(THEME_STYLE));
466 if (pStyle == NULL) return NULL;
467
468 pStyle->StyleName = _wcsdup(pszName);
469 if (pStyle->StyleName == NULL)
470 {
471 free(pStyle);
472 return NULL;
473 }
474
475 pStyle->DisplayName = _wcsdup(pszDisplayName);
476 if (pStyle->DisplayName == NULL)
477 {
478 free(pStyle->StyleName);
479 free(pStyle);
480 return NULL;
481 }
482
483 pStyle->ChildStyle = NULL;
484 pStyle->NextStyle = NULL;
485
486 return pStyle;
487 }
488
489 static void
490 CleanupStyles(IN PTHEME_STYLE pStylesList)
491 {
492 PTHEME_STYLE pStyle, pStyleOld;
493
494 pStyle = pStylesList;
495 while (pStyle)
496 {
497 if (pStyle->ChildStyle) CleanupStyles(pStyle->ChildStyle);
498 if (pStyle->DisplayName) free(pStyle->DisplayName);
499 if (pStyle->StyleName) free(pStyle->StyleName);
500
501 pStyleOld = pStyle;
502 pStyle = pStyle->NextStyle;
503 free(pStyleOld);
504 }
505 }
506
507 void
508 CleanupThemes(IN PTHEME pThemeList)
509 {
510 PTHEME pTheme, pThemeOld;
511
512 pTheme = pThemeList;
513 while (pTheme)
514 {
515 CleanupStyles(pTheme->ColoursList);
516 if (pTheme->SizesList) CleanupStyles(pTheme->SizesList);
517 if (pTheme->DisplayName) free(pTheme->DisplayName);
518 if (pTheme->ThemeFileName) free(pTheme->ThemeFileName);
519
520 pThemeOld = pTheme;
521 pTheme = pTheme->NextTheme;
522 free(pThemeOld);
523 }
524 }
525
526 static PTHEME_STYLE
527 FindStyle(IN PTHEME_STYLE pStylesList, IN PCWSTR StyleName)
528 {
529 PTHEME_STYLE pStyle;
530
531 for (pStyle = pStylesList; pStyle; pStyle = pStyle->NextStyle)
532 {
533 if (_wcsicmp(pStyle->StyleName, StyleName) == 0)
534 {
535 return pStyle;
536 }
537 }
538
539 /* If we can't find the style requested, return the first one */
540 return pStylesList;
541 }
542
543 /*
544 * LoadSchemeSizes: Returns a list of sizes from the registry key of a scheme
545 */
546 static PTHEME_STYLE
547 LoadSchemeSizes(IN HKEY hkScheme)
548 {
549 HKEY hkSizes, hkSize;
550 INT Result;
551 INT iStyle;
552 WCHAR wstrSizeName[5], wstrDisplayName[50];
553 THEME_STYLE *List = NULL, *pCurrentStyle;
554
555 Result = RegOpenKeyW(hkScheme, L"Sizes", &hkSizes);
556 if (Result != ERROR_SUCCESS) return NULL;
557
558 iStyle = 0;
559 while ((RegEnumKeyW(hkSizes, iStyle, wstrSizeName, 5) == ERROR_SUCCESS))
560 {
561 iStyle++;
562
563 Result = RegOpenKeyW(hkSizes, wstrSizeName, &hkSize);
564 if (Result != ERROR_SUCCESS) continue;
565
566 Result = RegLoadMUIStringW(hkSize,
567 L"DisplayName",
568 wstrDisplayName,
569 sizeof(wstrDisplayName),
570 NULL,
571 0,
572 NULL);
573 if (Result != ERROR_SUCCESS)
574 {
575 Result = RegLoadMUIStringW(hkSize,
576 L"LegacyName",
577 wstrDisplayName,
578 sizeof(wstrDisplayName),
579 NULL,
580 0,
581 NULL);
582 }
583
584 if (Result == ERROR_SUCCESS)
585 pCurrentStyle = CreateStyle(wstrSizeName, wstrDisplayName);
586 else
587 pCurrentStyle = CreateStyle(wstrSizeName, wstrSizeName);
588
589 if (pCurrentStyle != NULL)
590 {
591 pCurrentStyle->NextStyle = List;
592 List = pCurrentStyle;
593 }
594
595 RegCloseKey(hkSize);
596 }
597
598 RegCloseKey(hkSizes);
599 return List;
600 }
601
602 /*
603 * LoadClassicColorSchemes: Returns a list of classic theme colours from the registry key of a scheme
604 */
605 static THEME_STYLE*
606 LoadClassicColorSchemes(VOID)
607 {
608 INT Result;
609 HKEY hkNewSchemes, hkScheme;
610 INT iStyle;
611 WCHAR wstrStyleName[5], wstrDisplayName[50];
612 THEME_STYLE *List = NULL, *pCurrentStyle;
613
614 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes);
615 if (Result != ERROR_SUCCESS) return NULL;
616
617 iStyle = 0;
618 while ((RegEnumKeyW(hkNewSchemes, iStyle, wstrStyleName, 5) == ERROR_SUCCESS))
619 {
620 iStyle++;
621
622 Result = RegOpenKeyW(hkNewSchemes, wstrStyleName, &hkScheme);
623 if (Result != ERROR_SUCCESS) continue;
624
625 Result = RegLoadMUIStringW(hkScheme,
626 L"DisplayName",
627 wstrDisplayName,
628 sizeof(wstrDisplayName),
629 NULL,
630 0,
631 NULL);
632 if (Result != ERROR_SUCCESS)
633 {
634 Result = RegLoadMUIStringW(hkScheme,
635 L"LegacyName",
636 wstrDisplayName,
637 sizeof(wstrDisplayName),
638 NULL,
639 0,
640 NULL);
641 }
642
643 if (Result == ERROR_SUCCESS)
644 pCurrentStyle = CreateStyle(wstrStyleName, wstrDisplayName);
645 else
646 pCurrentStyle = CreateStyle(wstrStyleName, wstrStyleName);
647
648 if (pCurrentStyle != NULL)
649 {
650 pCurrentStyle->NextStyle = List;
651 pCurrentStyle->ChildStyle = LoadSchemeSizes(hkScheme);
652 if(pCurrentStyle->ChildStyle == NULL)
653 CleanupStyles(pCurrentStyle);
654 else
655 List = pCurrentStyle;
656 }
657
658 RegCloseKey(hkScheme);
659 }
660
661 RegCloseKey(hkNewSchemes);
662 return List;
663 }
664
665 typedef HRESULT (WINAPI *ENUMTHEMESTYLE) (LPCWSTR, LPWSTR, DWORD, PTHEMENAMES);
666
667 static THEME_STYLE*
668 EnumThemeStyles(IN LPCWSTR pszThemeFileName, IN ENUMTHEMESTYLE pfnEnumTheme)
669 {
670 DWORD index = 0;
671 THEMENAMES names;
672 THEME_STYLE *List = NULL, **ppPrevStyle, *pCurrentStyle;
673
674 ppPrevStyle = &List;
675
676 while (SUCCEEDED(pfnEnumTheme (pszThemeFileName, NULL, index++, &names)))
677 {
678 pCurrentStyle = CreateStyle(names.szName, names.szDisplayName);
679 if(pCurrentStyle == NULL) break;
680
681 *ppPrevStyle = pCurrentStyle;
682 ppPrevStyle = &pCurrentStyle->NextStyle;
683 }
684
685 return List;
686 }
687
688 BOOL CALLBACK
689 EnumThemeProc(IN LPVOID lpReserved,
690 IN LPCWSTR pszThemeFileName,
691 IN LPCWSTR pszThemeName,
692 IN LPCWSTR pszToolTip,
693 IN LPVOID lpReserved2,
694 IN OUT LPVOID lpData)
695 {
696 PTHEME *List, pTheme;
697
698 List = (PTHEME*)lpData;
699
700 pTheme = CreateTheme(pszThemeFileName, pszThemeName);
701 if (pTheme == NULL) return FALSE;
702
703 pTheme->SizesList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeSizes);
704 pTheme->ColoursList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeColors);
705 if(pTheme->SizesList == NULL || pTheme->ColoursList == NULL)
706 {
707 CleanupThemes(pTheme);
708 return FALSE;
709 }
710
711 pTheme->NextTheme = *List;
712 *List = pTheme;
713
714 return TRUE;
715 }
716
717 /*
718 * LoadThemes: Returns a list that contains tha classic theme and
719 * the visual styles of the system
720 */
721 PTHEME
722 LoadThemes(VOID)
723 {
724 HRESULT hret;
725 PTHEME pClassicTheme;
726 WCHAR strClassicTheme[40];
727 WCHAR szThemesPath[MAX_PATH], *pszClassicTheme;
728 int res;
729
730 /* Insert the classic theme */
731 res = LoadString(hApplet, IDS_CLASSIC_THEME, strClassicTheme, 40);
732 pszClassicTheme = (res > 0 ? strClassicTheme : L"Classic Theme");
733 pClassicTheme = CreateTheme(NULL, pszClassicTheme);
734 if (pClassicTheme == NULL) return NULL;
735 pClassicTheme->ColoursList = LoadClassicColorSchemes();
736
737 /* Get path to themes folder */
738 ZeroMemory(szThemesPath, sizeof(szThemesPath));
739 hret = SHGetFolderPathW (NULL, CSIDL_RESOURCES, NULL, SHGFP_TYPE_DEFAULT, szThemesPath);
740 if (FAILED(hret)) return pClassicTheme;
741 lstrcatW (szThemesPath, L"\\Themes");
742
743 /* Enumerate themes */
744 hret = EnumThemes( szThemesPath, EnumThemeProc, &pClassicTheme->NextTheme);
745 if (FAILED(hret))
746 {
747 pClassicTheme->NextTheme = NULL;
748 if (pClassicTheme->ColoursList == NULL)
749 {
750 free(pClassicTheme->DisplayName);
751 free(pClassicTheme);
752 return NULL;
753 }
754 }
755
756 return pClassicTheme;
757 }
758
759 /*
760 * GetActiveTheme: Gets the active theme and populates pSelectedTheme
761 * with entries from the list of loaded themes
762 */
763 BOOL
764 GetActiveTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme)
765 {
766 WCHAR szThemeFileName[MAX_PATH];
767 WCHAR szColorBuff[MAX_PATH];
768 WCHAR szSizeBuff[MAX_PATH];
769 PTHEME pTheme;
770 HRESULT hret;
771
772 ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION));
773
774 /* Retrieve the name of the current theme */
775 hret = GetCurrentThemeName(szThemeFileName,
776 MAX_PATH,
777 szColorBuff,
778 MAX_PATH,
779 szSizeBuff,
780 MAX_PATH);
781 if (FAILED(hret)) return FALSE;
782
783 for (pTheme = pThemeList; pTheme; pTheme = pTheme->NextTheme)
784 {
785 if (pTheme->ThemeFileName &&
786 _wcsicmp(pTheme->ThemeFileName, szThemeFileName) == 0)
787 {
788 break;
789 }
790 }
791
792 if (pTheme == NULL) return FALSE;
793
794 pSelectedTheme->ThemeActive = TRUE;
795 pSelectedTheme->Theme = pTheme;
796 pSelectedTheme->Color = FindStyle(pTheme->ColoursList, szColorBuff);
797 pSelectedTheme->Size = FindStyle(pTheme->SizesList, szSizeBuff);
798
799 return TRUE;
800 }
801
802 /*
803 * GetActiveTheme: Gets the active classic theme and populates pSelectedTheme
804 * with entries from the list of loaded themes
805 */
806 BOOL
807 GetActiveClassicTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme)
808 {
809 INT Result;
810 WCHAR szSelectedClassicScheme[5], szSelectedClassicSize[5];
811 HKEY hkNewSchemes;
812 DWORD dwType, dwDisplayNameLength;
813 PTHEME_STYLE pCurrentStyle, pCurrentSize;
814
815 ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION));
816
817 /* Assume failure */
818 szSelectedClassicScheme[0] = 0;
819 szSelectedClassicSize[0] = 0;
820
821 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes);
822 if (Result != ERROR_SUCCESS) return FALSE;
823
824 dwType = REG_SZ;
825 dwDisplayNameLength = sizeof(szSelectedClassicScheme);
826 Result = RegQueryValueEx(hkNewSchemes, L"SelectedStyle", NULL, &dwType,
827 (LPBYTE)&szSelectedClassicScheme, &dwDisplayNameLength);
828 if (Result == ERROR_SUCCESS)
829 {
830 dwType = REG_SZ;
831 dwDisplayNameLength = sizeof(szSelectedClassicSize);
832 Result = SHGetValue(hkNewSchemes, szSelectedClassicScheme, L"SelectedSize",
833 &dwType, szSelectedClassicSize, &dwDisplayNameLength);
834 }
835
836 RegCloseKey(hkNewSchemes);
837
838 pCurrentStyle = FindStyle(pThemeList->ColoursList, szSelectedClassicScheme);
839 pCurrentSize = FindStyle(pCurrentStyle->ChildStyle, szSelectedClassicSize);
840
841 pSelectedTheme->Theme = pThemeList;
842 pSelectedTheme->Color = pCurrentStyle;
843 pSelectedTheme->Size = pCurrentSize;
844
845 return TRUE;
846 }
847
848 BOOL
849 ActivateTheme(IN PTHEME_SELECTION pSelectedTheme)
850 {
851 HTHEMEFILE hThemeFile = 0;
852 HRESULT hret;
853
854 if (pSelectedTheme->ThemeActive)
855 {
856 hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName,
857 pSelectedTheme->Color->StyleName,
858 pSelectedTheme->Size->StyleName,
859 &hThemeFile,
860 0);
861
862 if (!SUCCEEDED(hret)) return FALSE;
863 }
864
865 hret = ApplyTheme(hThemeFile, "", 0);
866
867 if (pSelectedTheme->ThemeActive)
868 {
869 CloseThemeFile(hThemeFile);
870 }
871
872 return SUCCEEDED(hret);
873 }
874
875 BOOL
876 LoadSchemeFromTheme(OUT PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme)
877 {
878 HTHEMEFILE hThemeFile = 0;
879 HRESULT hret;
880 HTHEME hTheme;
881 int i;
882
883 hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName,
884 pSelectedTheme->Color->StyleName,
885 pSelectedTheme->Size->StyleName,
886 &hThemeFile,
887 0);
888
889 if (!SUCCEEDED(hret)) return FALSE;
890
891 hTheme = OpenThemeDataFromFile(hThemeFile, hCPLWindow, L"WINDOW", 0);
892 if (hTheme == NULL) return FALSE;
893
894 /* Load colors */
895 for (i = 0; i < NUM_COLORS; i++)
896 {
897 scheme->crColor[i] = GetThemeSysColor(hTheme,i);
898 }
899
900 /* Load sizes */
901 /* I wonder why GetThemeSysInt doesn't work here */
902 scheme->ncMetrics.iBorderWidth = GetThemeSysSize(hTheme, SM_CXFRAME);
903 scheme->ncMetrics.iScrollWidth = GetThemeSysSize(hTheme, SM_CXVSCROLL);
904 scheme->ncMetrics.iScrollHeight = GetThemeSysSize(hTheme, SM_CYHSCROLL);
905 scheme->ncMetrics.iCaptionWidth = GetThemeSysSize(hTheme, SM_CXSIZE);
906 scheme->ncMetrics.iCaptionHeight = GetThemeSysSize(hTheme, SM_CYSIZE);
907 scheme->ncMetrics.iSmCaptionWidth = GetThemeSysSize(hTheme, SM_CXSMSIZE);
908 scheme->ncMetrics.iSmCaptionHeight = GetThemeSysSize(hTheme, SM_CYSMSIZE);
909 scheme->ncMetrics.iMenuWidth = GetThemeSysSize(hTheme, SM_CXMENUSIZE);
910 scheme->ncMetrics.iMenuHeight = GetThemeSysSize(hTheme, SM_CYMENUSIZE);
911
912 /* Load fonts */
913 GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &scheme->ncMetrics.lfCaptionFont);
914 GetThemeSysFont(hTheme, TMT_SMALLCAPTIONFONT, &scheme->ncMetrics.lfSmCaptionFont);
915 GetThemeSysFont(hTheme, TMT_MENUFONT, &scheme->ncMetrics.lfMenuFont );
916 GetThemeSysFont(hTheme, TMT_STATUSFONT, &scheme->ncMetrics.lfStatusFont);
917 GetThemeSysFont(hTheme, TMT_MSGBOXFONT, &scheme->ncMetrics.lfMessageFont);
918 GetThemeSysFont(hTheme, TMT_ICONTITLEFONT, &scheme->icMetrics.lfFont);
919
920 CloseThemeData(hTheme);
921
922 return TRUE;
923 }
924
925 BOOL
926 DrawThemePreview(IN HDC hdcMem, IN PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme, IN PRECT prcWindow)
927 {
928 HBRUSH hbrBack;
929 HRESULT hres;
930
931 hbrBack = CreateSolidBrush(scheme->crColor[COLOR_DESKTOP]);
932
933 FillRect(hdcMem, prcWindow, hbrBack);
934 DeleteObject(hbrBack);
935
936 InflateRect(prcWindow, -10, -10);
937
938 hres = DrawNCPreview(hdcMem,
939 DNCP_DRAW_ALL,
940 prcWindow,
941 pSelectedTheme->Theme->ThemeFileName,
942 pSelectedTheme->Color->StyleName,
943 pSelectedTheme->Size->StyleName,
944 &scheme->ncMetrics,
945 scheme->crColor);
946
947 return SUCCEEDED(hres);
948 }