[THEMES]
[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
6 *
7 * PROGRAMMERS: Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
8 */
9
10 #include "desk.h"
11
12 static const WCHAR g_CPColors[] = L"Control Panel\\Colors";
13 static const WCHAR g_CPANewSchemes[] = L"Control Panel\\Appearance\\New Schemes";
14 static const WCHAR g_CPMetrics[] = L"Control Panel\\Desktop\\WindowMetrics";
15 static const WCHAR g_SelectedStyle[] = L"SelectedStyle";
16
17 /******************************************************************************/
18
19 /* This is the list of names for the colors stored in the registry */
20 static const WCHAR *g_RegColorNames[NUM_COLORS] = {
21 L"Scrollbar", /* 00 = COLOR_SCROLLBAR */
22 L"Background", /* 01 = COLOR_DESKTOP */
23 L"ActiveTitle", /* 02 = COLOR_ACTIVECAPTION */
24 L"InactiveTitle", /* 03 = COLOR_INACTIVECAPTION */
25 L"Menu", /* 04 = COLOR_MENU */
26 L"Window", /* 05 = COLOR_WINDOW */
27 L"WindowFrame", /* 06 = COLOR_WINDOWFRAME */
28 L"MenuText", /* 07 = COLOR_MENUTEXT */
29 L"WindowText", /* 08 = COLOR_WINDOWTEXT */
30 L"TitleText", /* 09 = COLOR_CAPTIONTEXT */
31 L"ActiveBorder", /* 10 = COLOR_ACTIVEBORDER */
32 L"InactiveBorder", /* 11 = COLOR_INACTIVEBORDER */
33 L"AppWorkSpace", /* 12 = COLOR_APPWORKSPACE */
34 L"Hilight", /* 13 = COLOR_HIGHLIGHT */
35 L"HilightText", /* 14 = COLOR_HIGHLIGHTTEXT */
36 L"ButtonFace", /* 15 = COLOR_BTNFACE */
37 L"ButtonShadow", /* 16 = COLOR_BTNSHADOW */
38 L"GrayText", /* 17 = COLOR_GRAYTEXT */
39 L"ButtonText", /* 18 = COLOR_BTNTEXT */
40 L"InactiveTitleText", /* 19 = COLOR_INACTIVECAPTIONTEXT */
41 L"ButtonHilight", /* 20 = COLOR_BTNHIGHLIGHT */
42 L"ButtonDkShadow", /* 21 = COLOR_3DDKSHADOW */
43 L"ButtonLight", /* 22 = COLOR_3DLIGHT */
44 L"InfoText", /* 23 = COLOR_INFOTEXT */
45 L"InfoWindow", /* 24 = COLOR_INFOBK */
46 L"ButtonAlternateFace", /* 25 = COLOR_ALTERNATEBTNFACE */
47 L"HotTrackingColor", /* 26 = COLOR_HOTLIGHT */
48 L"GradientActiveTitle", /* 27 = COLOR_GRADIENTACTIVECAPTION */
49 L"GradientInactiveTitle", /* 28 = COLOR_GRADIENTINACTIVECAPTION */
50 L"MenuHilight", /* 29 = COLOR_MENUHILIGHT */
51 L"MenuBar", /* 30 = COLOR_MENUBAR */
52 };
53
54 /******************************************************************************/
55
56 VOID
57 SchemeSetMetric(IN COLOR_SCHEME *scheme, int id, int value)
58 {
59 switch(id)
60 {
61 case SIZE_BORDER_WIDTH: scheme->ncMetrics.iBorderWidth = value; break;
62 case SIZE_SCROLL_WIDTH: scheme->ncMetrics.iScrollWidth = value; break;
63 case SIZE_SCROLL_HEIGHT: scheme->ncMetrics.iScrollHeight = value; break;
64 case SIZE_CAPTION_WIDTH: scheme->ncMetrics.iCaptionWidth = value; break;
65 case SIZE_CAPTION_HEIGHT: scheme->ncMetrics.iCaptionHeight = value; break;
66 case SIZE_SM_CAPTION_WIDTH: scheme->ncMetrics.iSmCaptionWidth = value; break;
67 case SIZE_SM_CAPTION_HEIGHT: scheme->ncMetrics.iSmCaptionHeight = value; break;
68 case SIZE_MENU_WIDTH: scheme->ncMetrics.iMenuWidth = value; break;
69 case SIZE_MENU_HEIGHT: scheme->ncMetrics.iMenuHeight = value; break;
70 case SIZE_ICON: scheme->iIconSize = value; break;
71 case SIZE_ICON_SPACE_X: scheme->icMetrics.iHorzSpacing = value; break;
72 case SIZE_ICON_SPACE_Y: scheme->icMetrics.iVertSpacing = value; break;
73 }
74 }
75
76 int
77 SchemeGetMetric(IN COLOR_SCHEME *scheme, int id)
78 {
79 switch (id)
80 {
81 case SIZE_BORDER_WIDTH: return scheme->ncMetrics.iBorderWidth;
82 case SIZE_SCROLL_WIDTH: return scheme->ncMetrics.iScrollWidth;
83 case SIZE_SCROLL_HEIGHT: return scheme->ncMetrics.iScrollHeight;
84 case SIZE_CAPTION_WIDTH: return scheme->ncMetrics.iCaptionWidth;
85 case SIZE_CAPTION_HEIGHT: return scheme->ncMetrics.iCaptionHeight;
86 case SIZE_SM_CAPTION_WIDTH: return scheme->ncMetrics.iSmCaptionWidth;
87 case SIZE_SM_CAPTION_HEIGHT: return scheme->ncMetrics.iSmCaptionHeight;
88 case SIZE_MENU_WIDTH: return scheme->ncMetrics.iMenuWidth;
89 case SIZE_MENU_HEIGHT: return scheme->ncMetrics.iMenuHeight;
90 case SIZE_ICON: return scheme->iIconSize;
91 case SIZE_ICON_SPACE_X: return scheme->icMetrics.iHorzSpacing;
92 case SIZE_ICON_SPACE_Y: return scheme->icMetrics.iVertSpacing;
93 }
94 return 0;
95 }
96
97 PLOGFONTW
98 SchemeGetFont(IN COLOR_SCHEME *scheme, int id)
99 {
100 switch (id)
101 {
102 case FONT_CAPTION: return &scheme->ncMetrics.lfCaptionFont;
103 case FONT_SMCAPTION: return &scheme->ncMetrics.lfSmCaptionFont;
104 case FONT_MENU: return &scheme->ncMetrics.lfMenuFont;
105 case FONT_STATUS: return &scheme->ncMetrics.lfStatusFont;
106 case FONT_MESSAGE: return &scheme->ncMetrics.lfMessageFont;
107 case FONT_ICON: return &scheme->icMetrics.lfFont;
108 }
109 return NULL;
110 }
111
112 /*
113 * LoadCurrentScheme: Populates the passed scheme based on the current system settings
114 */
115 BOOL
116 LoadCurrentScheme(OUT COLOR_SCHEME *scheme)
117 {
118 INT i, Result;
119 HKEY hKey;
120 BOOL ret;
121
122 /* Load colors */
123 for (i = 0; i < NUM_COLORS; i++)
124 {
125 scheme->crColor[i] = (COLORREF)GetSysColor(i);
126 }
127
128 /* Load non client metrics */
129 scheme->ncMetrics.cbSize = sizeof(NONCLIENTMETRICSW);
130 ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
131 sizeof(NONCLIENTMETRICSW),
132 &scheme->ncMetrics,
133 0);
134 if (!ret) return FALSE;
135
136 /* Load icon metrics */
137 scheme->icMetrics.cbSize = sizeof(ICONMETRICSW);
138 ret = SystemParametersInfoW(SPI_GETICONMETRICS,
139 sizeof(ICONMETRICSW),
140 &scheme->icMetrics,
141 0);
142 if (!ret) return FALSE;
143
144 /* Load flat menu style */
145 ret = SystemParametersInfoW(SPI_GETFLATMENU,
146 0,
147 &scheme->bFlatMenus,
148 0);
149 if (!ret) return FALSE;
150
151 /* Effects */
152 /* "Use the following transition effect for menus and tooltips" */
153 ret = SystemParametersInfoW(SPI_GETMENUANIMATION,
154 0,
155 &scheme->Effects.bMenuAnimation,
156 0);
157 if (!ret) return FALSE;
158
159 ret = SystemParametersInfoW(SPI_GETMENUFADE,
160 0,
161 &scheme->Effects.bMenuFade,
162 0);
163 if (!ret) return FALSE;
164
165 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings
166 * Just keep them in sync for now:
167 */
168 scheme->Effects.bTooltipAnimation = scheme->Effects.bMenuAnimation;
169 scheme->Effects.bTooltipFade = scheme->Effects.bMenuFade;
170
171 /* Show content of windows during dragging */
172 ret = SystemParametersInfoW(SPI_GETDRAGFULLWINDOWS,
173 0,
174 &scheme->Effects.bDragFullWindows,
175 0);
176 if (!ret) return FALSE;
177
178 /* "Hide underlined letters for keyboard navigation until I press the Alt key" */
179 ret = SystemParametersInfoW(SPI_GETKEYBOARDCUES,
180 0,
181 &scheme->Effects.bKeyboardCues,
182 0);
183 if (!ret) return FALSE;
184
185 /* Read the icon size from registry */
186 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPMetrics, &hKey);
187 if(Result == ERROR_SUCCESS)
188 {
189 scheme->iIconSize = SHRegGetIntW(hKey, L"Shell Icon Size", 32);
190 RegCloseKey(hKey);
191 }
192
193 return TRUE;
194 }
195
196 /*
197 * LoadSchemeFromReg: Populates the passed scheme with values retireved from registry
198 */
199 BOOL
200 LoadSchemeFromReg(OUT COLOR_SCHEME *scheme, IN PTHEME_SELECTION pSelectedTheme)
201 {
202 INT i;
203 WCHAR strValueName[10], strSchemeKey[MAX_PATH];
204 HKEY hkScheme = NULL;
205 DWORD dwType, dwLength;
206 UINT64 iSize;
207 BOOL Ret = TRUE;
208 LONG result;
209
210 wsprintf(strSchemeKey, L"%s\\%s\\Sizes\\%s",
211 g_CPANewSchemes,
212 pSelectedTheme->Color->StyleName,
213 pSelectedTheme->Size->StyleName);
214
215 result = RegOpenKeyW(HKEY_CURRENT_USER, strSchemeKey, &hkScheme);
216 if (result != ERROR_SUCCESS) return FALSE;
217
218 scheme->bFlatMenus = SHRegGetIntW(hkScheme, L"FlatMenus", 0);
219
220 for (i = 0; i < NUM_COLORS; i++)
221 {
222 wsprintf(strValueName, L"Color #%d", i);
223 dwLength = sizeof(COLORREF);
224 result = RegQueryValueExW(hkScheme,
225 strValueName,
226 NULL,
227 &dwType,
228 (LPBYTE)&scheme->crColor[i],
229 &dwLength);
230 if (result != ERROR_SUCCESS || dwType != REG_DWORD)
231 {
232 /* Failed to read registry value, initialize with current setting for now */
233 scheme->crColor[i] = GetSysColor(i);
234 }
235 }
236
237 for (i = 0; i < NUM_FONTS; i++)
238 {
239 PLOGFONTW lpfFont = SchemeGetFont(scheme, i);
240
241 wsprintf(strValueName, L"Font #%d", i);
242 dwLength = sizeof(LOGFONT);
243 result = RegQueryValueExW(hkScheme,
244 strValueName,
245 NULL,
246 &dwType,
247 (LPBYTE)lpfFont,
248 &dwLength);
249 if (result != ERROR_SUCCESS || dwType != REG_BINARY ||
250 dwLength != sizeof(LOGFONT))
251 {
252 /* Failed to read registry value */
253 Ret = FALSE;
254 }
255 }
256
257 for (i = 0; i < NUM_SIZES; i++)
258 {
259 wsprintf(strValueName, L"Size #%d", i);
260 dwLength = sizeof(UINT64);
261 result = RegQueryValueExW(hkScheme,
262 strValueName,
263 NULL,
264 &dwType,
265 (LPBYTE)&iSize,
266 &dwLength);
267 if (result != ERROR_SUCCESS || dwType != REG_QWORD ||
268 dwLength != sizeof(UINT64))
269 {
270 /* Failed to read registry value, initialize with current setting for now */
271 }
272 else
273 {
274 SchemeSetMetric(scheme, i, (int)iSize);
275 }
276 }
277
278 RegCloseKey(hkScheme);
279
280 return Ret;
281 }
282
283 /*
284 ApplyScheme: Applies the selected scheme and stores its id in the registry if needed
285 */
286 VOID
287 ApplyScheme(IN COLOR_SCHEME *scheme, IN PTHEME_SELECTION pSelectedTheme)
288 {
289 INT i, Result;
290 HKEY hKey;
291 WCHAR clText[16], *StyleName;
292 INT ColorList[NUM_COLORS];
293
294 /* Apply system colors */
295 for (i = 0; i < NUM_COLORS; i++)
296 ColorList[i] = i;
297 SetSysColors(NUM_COLORS, ColorList, scheme->crColor);
298
299 /* Save colors to registry */
300 Result = RegCreateKeyW(HKEY_CURRENT_USER, g_CPColors, &hKey);
301 if (Result == ERROR_SUCCESS)
302 {
303 for (i = 0; i < NUM_COLORS; i++)
304 {
305 wsprintf(clText,
306 L"%d %d %d",
307 GetRValue(scheme->crColor[i]),
308 GetGValue(scheme->crColor[i]),
309 GetBValue(scheme->crColor[i]));
310
311 RegSetValueExW(hKey,
312 g_RegColorNames[i],
313 0,
314 REG_SZ,
315 (BYTE *)clText,
316 (lstrlen(clText) + 1) * sizeof(WCHAR));
317 }
318 RegCloseKey(hKey);
319 }
320
321 /* Apply non client metrics */
322 SystemParametersInfoW(SPI_SETNONCLIENTMETRICS,
323 sizeof(NONCLIENTMETRICS),
324 &scheme->ncMetrics,
325 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
326
327 /* Apply icon metrics */
328 SystemParametersInfoW(SPI_SETICONMETRICS,
329 sizeof(ICONMETRICS),
330 &scheme->icMetrics,
331 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
332
333 /* Effects, save only when needed: */
334 /* FIXME: XP seems to use grayed checkboxes to reflect differences between menu and tooltips settings
335 * Just keep them in sync for now.
336 */
337 scheme->Effects.bTooltipAnimation = scheme->Effects.bMenuAnimation;
338 scheme->Effects.bTooltipFade = scheme->Effects.bMenuFade;
339 SystemParametersInfoW(SPI_SETDRAGFULLWINDOWS, scheme->Effects.bDragFullWindows, (PVOID)&scheme->Effects.bDragFullWindows, SPIF_SENDCHANGE | SPIF_UPDATEINIFILE);
340 SystemParametersInfoW(SPI_SETKEYBOARDCUES, 0, IntToPtr(scheme->Effects.bKeyboardCues), SPIF_SENDCHANGE | SPIF_UPDATEINIFILE);
341 //SystemParametersInfoW(SPI_SETACTIVEWINDOWTRACKING, 0, (PVOID)&scheme->Effects.bActiveWindowTracking, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
342 //SystemParametersInfoW(SPI_SETMENUANIMATION, 0, (PVOID)&scheme->Effects.bMenuAnimation, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
343 //SystemParametersInfoW(SPI_SETCOMBOBOXANIMATION, 0, (PVOID)&scheme->Effects.bComboBoxAnimation, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
344 //SystemParametersInfoW(SPI_SETLISTBOXSMOOTHSCROLLING, 0, (PVOID)&scheme->Effects.bListBoxSmoothScrolling, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
345 //SystemParametersInfoW(SPI_SETGRADIENTCAPTIONS, 0, (PVOID)&scheme->Effects.bGradientCaptions, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
346 //SystemParametersInfoW(SPI_SETACTIVEWNDTRKZORDER, 0, (PVOID)&scheme->Effects.bActiveWndTrkZorder, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
347 //SystemParametersInfoW(SPI_SETHOTTRACKING, 0, (PVOID)&scheme->Effects.bHotTracking, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
348 SystemParametersInfoW(SPI_SETMENUFADE, 0, (PVOID)&scheme->Effects.bMenuFade, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
349 //SystemParametersInfoW(SPI_SETSELECTIONFADE, 0, (PVOID)&scheme->Effects.bSelectionFade, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
350 SystemParametersInfoW(SPI_SETTOOLTIPANIMATION, 0, (PVOID)&scheme->Effects.bTooltipAnimation, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
351 SystemParametersInfoW(SPI_SETTOOLTIPFADE, 0, (PVOID)&scheme->Effects.bTooltipFade, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
352 //SystemParametersInfoW(SPI_SETCURSORSHADOW, 0, (PVOID)&scheme->Effects.bCursorShadow, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
353 //SystemParametersInfoW(SPI_SETUIEFFECTS, 0, (PVOID)&scheme->Effects.bUiEffects, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE);
354
355 /* Save SchemeId in the registry */
356 if (pSelectedTheme->Theme != NULL && pSelectedTheme->ThemeActive == FALSE)
357 {
358 StyleName = pSelectedTheme->Color->StyleName;
359 SHSetValueW(HKEY_CURRENT_USER,
360 g_CPANewSchemes,
361 g_SelectedStyle,
362 REG_SZ,
363 StyleName,
364 (lstrlenW(StyleName) + 1) * sizeof(WCHAR));
365 }
366 }
367
368 static THEME*
369 CreateTheme(LPCWSTR pszName, LPCWSTR pszDisplayName)
370 {
371 PTHEME pTheme;
372
373 pTheme = (PTHEME) malloc(sizeof(THEME));
374 if (pTheme == NULL) return NULL;
375
376 pTheme->DisplayName = _wcsdup(pszDisplayName);
377 if (pTheme->DisplayName == NULL)
378 {
379 free(pTheme);
380 return NULL;
381 }
382
383 pTheme->ColoursList = NULL;
384 pTheme->NextTheme = NULL;
385 pTheme->SizesList = NULL;
386
387 if (pszName == NULL)
388 {
389 pTheme->ThemeFileName = NULL;
390 return pTheme;
391 }
392
393 pTheme->ThemeFileName = _wcsdup(pszName);
394 if (pTheme->ThemeFileName == NULL)
395 {
396 free(pTheme->DisplayName);
397 free(pTheme);
398 return NULL;
399 }
400
401 return pTheme;
402 }
403
404 static PTHEME_STYLE
405 CreateStyle(LPCWSTR pszName, LPCWSTR pszDisplayName)
406 {
407 PTHEME_STYLE pStyle;
408
409 pStyle = (PTHEME_STYLE) malloc(sizeof(THEME_STYLE));
410 if (pStyle == NULL) return NULL;
411
412 pStyle->StyleName = _wcsdup(pszName);
413 if (pStyle->StyleName == NULL)
414 {
415 free(pStyle);
416 return NULL;
417 }
418
419 pStyle->DisplayName = _wcsdup(pszDisplayName);
420 if (pStyle->DisplayName == NULL)
421 {
422 free(pStyle->StyleName);
423 free(pStyle);
424 return NULL;
425 }
426
427 pStyle->ChildStyle = NULL;
428 pStyle->NextStyle = NULL;
429
430 return pStyle;
431 }
432
433 static void
434 CleanupStyles(IN PTHEME_STYLE pStylesList)
435 {
436 PTHEME_STYLE pStyle, pStyleOld;
437
438 pStyle = pStylesList;
439 while (pStyle)
440 {
441 if (pStyle->ChildStyle) CleanupStyles(pStyle->ChildStyle);
442 if (pStyle->DisplayName) free(pStyle->DisplayName);
443 if (pStyle->StyleName) free(pStyle->StyleName);
444
445 pStyleOld = pStyle;
446 pStyle = pStyle->NextStyle;
447 free(pStyleOld);
448 }
449 }
450
451 void
452 CleanupThemes(IN PTHEME pThemeList)
453 {
454 PTHEME pTheme, pThemeOld;
455
456 pTheme = pThemeList;
457 while (pTheme)
458 {
459 CleanupStyles(pTheme->ColoursList);
460 if (pTheme->SizesList) CleanupStyles(pTheme->SizesList);
461 if (pTheme->DisplayName) free(pTheme->DisplayName);
462 if (pTheme->ThemeFileName) free(pTheme->ThemeFileName);
463
464 pThemeOld = pTheme;
465 pTheme = pTheme->NextTheme;
466 free(pThemeOld);
467 }
468 }
469
470 static PTHEME_STYLE
471 FindStyle(IN PTHEME_STYLE pStylesList, IN PCWSTR StyleName)
472 {
473 PTHEME_STYLE pStyle;
474
475 for (pStyle = pStylesList; pStyle; pStyle = pStyle->NextStyle)
476 {
477 if (_wcsicmp(pStyle->StyleName, StyleName) == 0)
478 {
479 return pStyle;
480 }
481 }
482
483 /* If we can't find the style requested, return the first one */
484 return pStylesList;
485 }
486
487 /*
488 * LoadSchemeSizes: Returns a list of sizes from the registry key of a scheme
489 */
490 static PTHEME_STYLE
491 LoadSchemeSizes(IN HKEY hkScheme)
492 {
493 HKEY hkSizes, hkSize;
494 INT Result;
495 INT iStyle;
496 WCHAR wstrSizeName[5], wstrDisplayName[50];
497 THEME_STYLE *List = NULL, *pCurrentStyle;
498
499 Result = RegOpenKeyW(hkScheme, L"Sizes", &hkSizes);
500 if (Result != ERROR_SUCCESS) return NULL;
501
502 iStyle = 0;
503 while ((RegEnumKeyW(hkSizes, iStyle, wstrSizeName, 5) == ERROR_SUCCESS))
504 {
505 iStyle++;
506
507 Result = RegOpenKeyW(hkSizes, wstrSizeName, &hkSize);
508 if (Result != ERROR_SUCCESS) continue;
509
510 Result = RegLoadMUIStringW(hkSize,
511 L"DisplayName",
512 wstrDisplayName,
513 sizeof(wstrDisplayName),
514 NULL,
515 0,
516 NULL);
517 if (Result != ERROR_SUCCESS)
518 {
519 Result = RegLoadMUIStringW(hkSize,
520 L"LegacyName",
521 wstrDisplayName,
522 sizeof(wstrDisplayName),
523 NULL,
524 0,
525 NULL);
526 }
527
528 if (Result == ERROR_SUCCESS)
529 pCurrentStyle = CreateStyle(wstrSizeName, wstrDisplayName);
530 else
531 pCurrentStyle = CreateStyle(wstrSizeName, wstrSizeName);
532
533 if (pCurrentStyle != NULL)
534 {
535 pCurrentStyle->NextStyle = List;
536 List = pCurrentStyle;
537 }
538
539 RegCloseKey(hkSize);
540 }
541
542 RegCloseKey(hkSizes);
543 return List;
544 }
545
546 /*
547 LoadClassicColorSchemes: Returns a list of classic theme colours from the registry key of a scheme
548 */
549 static THEME_STYLE*
550 LoadClassicColorSchemes(VOID)
551 {
552 INT Result;
553 HKEY hkNewSchemes, hkScheme;
554 INT iStyle;
555 WCHAR wstrStyleName[5], wstrDisplayName[50];
556 THEME_STYLE *List = NULL, *pCurrentStyle;
557
558 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes);
559 if (Result != ERROR_SUCCESS) return NULL;
560
561 iStyle = 0;
562 while ((RegEnumKeyW(hkNewSchemes, iStyle, wstrStyleName, 5) == ERROR_SUCCESS))
563 {
564 iStyle++;
565
566 Result = RegOpenKeyW(hkNewSchemes, wstrStyleName, &hkScheme);
567 if (Result != ERROR_SUCCESS) continue;
568
569 Result = RegLoadMUIStringW(hkScheme,
570 L"DisplayName",
571 wstrDisplayName,
572 sizeof(wstrDisplayName),
573 NULL,
574 0,
575 NULL);
576 if (Result != ERROR_SUCCESS)
577 {
578 Result = RegLoadMUIStringW(hkScheme,
579 L"LegacyName",
580 wstrDisplayName,
581 sizeof(wstrDisplayName),
582 NULL,
583 0,
584 NULL);
585 }
586
587 if (Result == ERROR_SUCCESS)
588 pCurrentStyle = CreateStyle(wstrStyleName, wstrDisplayName);
589 else
590 pCurrentStyle = CreateStyle(wstrStyleName, wstrStyleName);
591
592 if (pCurrentStyle != NULL)
593 {
594 pCurrentStyle->NextStyle = List;
595 pCurrentStyle->ChildStyle = LoadSchemeSizes(hkScheme);
596 if(pCurrentStyle->ChildStyle == NULL)
597 CleanupStyles(pCurrentStyle);
598 else
599 List = pCurrentStyle;
600 }
601
602 RegCloseKey(hkScheme);
603 }
604
605 RegCloseKey(hkNewSchemes);
606 return List;
607 }
608
609 typedef HRESULT (WINAPI *ENUMTHEMESTYLE) (LPCWSTR, LPWSTR, DWORD, PTHEMENAMES);
610
611 static THEME_STYLE*
612 EnumThemeStyles(IN LPCWSTR pszThemeFileName, IN ENUMTHEMESTYLE pfnEnumTheme)
613 {
614 DWORD index = 0;
615 THEMENAMES names;
616 THEME_STYLE *List = NULL, **ppPrevStyle, *pCurrentStyle;
617
618 ppPrevStyle = &List;
619
620 while (SUCCEEDED(pfnEnumTheme (pszThemeFileName, NULL, index++, &names)))
621 {
622 pCurrentStyle = CreateStyle(names.szName, names.szDisplayName);
623 if(pCurrentStyle == NULL) break;
624
625 *ppPrevStyle = pCurrentStyle;
626 ppPrevStyle = &pCurrentStyle->NextStyle;
627 }
628
629 return List;
630 }
631
632 BOOL CALLBACK
633 EnumThemeProc(IN LPVOID lpReserved,
634 IN LPCWSTR pszThemeFileName,
635 IN LPCWSTR pszThemeName,
636 IN LPCWSTR pszToolTip,
637 IN LPVOID lpReserved2,
638 IN OUT LPVOID lpData)
639 {
640 PTHEME *List, pTheme;
641
642 List = (PTHEME*)lpData;
643
644 pTheme = CreateTheme(pszThemeFileName, pszThemeName);
645 if (pTheme == NULL) return FALSE;
646
647 pTheme->SizesList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeSizes);
648 pTheme->ColoursList = EnumThemeStyles( pszThemeFileName, (ENUMTHEMESTYLE)EnumThemeColors);
649 if(pTheme->SizesList == NULL || pTheme->ColoursList == NULL)
650 {
651 CleanupThemes(pTheme);
652 return FALSE;
653 }
654
655 pTheme->NextTheme = *List;
656 *List = pTheme;
657
658 return TRUE;
659 }
660
661 /*
662 LoadThemes: Returns a list that contains tha classic theme and
663 the visual styles of the system
664 */
665 PTHEME
666 LoadThemes()
667 {
668 HRESULT hret;
669 PTHEME pClassicTheme;
670 WCHAR strClassicTheme[40];
671 WCHAR szThemesPath[MAX_PATH], *pszClassicTheme;
672 int res;
673
674 /* Insert the classic theme */
675 res = LoadString(hApplet, IDS_CLASSIC_THEME, strClassicTheme, 40);
676 pszClassicTheme = (res > 0 ? strClassicTheme : L"Classic Theme");
677 pClassicTheme = CreateTheme(NULL, pszClassicTheme);
678 if (pClassicTheme == NULL) return NULL;
679 pClassicTheme->ColoursList = LoadClassicColorSchemes();
680
681 /* Get path to themes folder */
682 ZeroMemory(szThemesPath, sizeof(szThemesPath));
683 hret = SHGetFolderPathW (NULL, CSIDL_RESOURCES, NULL, SHGFP_TYPE_DEFAULT, szThemesPath);
684 if (FAILED(hret)) return pClassicTheme;
685 lstrcatW (szThemesPath, L"\\Themes");
686
687 /* Enumerate themes */
688 hret = EnumThemes( szThemesPath, EnumThemeProc, &pClassicTheme->NextTheme);
689 if (FAILED(hret))
690 {
691 pClassicTheme->NextTheme = NULL;
692 if (pClassicTheme->ColoursList == NULL)
693 {
694 free(pClassicTheme->DisplayName);
695 free(pClassicTheme);
696 return NULL;
697 }
698 }
699
700 return pClassicTheme;
701 }
702
703 /*
704 * GetActiveTheme: Gets the active theme and populates pSelectedTheme
705 * with entries from the list of loaded themes
706 */
707 BOOL
708 GetActiveTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme)
709 {
710 WCHAR szThemeFileName[MAX_PATH];
711 WCHAR szColorBuff[MAX_PATH];
712 WCHAR szSizeBuff[MAX_PATH];
713 PTHEME pTheme;
714 HRESULT hret;
715
716 ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION));
717
718 /* Retrieve the name of the current theme */
719 hret = GetCurrentThemeName(szThemeFileName,
720 MAX_PATH,
721 szColorBuff,
722 MAX_PATH,
723 szSizeBuff,
724 MAX_PATH);
725 if (FAILED(hret)) return FALSE;
726
727 for (pTheme = pThemeList; pTheme; pTheme = pTheme->NextTheme)
728 {
729 if(pTheme->ThemeFileName &&
730 _wcsicmp(pTheme->ThemeFileName, szThemeFileName) == 0)
731 {
732 break;
733 }
734 }
735
736 if (pTheme == NULL) return FALSE;
737
738 pSelectedTheme->ThemeActive = TRUE;
739 pSelectedTheme->Theme = pTheme;
740 pSelectedTheme->Color = FindStyle(pTheme->ColoursList, szColorBuff);
741 pSelectedTheme->Size = FindStyle(pTheme->SizesList, szSizeBuff);
742
743 return TRUE;
744 }
745
746 /*
747 GetActiveTheme: Gets the active classic theme and populates pSelectedTheme
748 with entries from the list of loaded themes
749 */
750 BOOL
751 GetActiveClassicTheme(IN PTHEME pThemeList, OUT PTHEME_SELECTION pSelectedTheme)
752 {
753 INT Result;
754 WCHAR szSelectedClassicScheme[5], szSelectedClassicSize[5];
755 HKEY hkNewSchemes;
756 DWORD dwType, dwDisplayNameLength;
757 PTHEME_STYLE pCurrentStyle, pCurrentSize;
758
759 ZeroMemory(pSelectedTheme, sizeof(THEME_SELECTION));
760
761 /* Assume failure */
762 szSelectedClassicScheme[0] = 0;
763 szSelectedClassicSize[0] = 0;
764
765 Result = RegOpenKeyW(HKEY_CURRENT_USER, g_CPANewSchemes, &hkNewSchemes);
766 if (Result != ERROR_SUCCESS) return FALSE;
767
768 dwType = REG_SZ;
769 dwDisplayNameLength = sizeof(szSelectedClassicScheme);
770 Result = RegQueryValueEx(hkNewSchemes, L"SelectedStyle", NULL, &dwType,
771 (LPBYTE)&szSelectedClassicScheme, &dwDisplayNameLength);
772 if (Result == ERROR_SUCCESS)
773 {
774 dwType = REG_SZ;
775 dwDisplayNameLength = sizeof(szSelectedClassicSize);
776 Result = SHGetValue(hkNewSchemes, szSelectedClassicScheme, L"SelectedSize",
777 &dwType, szSelectedClassicSize, &dwDisplayNameLength);
778 }
779
780 RegCloseKey(hkNewSchemes);
781
782 pCurrentStyle = FindStyle(pThemeList->ColoursList, szSelectedClassicScheme);
783 pCurrentSize = FindStyle(pCurrentStyle->ChildStyle, szSelectedClassicSize);
784
785 pSelectedTheme->Theme = pThemeList;
786 pSelectedTheme->Color = pCurrentStyle;
787 pSelectedTheme->Size = pCurrentSize;
788
789 return TRUE;
790 }
791
792 BOOL
793 ActivateTheme(IN PTHEME_SELECTION pSelectedTheme)
794 {
795 HTHEMEFILE hThemeFile = 0;
796 HRESULT hret;
797
798 if (pSelectedTheme->ThemeActive)
799 {
800 hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName,
801 pSelectedTheme->Color->StyleName,
802 pSelectedTheme->Size->StyleName,
803 &hThemeFile,
804 0);
805
806 if (!SUCCEEDED(hret)) return FALSE;
807 }
808
809 hret = ApplyTheme(hThemeFile, "", 0);
810
811 if (pSelectedTheme->ThemeActive)
812 {
813 CloseThemeFile(hThemeFile);
814 }
815
816 return SUCCEEDED(hret);
817 }
818
819 BOOL
820 LoadSchemeFromTheme(OUT PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme)
821 {
822 HTHEMEFILE hThemeFile = 0;
823 HRESULT hret;
824 HTHEME hTheme;
825 int i;
826
827 hret = OpenThemeFile(pSelectedTheme->Theme->ThemeFileName,
828 pSelectedTheme->Color->StyleName,
829 pSelectedTheme->Size->StyleName,
830 &hThemeFile,
831 0);
832
833 if (!SUCCEEDED(hret)) return FALSE;
834
835 hTheme = OpenThemeDataFromFile(hThemeFile, hCPLWindow, L"WINDOW", 0);
836 if (hTheme == NULL) return FALSE;
837
838 /* Load colors */
839 for (i = 0; i < NUM_COLORS; i++)
840 {
841 scheme->crColor[i] = GetThemeSysColor(hTheme,i);
842 }
843
844 /* Load sizes */
845 /* I wonder why GetThemeSysInt doesn't work here */
846 scheme->ncMetrics.iBorderWidth = GetThemeSysSize(hTheme, SM_CXFRAME);
847 scheme->ncMetrics.iScrollWidth = GetThemeSysSize(hTheme, SM_CXVSCROLL);
848 scheme->ncMetrics.iScrollHeight = GetThemeSysSize(hTheme, SM_CYHSCROLL);
849 scheme->ncMetrics.iCaptionWidth = GetThemeSysSize(hTheme, SM_CXSIZE);
850 scheme->ncMetrics.iCaptionHeight = GetThemeSysSize(hTheme, SM_CYSIZE);
851 scheme->ncMetrics.iSmCaptionWidth = GetThemeSysSize(hTheme, SM_CXSMSIZE);
852 scheme->ncMetrics.iSmCaptionHeight = GetThemeSysSize(hTheme, SM_CYSMSIZE);
853 scheme->ncMetrics.iMenuWidth = GetThemeSysSize(hTheme, SM_CXMENUSIZE);
854 scheme->ncMetrics.iMenuHeight = GetThemeSysSize(hTheme, SM_CYMENUSIZE);
855
856 /* Load fonts */
857 GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &scheme->ncMetrics.lfCaptionFont);
858 GetThemeSysFont(hTheme, TMT_SMALLCAPTIONFONT, &scheme->ncMetrics.lfSmCaptionFont);
859 GetThemeSysFont(hTheme, TMT_MENUFONT, &scheme->ncMetrics.lfMenuFont );
860 GetThemeSysFont(hTheme, TMT_STATUSFONT, &scheme->ncMetrics.lfStatusFont);
861 GetThemeSysFont(hTheme, TMT_MSGBOXFONT, &scheme->ncMetrics.lfMessageFont);
862 GetThemeSysFont(hTheme, TMT_ICONTITLEFONT, &scheme->icMetrics.lfFont);
863
864 CloseThemeData(hTheme);
865
866 return TRUE;
867 }
868
869 BOOL
870 DrawThemePreview(IN HDC hdcMem, IN PCOLOR_SCHEME scheme, IN PTHEME_SELECTION pSelectedTheme, IN PRECT prcWindow)
871 {
872 HBRUSH hbrBack;
873 HRESULT hres;
874
875 hbrBack = CreateSolidBrush(scheme->crColor[COLOR_DESKTOP]);
876
877 FillRect(hdcMem, prcWindow, hbrBack);
878 DeleteObject(hbrBack);
879
880 InflateRect(prcWindow, -10, -10);
881
882 hres = DrawNCPreview(hdcMem,
883 DNCP_DRAW_ALL,
884 prcWindow,
885 pSelectedTheme->Theme->ThemeFileName,
886 pSelectedTheme->Color->StyleName,
887 pSelectedTheme->Size->StyleName,
888 &scheme->ncMetrics,
889 scheme->crColor);
890
891 return SUCCEEDED(hres);
892 }
893