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