xref: /reactos/dll/win32/uxtheme/system.c (revision c2c66aff)
1 /*
2  * Win32 5.1 Theme system
3  *
4  * Copyright (C) 2003 Kevin Koltzau
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "uxthemep.h"
22 
23 #include <stdio.h>
24 #include <winreg.h>
25 #include <uxundoc.h>
26 
27 /***********************************************************************
28  * Defines and global variables
29  */
30 
31 static const WCHAR szThemeManager[] = {
32     'S','o','f','t','w','a','r','e','\\',
33     'M','i','c','r','o','s','o','f','t','\\',
34     'W','i','n','d','o','w','s','\\',
35     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
36     'T','h','e','m','e','M','a','n','a','g','e','r','\0'
37 };
38 static const WCHAR szThemeActive[] = {'T','h','e','m','e','A','c','t','i','v','e','\0'};
39 static const WCHAR szSizeName[] = {'S','i','z','e','N','a','m','e','\0'};
40 static const WCHAR szColorName[] = {'C','o','l','o','r','N','a','m','e','\0'};
41 static const WCHAR szDllName[] = {'D','l','l','N','a','m','e','\0'};
42 
43 static const WCHAR szIniDocumentation[] = {'d','o','c','u','m','e','n','t','a','t','i','o','n','\0'};
44 
45 HINSTANCE hDllInst;
46 ATOM atDialogThemeEnabled;
47 
48 static DWORD dwThemeAppProperties = STAP_ALLOW_NONCLIENT | STAP_ALLOW_CONTROLS;
49 ATOM atWindowTheme;
50 static ATOM atSubAppName;
51 static ATOM atSubIdList;
52 ATOM atWndContext;
53 
54 PTHEME_FILE g_ActiveThemeFile;
55 
56 RTL_HANDLE_TABLE g_UxThemeHandleTable;
57 int g_cHandles;
58 
59 /***********************************************************************/
60 
61 static BOOL CALLBACK UXTHEME_broadcast_msg_enumchild (HWND hWnd, LPARAM msg)
62 {
63     SendMessageW(hWnd, msg, 0, 0);
64     return TRUE;
65 }
66 
67 /* Broadcast a message to *all* windows, including children */
68 BOOL CALLBACK UXTHEME_broadcast_msg (HWND hWnd, LPARAM msg)
69 {
70     if (hWnd == NULL)
71     {
72 	EnumWindows (UXTHEME_broadcast_msg, msg);
73     }
74     else
75     {
76 	SendMessageW(hWnd, msg, 0, 0);
77 	EnumChildWindows (hWnd, UXTHEME_broadcast_msg_enumchild, msg);
78     }
79     return TRUE;
80 }
81 
82 /* At the end of the day this is a subset of what SHRegGetPath() does - copied
83  * here to avoid linking against shlwapi. */
84 static DWORD query_reg_path (HKEY hKey, LPCWSTR lpszValue,
85                              LPVOID pvData)
86 {
87   DWORD dwRet, dwType, dwUnExpDataLen = MAX_PATH * sizeof(WCHAR), dwExpDataLen;
88 
89   TRACE("(hkey=%p,%s,%p)\n", hKey, debugstr_w(lpszValue),
90         pvData);
91 
92   dwRet = RegQueryValueExW(hKey, lpszValue, 0, &dwType, pvData, &dwUnExpDataLen);
93   if (dwRet!=ERROR_SUCCESS && dwRet!=ERROR_MORE_DATA)
94       return dwRet;
95 
96   if (dwType == REG_EXPAND_SZ)
97   {
98     DWORD nBytesToAlloc;
99 
100     /* Expand type REG_EXPAND_SZ into REG_SZ */
101     LPWSTR szData;
102 
103     /* If the caller didn't supply a buffer or the buffer is too small we have
104      * to allocate our own
105      */
106     if (dwRet == ERROR_MORE_DATA)
107     {
108       WCHAR cNull = '\0';
109       nBytesToAlloc = dwUnExpDataLen;
110 
111       szData = LocalAlloc(LMEM_ZEROINIT, nBytesToAlloc);
112       RegQueryValueExW (hKey, lpszValue, 0, NULL, (LPBYTE)szData, &nBytesToAlloc);
113       dwExpDataLen = ExpandEnvironmentStringsW(szData, &cNull, 1);
114       dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen);
115       LocalFree(szData);
116     }
117     else
118     {
119       nBytesToAlloc = (lstrlenW(pvData) + 1) * sizeof(WCHAR);
120       szData = LocalAlloc(LMEM_ZEROINIT, nBytesToAlloc );
121       lstrcpyW(szData, pvData);
122       dwExpDataLen = ExpandEnvironmentStringsW(szData, pvData, MAX_PATH );
123       if (dwExpDataLen > MAX_PATH) dwRet = ERROR_MORE_DATA;
124       dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen);
125       LocalFree(szData);
126     }
127   }
128 
129   return dwRet;
130 }
131 
132 static HRESULT UXTHEME_SetActiveTheme(PTHEME_FILE tf)
133 {
134     if(g_ActiveThemeFile)
135         MSSTYLES_CloseThemeFile(g_ActiveThemeFile);
136     g_ActiveThemeFile = tf;
137     if (g_ActiveThemeFile)
138     {
139         MSSTYLES_ReferenceTheme(g_ActiveThemeFile);
140         MSSTYLES_ParseThemeIni(g_ActiveThemeFile);
141     }
142     return S_OK;
143 }
144 
145 static BOOL bIsThemeActive(LPCWSTR pszTheme, LPCWSTR pszColor, LPCWSTR pszSize)
146 {
147     if (g_ActiveThemeFile == NULL)
148         return FALSE;
149 
150     if (wcscmp(pszTheme, g_ActiveThemeFile->szThemeFile) != 0)
151         return FALSE;
152 
153     if (!pszColor[0])
154     {
155         if (g_ActiveThemeFile->pszAvailColors != g_ActiveThemeFile->pszSelectedColor)
156             return FALSE;
157     }
158     else
159     {
160         if (wcscmp(pszColor, g_ActiveThemeFile->pszSelectedColor) != 0)
161             return FALSE;
162     }
163 
164     if (!pszSize[0])
165     {
166         if (g_ActiveThemeFile->pszAvailSizes != g_ActiveThemeFile->pszSelectedSize)
167             return FALSE;
168     }
169     else
170     {
171         if (wcscmp(pszSize, g_ActiveThemeFile->pszSelectedSize) != 0)
172             return FALSE;
173     }
174 
175     return TRUE;
176 }
177 
178 /***********************************************************************
179  *      UXTHEME_LoadTheme
180  *
181  * Set the current active theme from the registry
182  */
183 void UXTHEME_LoadTheme(BOOL bLoad)
184 {
185     HKEY hKey;
186     DWORD buffsize;
187     HRESULT hr;
188     WCHAR tmp[10];
189     PTHEME_FILE pt;
190     WCHAR szCurrentTheme[MAX_PATH];
191     WCHAR szCurrentColor[64];
192     WCHAR szCurrentSize[64];
193     BOOL bThemeActive = FALSE;
194 
195     if ((bLoad != FALSE) && g_bThemeHooksActive)
196     {
197         /* Get current theme configuration */
198         if(!RegOpenKeyW(HKEY_CURRENT_USER, szThemeManager, &hKey)) {
199             TRACE("Loading theme config\n");
200             buffsize = sizeof(tmp);
201             if(!RegQueryValueExW(hKey, szThemeActive, NULL, NULL, (LPBYTE)tmp, &buffsize)) {
202                 bThemeActive = (tmp[0] != '0');
203             }
204             else {
205                 bThemeActive = FALSE;
206                 TRACE("Failed to get ThemeActive: %d\n", GetLastError());
207             }
208             buffsize = sizeof(szCurrentColor);
209             if(RegQueryValueExW(hKey, szColorName, NULL, NULL, (LPBYTE)szCurrentColor, &buffsize))
210                 szCurrentColor[0] = '\0';
211             buffsize = sizeof(szCurrentSize);
212             if(RegQueryValueExW(hKey, szSizeName, NULL, NULL, (LPBYTE)szCurrentSize, &buffsize))
213                 szCurrentSize[0] = '\0';
214             if (query_reg_path (hKey, szDllName, szCurrentTheme))
215                 szCurrentTheme[0] = '\0';
216             RegCloseKey(hKey);
217         }
218         else
219             TRACE("Failed to open theme registry key\n");
220     }
221     else
222     {
223         bThemeActive = FALSE;
224     }
225 
226     if(bThemeActive)
227     {
228         if( bIsThemeActive(szCurrentTheme, szCurrentColor, szCurrentSize) )
229         {
230             TRACE("Tried to load active theme again\n");
231             return;
232         }
233 
234         /* Make sure the theme requested is actually valid */
235         hr = MSSTYLES_OpenThemeFile(szCurrentTheme,
236                                     szCurrentColor[0]?szCurrentColor:NULL,
237                                     szCurrentSize[0]?szCurrentSize:NULL,
238                                     &pt);
239         if(FAILED(hr)) {
240             bThemeActive = FALSE;
241         }
242         else {
243             TRACE("Theme active: %s %s %s\n", debugstr_w(szCurrentTheme),
244                 debugstr_w(szCurrentColor), debugstr_w(szCurrentSize));
245 
246             UXTHEME_SetActiveTheme(pt);
247             MSSTYLES_CloseThemeFile(pt);
248         }
249     }
250     if(!bThemeActive) {
251         UXTHEME_SetActiveTheme(NULL);
252         TRACE("Theming not active\n");
253     }
254 }
255 
256 /***********************************************************************/
257 
258 static const char * const SysColorsNames[] =
259 {
260     "Scrollbar",                /* COLOR_SCROLLBAR */
261     "Background",               /* COLOR_BACKGROUND */
262     "ActiveTitle",              /* COLOR_ACTIVECAPTION */
263     "InactiveTitle",            /* COLOR_INACTIVECAPTION */
264     "Menu",                     /* COLOR_MENU */
265     "Window",                   /* COLOR_WINDOW */
266     "WindowFrame",              /* COLOR_WINDOWFRAME */
267     "MenuText",                 /* COLOR_MENUTEXT */
268     "WindowText",               /* COLOR_WINDOWTEXT */
269     "TitleText",                /* COLOR_CAPTIONTEXT */
270     "ActiveBorder",             /* COLOR_ACTIVEBORDER */
271     "InactiveBorder",           /* COLOR_INACTIVEBORDER */
272     "AppWorkSpace",             /* COLOR_APPWORKSPACE */
273     "Hilight",                  /* COLOR_HIGHLIGHT */
274     "HilightText",              /* COLOR_HIGHLIGHTTEXT */
275     "ButtonFace",               /* COLOR_BTNFACE */
276     "ButtonShadow",             /* COLOR_BTNSHADOW */
277     "GrayText",                 /* COLOR_GRAYTEXT */
278     "ButtonText",               /* COLOR_BTNTEXT */
279     "InactiveTitleText",        /* COLOR_INACTIVECAPTIONTEXT */
280     "ButtonHilight",            /* COLOR_BTNHIGHLIGHT */
281     "ButtonDkShadow",           /* COLOR_3DDKSHADOW */
282     "ButtonLight",              /* COLOR_3DLIGHT */
283     "InfoText",                 /* COLOR_INFOTEXT */
284     "InfoWindow",               /* COLOR_INFOBK */
285     "ButtonAlternateFace",      /* COLOR_ALTERNATEBTNFACE */
286     "HotTrackingColor",         /* COLOR_HOTLIGHT */
287     "GradientActiveTitle",      /* COLOR_GRADIENTACTIVECAPTION */
288     "GradientInactiveTitle",    /* COLOR_GRADIENTINACTIVECAPTION */
289     "MenuHilight",              /* COLOR_MENUHILIGHT */
290     "MenuBar",                  /* COLOR_MENUBAR */
291 };
292 static const WCHAR strColorKey[] =
293     { 'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\',
294       'C','o','l','o','r','s',0 };
295 static const WCHAR keyFlatMenus[] = { 'F','l','a','t','M','e','n','u', 0};
296 static const WCHAR keyGradientCaption[] = { 'G','r','a','d','i','e','n','t',
297                                             'C','a','p','t','i','o','n', 0 };
298 static const WCHAR keyNonClientMetrics[] = { 'N','o','n','C','l','i','e','n','t',
299                                              'M','e','t','r','i','c','s',0 };
300 static const WCHAR keyIconTitleFont[] = { 'I','c','o','n','T','i','t','l','e',
301 					  'F','o','n','t',0 };
302 
303 static const struct BackupSysParam
304 {
305     int spiGet, spiSet;
306     const WCHAR* keyName;
307 } backupSysParams[] =
308 {
309     {SPI_GETFLATMENU, SPI_SETFLATMENU, keyFlatMenus},
310     {SPI_GETGRADIENTCAPTIONS, SPI_SETGRADIENTCAPTIONS, keyGradientCaption},
311     {-1, -1, 0}
312 };
313 
314 #define NUM_SYS_COLORS     (COLOR_MENUBAR+1)
315 
316 static void save_sys_colors (HKEY baseKey)
317 {
318     char colorStr[13];
319     HKEY hKey;
320     int i;
321 
322     if (RegCreateKeyExW( baseKey, strColorKey,
323                          0, 0, 0, KEY_ALL_ACCESS,
324                          0, &hKey, 0 ) == ERROR_SUCCESS)
325     {
326         for (i = 0; i < NUM_SYS_COLORS; i++)
327         {
328             COLORREF col = GetSysColor (i);
329 
330             sprintf (colorStr, "%d %d %d",
331                 GetRValue (col), GetGValue (col), GetBValue (col));
332 
333             RegSetValueExA (hKey, SysColorsNames[i], 0, REG_SZ,
334                 (BYTE*)colorStr, strlen (colorStr)+1);
335         }
336         RegCloseKey (hKey);
337     }
338 }
339 
340 /* Before activating a theme, query current system colors, certain settings
341  * and backup them in the registry, so they can be restored when the theme
342  * is deactivated */
343 static void UXTHEME_BackupSystemMetrics(void)
344 {
345     HKEY hKey;
346     const struct BackupSysParam* bsp = backupSysParams;
347 
348     if (RegCreateKeyExW( HKEY_CURRENT_USER, szThemeManager,
349                          0, 0, 0, KEY_ALL_ACCESS,
350                          0, &hKey, 0) == ERROR_SUCCESS)
351     {
352         NONCLIENTMETRICSW ncm;
353         LOGFONTW iconTitleFont;
354 
355         /* back up colors */
356         save_sys_colors (hKey);
357 
358         /* back up "other" settings */
359         while (bsp->spiGet >= 0)
360         {
361             DWORD value;
362 
363             SystemParametersInfoW (bsp->spiGet, 0, &value, 0);
364             RegSetValueExW (hKey, bsp->keyName, 0, REG_DWORD,
365                 (LPBYTE)&value, sizeof (value));
366 
367             bsp++;
368         }
369 
370 	/* back up non-client metrics */
371         memset (&ncm, 0, sizeof (ncm));
372         ncm.cbSize = sizeof (ncm);
373         SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, sizeof (ncm), &ncm, 0);
374         RegSetValueExW (hKey, keyNonClientMetrics, 0, REG_BINARY, (LPBYTE)&ncm,
375             sizeof (ncm));
376 	memset (&iconTitleFont, 0, sizeof (iconTitleFont));
377 	SystemParametersInfoW (SPI_GETICONTITLELOGFONT, sizeof (iconTitleFont),
378 	    &iconTitleFont, 0);
379 	RegSetValueExW (hKey, keyIconTitleFont, 0, REG_BINARY,
380 	    (LPBYTE)&iconTitleFont, sizeof (iconTitleFont));
381 
382         RegCloseKey (hKey);
383     }
384 }
385 
386 /* Read back old settings after a theme was deactivated */
387 static void UXTHEME_RestoreSystemMetrics(void)
388 {
389     HKEY hKey;
390     const struct BackupSysParam* bsp = backupSysParams;
391 
392     if (RegOpenKeyExW (HKEY_CURRENT_USER, szThemeManager,
393                        0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
394     {
395         HKEY colorKey;
396 
397         /* read backed-up colors */
398         if (RegOpenKeyExW (hKey, strColorKey,
399                            0, KEY_QUERY_VALUE, &colorKey) == ERROR_SUCCESS)
400         {
401             int i;
402             COLORREF sysCols[NUM_SYS_COLORS];
403             int sysColsIndices[NUM_SYS_COLORS];
404             int sysColCount = 0;
405 
406             for (i = 0; i < NUM_SYS_COLORS; i++)
407             {
408                 DWORD type;
409                 char colorStr[13];
410                 DWORD count = sizeof(colorStr);
411 
412                 if (RegQueryValueExA (colorKey, SysColorsNames[i], 0,
413                     &type, (LPBYTE) colorStr, &count) == ERROR_SUCCESS)
414                 {
415                     int r, g, b;
416                     if (sscanf (colorStr, "%d %d %d", &r, &g, &b) == 3)
417                     {
418                         sysColsIndices[sysColCount] = i;
419                         sysCols[sysColCount] = RGB(r, g, b);
420                         sysColCount++;
421                     }
422                 }
423             }
424             RegCloseKey (colorKey);
425 
426             SetSysColors (sysColCount, sysColsIndices, sysCols);
427         }
428 
429         /* read backed-up other settings */
430         while (bsp->spiGet >= 0)
431         {
432             DWORD value;
433             DWORD count = sizeof(value);
434             DWORD type;
435 
436             if (RegQueryValueExW (hKey, bsp->keyName, 0,
437                 &type, (LPBYTE)&value, &count) == ERROR_SUCCESS)
438             {
439                 SystemParametersInfoW (bsp->spiSet, 0, UlongToPtr(value), SPIF_UPDATEINIFILE);
440             }
441 
442             bsp++;
443         }
444 
445         /* read backed-up non-client metrics */
446         {
447             NONCLIENTMETRICSW ncm;
448             LOGFONTW iconTitleFont;
449             DWORD count = sizeof(ncm);
450             DWORD type;
451 
452 	    if (RegQueryValueExW (hKey, keyNonClientMetrics, 0,
453 		&type, (LPBYTE)&ncm, &count) == ERROR_SUCCESS)
454 	    {
455 		SystemParametersInfoW (SPI_SETNONCLIENTMETRICS,
456                     count, &ncm, SPIF_UPDATEINIFILE);
457 	    }
458 
459             count = sizeof(iconTitleFont);
460 
461 	    if (RegQueryValueExW (hKey, keyIconTitleFont, 0,
462 		&type, (LPBYTE)&iconTitleFont, &count) == ERROR_SUCCESS)
463 	    {
464 		SystemParametersInfoW (SPI_SETICONTITLELOGFONT,
465                     count, &iconTitleFont, SPIF_UPDATEINIFILE);
466 	    }
467 	}
468 
469         RegCloseKey (hKey);
470     }
471 }
472 
473 /* Make system settings persistent, so they're in effect even w/o uxtheme
474  * loaded.
475  * For efficiency reasons, only the last SystemParametersInfoW sets
476  * SPIF_SENDWININICHANGE */
477 static void UXTHEME_SaveSystemMetrics(void)
478 {
479     const struct BackupSysParam* bsp = backupSysParams;
480     NONCLIENTMETRICSW ncm;
481     LOGFONTW iconTitleFont;
482 
483     save_sys_colors (HKEY_CURRENT_USER);
484 
485     while (bsp->spiGet >= 0)
486     {
487         DWORD value;
488 
489         SystemParametersInfoW (bsp->spiGet, 0, &value, 0);
490         SystemParametersInfoW (bsp->spiSet, 0, UlongToPtr(value), SPIF_UPDATEINIFILE);
491         bsp++;
492     }
493 
494     memset (&ncm, 0, sizeof (ncm));
495     ncm.cbSize = sizeof (ncm);
496     SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, sizeof (ncm), &ncm, 0);
497     SystemParametersInfoW (SPI_SETNONCLIENTMETRICS, sizeof (ncm), &ncm,
498         SPIF_UPDATEINIFILE);
499 
500     memset (&iconTitleFont, 0, sizeof (iconTitleFont));
501     SystemParametersInfoW (SPI_GETICONTITLELOGFONT, sizeof (iconTitleFont),
502         &iconTitleFont, 0);
503     SystemParametersInfoW (SPI_SETICONTITLELOGFONT, sizeof (iconTitleFont),
504         &iconTitleFont, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
505 }
506 
507 /***********************************************************************
508  *      UXTHEME_ApplyTheme
509  *
510  * Change the current active theme
511  */
512 static HRESULT UXTHEME_ApplyTheme(PTHEME_FILE tf)
513 {
514     HKEY hKey;
515     WCHAR tmp[2];
516     HRESULT hr;
517 
518     TRACE("UXTHEME_ApplyTheme\n");
519 
520     if (tf && !g_ActiveThemeFile)
521     {
522         UXTHEME_BackupSystemMetrics();
523     }
524 
525     hr = UXTHEME_SetActiveTheme(tf);
526     if (FAILED(hr))
527         return hr;
528 
529     if (!tf)
530     {
531         UXTHEME_RestoreSystemMetrics();
532     }
533 
534     TRACE("Writing theme config to registry\n");
535     if(!RegCreateKeyW(HKEY_CURRENT_USER, szThemeManager, &hKey)) {
536         tmp[0] = g_ActiveThemeFile ? '1' : '0';
537         tmp[1] = '\0';
538         RegSetValueExW(hKey, szThemeActive, 0, REG_SZ, (const BYTE*)tmp, sizeof(WCHAR)*2);
539         if (g_ActiveThemeFile) {
540             RegSetValueExW(hKey, szColorName, 0, REG_SZ, (const BYTE*)tf->pszSelectedColor,
541 		(lstrlenW(tf->pszSelectedColor)+1)*sizeof(WCHAR));
542             RegSetValueExW(hKey, szSizeName, 0, REG_SZ, (const BYTE*)tf->pszSelectedSize,
543 		(lstrlenW(tf->pszSelectedSize)+1)*sizeof(WCHAR));
544             RegSetValueExW(hKey, szDllName, 0, REG_SZ, (const BYTE*)tf->szThemeFile,
545 		(lstrlenW(tf->szThemeFile)+1)*sizeof(WCHAR));
546         }
547         else {
548             RegDeleteValueW(hKey, szColorName);
549             RegDeleteValueW(hKey, szSizeName);
550             RegDeleteValueW(hKey, szDllName);
551 
552         }
553         RegCloseKey(hKey);
554     }
555     else
556         TRACE("Failed to open theme registry key\n");
557 
558     UXTHEME_SaveSystemMetrics ();
559 
560     return hr;
561 }
562 
563 /***********************************************************************
564  *      UXTHEME_InitSystem
565  */
566 void UXTHEME_InitSystem(HINSTANCE hInst)
567 {
568     static const WCHAR szWindowTheme[] = {
569         'u','x','_','t','h','e','m','e','\0'
570     };
571     static const WCHAR szSubAppName[] = {
572         'u','x','_','s','u','b','a','p','p','\0'
573     };
574     static const WCHAR szSubIdList[] = {
575         'u','x','_','s','u','b','i','d','l','s','t','\0'
576     };
577     static const WCHAR szDialogThemeEnabled[] = {
578         'u','x','_','d','i','a','l','o','g','t','h','e','m','e','\0'
579     };
580 
581     hDllInst = hInst;
582 
583     atWindowTheme        = GlobalAddAtomW(szWindowTheme);
584     atSubAppName         = GlobalAddAtomW(szSubAppName);
585     atSubIdList          = GlobalAddAtomW(szSubIdList);
586     atDialogThemeEnabled = GlobalAddAtomW(szDialogThemeEnabled);
587     atWndContext        = GlobalAddAtomW(L"ux_WndContext");
588 
589     RtlInitializeHandleTable(0xFFF, sizeof(UXTHEME_HANDLE), &g_UxThemeHandleTable);
590     g_cHandles = 0;
591 }
592 
593 /***********************************************************************
594  *      IsAppThemed                                         (UXTHEME.@)
595  */
596 BOOL WINAPI IsAppThemed(void)
597 {
598     TRACE("\n");
599     SetLastError(ERROR_SUCCESS);
600     return (g_ActiveThemeFile != NULL);
601 }
602 
603 /***********************************************************************
604  *      IsThemeActive                                       (UXTHEME.@)
605  */
606 BOOL WINAPI IsThemeActive(void)
607 {
608     BOOL bActive;
609     LRESULT Result;
610     HKEY hKey;
611     WCHAR tmp[10];
612     DWORD buffsize;
613 
614     TRACE("IsThemeActive\n");
615     SetLastError(ERROR_SUCCESS);
616 
617     if (g_ActiveThemeFile)
618         return TRUE;
619 
620     if (g_bThemeHooksActive)
621         return FALSE;
622 
623     bActive = FALSE;
624     Result = RegOpenKeyW(HKEY_CURRENT_USER, szThemeManager, &hKey);
625     if (Result == ERROR_SUCCESS)
626     {
627         buffsize = sizeof(tmp);
628         if (!RegQueryValueExW(hKey, szThemeActive, NULL, NULL, (LPBYTE)tmp, &buffsize))
629             bActive = (tmp[0] != '0');
630         RegCloseKey(hKey);
631     }
632 
633     return bActive;
634 }
635 
636 /***********************************************************************
637  *      EnableTheming                                       (UXTHEME.@)
638  *
639  * NOTES
640  * This is a global and persistent change
641  */
642 HRESULT WINAPI EnableTheming(BOOL fEnable)
643 {
644     HKEY hKey;
645     WCHAR szEnabled[] = {'0','\0'};
646 
647     TRACE("(%d)\n", fEnable);
648 
649     if (fEnable != (g_ActiveThemeFile != NULL)) {
650         if(fEnable)
651             UXTHEME_BackupSystemMetrics();
652         else
653             UXTHEME_RestoreSystemMetrics();
654         UXTHEME_SaveSystemMetrics ();
655 
656         if (fEnable) szEnabled[0] = '1';
657         if(!RegOpenKeyW(HKEY_CURRENT_USER, szThemeManager, &hKey)) {
658             RegSetValueExW(hKey, szThemeActive, 0, REG_SZ, (LPBYTE)szEnabled, sizeof(WCHAR));
659             RegCloseKey(hKey);
660         }
661 	UXTHEME_broadcast_msg (NULL, WM_THEMECHANGED);
662     }
663     return S_OK;
664 }
665 
666 /***********************************************************************
667  *      UXTHEME_SetWindowProperty
668  *
669  * I'm using atoms as there may be large numbers of duplicated strings
670  * and they do the work of keeping memory down as a cause of that quite nicely
671  */
672 static HRESULT UXTHEME_SetWindowProperty(HWND hwnd, ATOM aProp, LPCWSTR pszValue)
673 {
674     ATOM oldValue = (ATOM)(size_t)RemovePropW(hwnd, (LPCWSTR)MAKEINTATOM(aProp));
675     if(oldValue)
676     {
677         DeleteAtom(oldValue);
678     }
679 
680     if(pszValue)
681     {
682         ATOM atValue;
683 
684         /* A string with zero lenght is not acceptatble in AddAtomW but we want to support
685            users passing an empty string meaning they want no theme for the specified window */
686         if(!pszValue[0])
687             pszValue = L"0";
688 
689         atValue = AddAtomW(pszValue);
690         if (!atValue)
691         {
692             ERR("AddAtomW for %S failed with last error %d!\n", pszValue, GetLastError());
693             return HRESULT_FROM_WIN32(GetLastError());
694         }
695 
696         if (!SetPropW(hwnd, (LPCWSTR)MAKEINTATOM(aProp), (LPWSTR)MAKEINTATOM(atValue)))
697         {
698             ERR("SetPropW for atom %d failed with last error %d\n", aProp, GetLastError());
699             if(atValue) DeleteAtom(atValue);
700             return HRESULT_FROM_WIN32(GetLastError());
701         }
702     }
703     return S_OK;
704 }
705 
706 static LPWSTR UXTHEME_GetWindowProperty(HWND hwnd, ATOM aProp, LPWSTR pszBuffer, int dwLen)
707 {
708     ATOM atValue = (ATOM)(size_t)GetPropW(hwnd, (LPCWSTR)MAKEINTATOM(aProp));
709     if(atValue) {
710         if(GetAtomNameW(atValue, pszBuffer, dwLen))
711             return pszBuffer;
712         TRACE("property defined, but unable to get value\n");
713     }
714     return NULL;
715 }
716 
717 PTHEME_CLASS ValidateHandle(HTHEME hTheme)
718 {
719     PUXTHEME_HANDLE pHandle;
720 
721     if (!g_bThemeHooksActive || !hTheme || hTheme == INVALID_HANDLE_VALUE)
722         return NULL;
723 
724     if (!RtlIsValidHandle(&g_UxThemeHandleTable, (PRTL_HANDLE_TABLE_ENTRY)hTheme))
725     {
726         ERR("Invalid handle 0x%x!\n", hTheme);
727         return NULL;
728     }
729 
730     pHandle = hTheme;
731     return pHandle->pClass;
732 }
733 
734 static HTHEME WINAPI
735 OpenThemeDataInternal(PTHEME_FILE ThemeFile, HWND hwnd, LPCWSTR pszClassList, DWORD flags)
736 {
737     WCHAR szAppBuff[256];
738     WCHAR szClassBuff[256];
739     LPCWSTR pszAppName;
740     LPCWSTR pszUseClassList;
741     HTHEME hTheme = NULL;
742     TRACE("(%p,%s, %x)\n", hwnd, debugstr_w(pszClassList), flags);
743 
744     if(!pszClassList)
745     {
746         SetLastError(E_POINTER);
747         return NULL;
748     }
749 
750     if ((flags & OTD_NONCLIENT) && !(dwThemeAppProperties & STAP_ALLOW_NONCLIENT))
751     {
752         SetLastError(E_PROP_ID_UNSUPPORTED);
753         return NULL;
754     }
755 
756     if (!(flags & OTD_NONCLIENT) && !(dwThemeAppProperties & STAP_ALLOW_CONTROLS))
757     {
758         SetLastError(E_PROP_ID_UNSUPPORTED);
759         return NULL;
760     }
761 
762     if (ThemeFile)
763     {
764         pszAppName = UXTHEME_GetWindowProperty(hwnd, atSubAppName, szAppBuff, sizeof(szAppBuff)/sizeof(szAppBuff[0]));
765         /* If SetWindowTheme was used on the window, that overrides the class list passed to this function */
766         pszUseClassList = UXTHEME_GetWindowProperty(hwnd, atSubIdList, szClassBuff, sizeof(szClassBuff)/sizeof(szClassBuff[0]));
767         if(!pszUseClassList)
768             pszUseClassList = pszClassList;
769 
770          if (pszUseClassList)
771          {
772             PTHEME_CLASS pClass;
773             PUXTHEME_HANDLE pHandle;
774 
775              if (!ThemeFile->classes)
776                  MSSTYLES_ParseThemeIni(ThemeFile);
777             pClass = MSSTYLES_OpenThemeClass(ThemeFile, pszAppName, pszUseClassList);
778 
779             if (pClass)
780             {
781                 pHandle = (PUXTHEME_HANDLE)RtlAllocateHandle(&g_UxThemeHandleTable, NULL);
782                 if (pHandle)
783                 {
784                     g_cHandles++;
785                     TRACE("Created handle 0x%x for class 0x%x, app %S, class %S. Count: %d\n", pHandle, pClass, pszAppName, pszUseClassList, g_cHandles);
786                     pHandle->pClass = pClass;
787                     pHandle->Handle.Flags = RTL_HANDLE_VALID;
788                     hTheme = pHandle;
789                 }
790                 else
791                 {
792                     MSSTYLES_CloseThemeClass(pClass);
793                 }
794             }
795         }
796     }
797 
798     if(IsWindow(hwnd))
799     {
800         if ((flags & OTD_NONCLIENT) == 0)
801         {
802             SetPropW(hwnd, (LPCWSTR)MAKEINTATOM(atWindowTheme), hTheme);
803         }
804     }
805     else
806     {
807         SetLastError(E_PROP_ID_UNSUPPORTED);
808     }
809 
810     SetLastError(hTheme ? ERROR_SUCCESS : E_PROP_ID_UNSUPPORTED);
811 
812     TRACE(" = %p\n", hTheme);
813     return hTheme;
814 }
815 
816 /***********************************************************************
817  *      OpenThemeDataEx                                     (UXTHEME.61)
818  */
819 HTHEME WINAPI OpenThemeDataEx(HWND hwnd, LPCWSTR pszClassList, DWORD flags)
820 {
821     return OpenThemeDataInternal(g_ActiveThemeFile, hwnd, pszClassList, flags);
822 }
823 
824 /***********************************************************************
825  *      OpenThemeDataFromFile                               (UXTHEME.16)
826  */
827 HTHEME WINAPI OpenThemeDataFromFile(HTHEMEFILE hThemeFile, HWND hwnd, LPCWSTR pszClassList, DWORD flags)
828 {
829     return OpenThemeDataInternal((PTHEME_FILE)hThemeFile, hwnd, pszClassList, flags);
830 }
831 
832 /***********************************************************************
833  *      OpenThemeData                                       (UXTHEME.@)
834  */
835 HTHEME WINAPI OpenThemeData(HWND hwnd, LPCWSTR classlist)
836 {
837     return OpenThemeDataInternal(g_ActiveThemeFile, hwnd, classlist, 0);
838 }
839 
840 /***********************************************************************
841  *      GetWindowTheme                                      (UXTHEME.@)
842  *
843  * Retrieve the last theme opened for a window.
844  *
845  * PARAMS
846  *  hwnd  [I] window to retrieve the theme for
847  *
848  * RETURNS
849  *  The most recent theme.
850  */
851 HTHEME WINAPI GetWindowTheme(HWND hwnd)
852 {
853     TRACE("(%p)\n", hwnd);
854 
855 	if(!IsWindow(hwnd))
856     {
857 		SetLastError(E_HANDLE);
858         return NULL;
859     }
860 
861     return GetPropW(hwnd, (LPCWSTR)MAKEINTATOM(atWindowTheme));
862 }
863 
864 /***********************************************************************
865  *      SetWindowTheme                                      (UXTHEME.@)
866  *
867  * Persistent through the life of the window, even after themes change
868  */
869 HRESULT WINAPI SetWindowTheme(HWND hwnd, LPCWSTR pszSubAppName,
870                               LPCWSTR pszSubIdList)
871 {
872     HRESULT hr;
873     TRACE("(%p,%s,%s)\n", hwnd, debugstr_w(pszSubAppName),
874         debugstr_w(pszSubIdList));
875 
876     if(!IsWindow(hwnd))
877         return E_HANDLE;
878 
879     hr = UXTHEME_SetWindowProperty(hwnd, atSubAppName, pszSubAppName);
880     if (!SUCCEEDED(hr))
881         return hr;
882 
883     hr = UXTHEME_SetWindowProperty(hwnd, atSubIdList, pszSubIdList);
884     if (!SUCCEEDED(hr))
885         return hr;
886 
887     UXTHEME_broadcast_msg (hwnd, WM_THEMECHANGED);
888     return hr;
889 }
890 
891 /***********************************************************************
892  *      GetCurrentThemeName                                 (UXTHEME.@)
893  */
894 HRESULT WINAPI GetCurrentThemeName(LPWSTR pszThemeFileName, int dwMaxNameChars,
895                                    LPWSTR pszColorBuff, int cchMaxColorChars,
896                                    LPWSTR pszSizeBuff, int cchMaxSizeChars)
897 {
898     int cchar;
899 
900     if(g_ActiveThemeFile == NULL)
901          return E_PROP_ID_UNSUPPORTED;
902 
903     if (pszThemeFileName && dwMaxNameChars)
904     {
905         cchar = lstrlenW(g_ActiveThemeFile->szThemeFile) + 1;
906         if(cchar > dwMaxNameChars)
907            return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
908         lstrcpynW(pszThemeFileName, g_ActiveThemeFile->szThemeFile, cchar);
909     }
910 
911     if (pszColorBuff && cchMaxColorChars)
912     {
913         cchar = lstrlenW(g_ActiveThemeFile->pszSelectedColor) + 1;
914         if(cchar > cchMaxColorChars)
915             return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
916         lstrcpynW(pszColorBuff, g_ActiveThemeFile->pszSelectedColor, cchar);
917     }
918 
919    if (pszSizeBuff && cchMaxSizeChars)
920     {
921         cchar = lstrlenW(g_ActiveThemeFile->pszSelectedSize) + 1;
922         if(cchar > cchMaxSizeChars)
923             return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
924         lstrcpynW(pszSizeBuff, g_ActiveThemeFile->pszSelectedSize, cchar);
925     }
926 
927     return S_OK;
928 }
929 
930 /***********************************************************************
931  *      GetThemeAppProperties                               (UXTHEME.@)
932  */
933 DWORD WINAPI GetThemeAppProperties(void)
934 {
935     return dwThemeAppProperties;
936 }
937 
938 /***********************************************************************
939  *      SetThemeAppProperties                               (UXTHEME.@)
940  */
941 void WINAPI SetThemeAppProperties(DWORD dwFlags)
942 {
943     TRACE("(0x%08x)\n", dwFlags);
944     dwThemeAppProperties = dwFlags;
945 }
946 
947 /***********************************************************************
948  *      CloseThemeData                                      (UXTHEME.@)
949  */
950 HRESULT WINAPI CloseThemeData(HTHEME hTheme)
951 {
952     PUXTHEME_HANDLE pHandle = hTheme;
953     HRESULT hr;
954 
955     TRACE("(%p)\n", hTheme);
956 
957     if (!RtlIsValidHandle(&g_UxThemeHandleTable, (PRTL_HANDLE_TABLE_ENTRY)hTheme))
958         return E_HANDLE;
959 
960     hr = MSSTYLES_CloseThemeClass(pHandle->pClass);
961     if (SUCCEEDED(hr))
962     {
963         RtlFreeHandle(&g_UxThemeHandleTable, (PRTL_HANDLE_TABLE_ENTRY)pHandle);
964         g_cHandles--;
965         TRACE("Destroying handle 0x%x for class 0x%x. Count: %d\n", pHandle, pHandle->pClass, g_cHandles);
966     }
967     return hr;
968 }
969 
970 /***********************************************************************
971  *      HitTestThemeBackground                              (UXTHEME.@)
972  */
973 HRESULT WINAPI HitTestThemeBackground(HTHEME hTheme, HDC hdc, int iPartId,
974                                      int iStateId, DWORD dwOptions,
975                                      const RECT *pRect, HRGN hrgn,
976                                      POINT ptTest, WORD *pwHitTestCode)
977 {
978     FIXME("%d %d 0x%08x: stub\n", iPartId, iStateId, dwOptions);
979     if (!ValidateHandle(hTheme))
980         return E_HANDLE;
981     return E_NOTIMPL;
982 }
983 
984 /***********************************************************************
985  *      IsThemePartDefined                                  (UXTHEME.@)
986  */
987 BOOL WINAPI IsThemePartDefined(HTHEME hTheme, int iPartId, int iStateId)
988 {
989     PTHEME_CLASS pClass;
990 
991     TRACE("(%p,%d,%d)\n", hTheme, iPartId, iStateId);
992 
993     pClass = ValidateHandle(hTheme);
994     if (!pClass)
995     {
996         SetLastError(E_HANDLE);
997         return FALSE;
998     }
999     if(MSSTYLES_FindPartState(pClass, iPartId, iStateId, NULL))
1000         return TRUE;
1001     return FALSE;
1002 }
1003 
1004 /***********************************************************************
1005  *      GetThemeDocumentationProperty                       (UXTHEME.@)
1006  *
1007  * Try and retrieve the documentation property from string resources
1008  * if that fails, get it from the [documentation] section of themes.ini
1009  */
1010 HRESULT WINAPI GetThemeDocumentationProperty(LPCWSTR pszThemeName,
1011                                              LPCWSTR pszPropertyName,
1012                                              LPWSTR pszValueBuff,
1013                                              int cchMaxValChars)
1014 {
1015     const WORD wDocToRes[] = {
1016         TMT_DISPLAYNAME,5000,
1017         TMT_TOOLTIP,5001,
1018         TMT_COMPANY,5002,
1019         TMT_AUTHOR,5003,
1020         TMT_COPYRIGHT,5004,
1021         TMT_URL,5005,
1022         TMT_VERSION,5006,
1023         TMT_DESCRIPTION,5007
1024     };
1025 
1026     PTHEME_FILE pt;
1027     HRESULT hr;
1028     unsigned int i;
1029     int iDocId;
1030     TRACE("(%s,%s,%p,%d)\n", debugstr_w(pszThemeName), debugstr_w(pszPropertyName),
1031           pszValueBuff, cchMaxValChars);
1032 
1033     if (!g_bThemeHooksActive)
1034         return E_FAIL;
1035 
1036     hr = MSSTYLES_OpenThemeFile(pszThemeName, NULL, NULL, &pt);
1037     if(FAILED(hr)) return hr;
1038 
1039     /* Try to load from string resources */
1040     hr = E_PROP_ID_UNSUPPORTED;
1041     if(MSSTYLES_LookupProperty(pszPropertyName, NULL, &iDocId)) {
1042         for(i=0; i<sizeof(wDocToRes)/sizeof(wDocToRes[0]); i+=2) {
1043             if(wDocToRes[i] == iDocId) {
1044                 if(LoadStringW(pt->hTheme, wDocToRes[i+1], pszValueBuff, cchMaxValChars)) {
1045                     hr = S_OK;
1046                     break;
1047                 }
1048             }
1049         }
1050     }
1051     /* If loading from string resource failed, try getting it from the theme.ini */
1052     if(FAILED(hr)) {
1053         PUXINI_FILE uf = MSSTYLES_GetThemeIni(pt);
1054         if(UXINI_FindSection(uf, szIniDocumentation)) {
1055             LPCWSTR lpValue;
1056             DWORD dwLen;
1057             if(UXINI_FindValue(uf, pszPropertyName, &lpValue, &dwLen)) {
1058                 lstrcpynW(pszValueBuff, lpValue, min(dwLen+1,cchMaxValChars));
1059                 hr = S_OK;
1060             }
1061         }
1062         UXINI_CloseINI(uf);
1063     }
1064 
1065     MSSTYLES_CloseThemeFile(pt);
1066     return hr;
1067 }
1068 
1069 /**********************************************************************
1070  *      Undocumented functions
1071  */
1072 
1073 /**********************************************************************
1074  *      QueryThemeServices                                 (UXTHEME.1)
1075  *
1076  * RETURNS
1077  *     some kind of status flag
1078  */
1079 DWORD WINAPI QueryThemeServices(void)
1080 {
1081     FIXME("stub\n");
1082     return 3; /* This is what is returned under XP in most cases */
1083 }
1084 
1085 
1086 /**********************************************************************
1087  *      OpenThemeFile                                      (UXTHEME.2)
1088  *
1089  * Opens a theme file, which can be used to change the current theme, etc
1090  *
1091  * PARAMS
1092  *     pszThemeFileName    Path to a msstyles theme file
1093  *     pszColorName        Color defined in the theme, eg. NormalColor
1094  *     pszSizeName         Size defined in the theme, eg. NormalSize
1095  *     hThemeFile          Handle to theme file
1096  *
1097  * RETURNS
1098  *     Success: S_OK
1099  *     Failure: HRESULT error-code
1100  */
1101 HRESULT WINAPI OpenThemeFile(LPCWSTR pszThemeFileName, LPCWSTR pszColorName,
1102                              LPCWSTR pszSizeName, HTHEMEFILE *hThemeFile,
1103                              DWORD unknown)
1104 {
1105     TRACE("(%s,%s,%s,%p,%d)\n", debugstr_w(pszThemeFileName),
1106           debugstr_w(pszColorName), debugstr_w(pszSizeName),
1107           hThemeFile, unknown);
1108 
1109     if (!g_bThemeHooksActive)
1110         return E_FAIL;
1111 
1112     return MSSTYLES_OpenThemeFile(pszThemeFileName, pszColorName, pszSizeName, (PTHEME_FILE*)hThemeFile);
1113 }
1114 
1115 /**********************************************************************
1116  *      CloseThemeFile                                     (UXTHEME.3)
1117  *
1118  * Releases theme file handle returned by OpenThemeFile
1119  *
1120  * PARAMS
1121  *     hThemeFile           Handle to theme file
1122  *
1123  * RETURNS
1124  *     Success: S_OK
1125  *     Failure: HRESULT error-code
1126  */
1127 HRESULT WINAPI CloseThemeFile(HTHEMEFILE hThemeFile)
1128 {
1129     TRACE("(%p)\n", hThemeFile);
1130     MSSTYLES_CloseThemeFile(hThemeFile);
1131     return S_OK;
1132 }
1133 
1134 /**********************************************************************
1135  *      ApplyTheme                                         (UXTHEME.4)
1136  *
1137  * Set a theme file to be the currently active theme
1138  *
1139  * PARAMS
1140  *     hThemeFile           Handle to theme file
1141  *     unknown              See notes
1142  *     hWnd                 Window requesting the theme change
1143  *
1144  * RETURNS
1145  *     Success: S_OK
1146  *     Failure: HRESULT error-code
1147  *
1148  * NOTES
1149  * I'm not sure what the second parameter is (the datatype is likely wrong), other then this:
1150  * Under XP if I pass
1151  * char b[] = "";
1152  *   the theme is applied with the screen redrawing really badly (flickers)
1153  * char b[] = "\0"; where \0 can be one or more of any character, makes no difference
1154  *   the theme is applied smoothly (screen does not flicker)
1155  * char *b = "\0" or NULL; where \0 can be zero or more of any character, makes no difference
1156  *   the function fails returning invalid parameter... very strange
1157  */
1158 HRESULT WINAPI ApplyTheme(HTHEMEFILE hThemeFile, char *unknown, HWND hWnd)
1159 {
1160     HRESULT hr;
1161     TRACE("(%p,%s,%p)\n", hThemeFile, unknown, hWnd);
1162     hr = UXTHEME_ApplyTheme(hThemeFile);
1163     UXTHEME_broadcast_msg (NULL, WM_THEMECHANGED);
1164     return hr;
1165 }
1166 
1167 /**********************************************************************
1168  *      GetThemeDefaults                                   (UXTHEME.7)
1169  *
1170  * Get the default color & size for a theme
1171  *
1172  * PARAMS
1173  *     pszThemeFileName    Path to a msstyles theme file
1174  *     pszColorName        Buffer to receive the default color name
1175  *     dwColorNameLen      Length, in characters, of color name buffer
1176  *     pszSizeName         Buffer to receive the default size name
1177  *     dwSizeNameLen       Length, in characters, of size name buffer
1178  *
1179  * RETURNS
1180  *     Success: S_OK
1181  *     Failure: HRESULT error-code
1182  */
1183 HRESULT WINAPI GetThemeDefaults(LPCWSTR pszThemeFileName, LPWSTR pszColorName,
1184                                 DWORD dwColorNameLen, LPWSTR pszSizeName,
1185                                 DWORD dwSizeNameLen)
1186 {
1187     PTHEME_FILE pt;
1188     HRESULT hr;
1189     TRACE("(%s,%p,%d,%p,%d)\n", debugstr_w(pszThemeFileName),
1190           pszColorName, dwColorNameLen,
1191           pszSizeName, dwSizeNameLen);
1192 
1193     if (!g_bThemeHooksActive)
1194         return E_FAIL;
1195 
1196     hr = MSSTYLES_OpenThemeFile(pszThemeFileName, NULL, NULL, &pt);
1197     if(FAILED(hr)) return hr;
1198 
1199     lstrcpynW(pszColorName, pt->pszSelectedColor, dwColorNameLen);
1200     lstrcpynW(pszSizeName, pt->pszSelectedSize, dwSizeNameLen);
1201 
1202     MSSTYLES_CloseThemeFile(pt);
1203     return S_OK;
1204 }
1205 
1206 /**********************************************************************
1207  *      EnumThemes                                         (UXTHEME.8)
1208  *
1209  * Enumerate available themes, calls specified EnumThemeProc for each
1210  * theme found. Passes lpData through to callback function.
1211  *
1212  * PARAMS
1213  *     pszThemePath        Path containing themes
1214  *     callback            Called for each theme found in path
1215  *     lpData              Passed through to callback
1216  *
1217  * RETURNS
1218  *     Success: S_OK
1219  *     Failure: HRESULT error-code
1220  */
1221 HRESULT WINAPI EnumThemes(LPCWSTR pszThemePath, ENUMTHEMEPROC callback,
1222                           LPVOID lpData)
1223 {
1224     WCHAR szDir[MAX_PATH];
1225     WCHAR szPath[MAX_PATH];
1226     static const WCHAR szStar[] = {'*','.','*','\0'};
1227     static const WCHAR szFormat[] = {'%','s','%','s','\\','%','s','.','m','s','s','t','y','l','e','s','\0'};
1228     static const WCHAR szDisplayName[] = {'d','i','s','p','l','a','y','n','a','m','e','\0'};
1229     static const WCHAR szTooltip[] = {'t','o','o','l','t','i','p','\0'};
1230     WCHAR szName[60];
1231     WCHAR szTip[60];
1232     HANDLE hFind;
1233     WIN32_FIND_DATAW wfd;
1234     HRESULT hr;
1235     size_t pathLen;
1236 
1237     TRACE("(%s,%p,%p)\n", debugstr_w(pszThemePath), callback, lpData);
1238 
1239     if(!pszThemePath || !callback)
1240         return E_POINTER;
1241 
1242     lstrcpyW(szDir, pszThemePath);
1243     pathLen = lstrlenW (szDir);
1244     if ((pathLen > 0) && (pathLen < MAX_PATH-1) && (szDir[pathLen - 1] != '\\'))
1245     {
1246         szDir[pathLen] = '\\';
1247         szDir[pathLen+1] = 0;
1248     }
1249 
1250     lstrcpyW(szPath, szDir);
1251     lstrcatW(szPath, szStar);
1252     TRACE("searching %s\n", debugstr_w(szPath));
1253 
1254     hFind = FindFirstFileW(szPath, &wfd);
1255     if(hFind != INVALID_HANDLE_VALUE) {
1256         do {
1257             if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
1258                && !(wfd.cFileName[0] == '.' && ((wfd.cFileName[1] == '.' && wfd.cFileName[2] == 0) || wfd.cFileName[1] == 0))) {
1259                 wsprintfW(szPath, szFormat, szDir, wfd.cFileName, wfd.cFileName);
1260 
1261                 hr = GetThemeDocumentationProperty(szPath, szDisplayName, szName, sizeof(szName)/sizeof(szName[0]));
1262                 if(SUCCEEDED(hr))
1263                     hr = GetThemeDocumentationProperty(szPath, szTooltip, szTip, sizeof(szTip)/sizeof(szTip[0]));
1264                 if(SUCCEEDED(hr)) {
1265                     TRACE("callback(%s,%s,%s,%p)\n", debugstr_w(szPath), debugstr_w(szName), debugstr_w(szTip), lpData);
1266                     if(!callback(NULL, szPath, szName, szTip, NULL, lpData)) {
1267                         TRACE("callback ended enum\n");
1268                         break;
1269                     }
1270                 }
1271             }
1272         } while(FindNextFileW(hFind, &wfd));
1273         FindClose(hFind);
1274     }
1275     return S_OK;
1276 }
1277 
1278 
1279 /**********************************************************************
1280  *      EnumThemeColors                                    (UXTHEME.9)
1281  *
1282  * Enumerate theme colors available with a particular size
1283  *
1284  * PARAMS
1285  *     pszThemeFileName    Path to a msstyles theme file
1286  *     pszSizeName         Theme size to enumerate available colors
1287  *                         If NULL the default theme size is used
1288  *     dwColorNum          Color index to retrieve, increment from 0
1289  *     pszColorNames       Output color names
1290  *
1291  * RETURNS
1292  *     S_OK on success
1293  *     E_PROP_ID_UNSUPPORTED when dwColorName does not refer to a color
1294  *          or when pszSizeName does not refer to a valid size
1295  *
1296  * NOTES
1297  * XP fails with E_POINTER when pszColorNames points to a buffer smaller than
1298  * sizeof(THEMENAMES).
1299  *
1300  * Not very efficient that I'm opening & validating the theme every call, but
1301  * this is undocumented and almost never called..
1302  * (and this is how windows works too)
1303  */
1304 HRESULT WINAPI EnumThemeColors(LPWSTR pszThemeFileName, LPWSTR pszSizeName,
1305                                DWORD dwColorNum, PTHEMENAMES pszColorNames)
1306 {
1307     PTHEME_FILE pt;
1308     HRESULT hr;
1309     LPWSTR tmp;
1310     UINT resourceId = dwColorNum + 1000;
1311     TRACE("(%s,%s,%d)\n", debugstr_w(pszThemeFileName),
1312           debugstr_w(pszSizeName), dwColorNum);
1313 
1314     if (!g_bThemeHooksActive)
1315         return E_FAIL;
1316 
1317     hr = MSSTYLES_OpenThemeFile(pszThemeFileName, NULL, pszSizeName, &pt);
1318     if(FAILED(hr)) return hr;
1319 
1320     tmp = pt->pszAvailColors;
1321     while(dwColorNum && *tmp) {
1322         dwColorNum--;
1323         tmp += lstrlenW(tmp)+1;
1324     }
1325     if(!dwColorNum && *tmp) {
1326         TRACE("%s\n", debugstr_w(tmp));
1327         lstrcpyW(pszColorNames->szName, tmp);
1328         LoadStringW (pt->hTheme, resourceId,
1329             pszColorNames->szDisplayName,
1330             sizeof (pszColorNames->szDisplayName) / sizeof (WCHAR));
1331         LoadStringW (pt->hTheme, resourceId+1000,
1332             pszColorNames->szTooltip,
1333             sizeof (pszColorNames->szTooltip) / sizeof (WCHAR));
1334     }
1335     else
1336         hr = E_PROP_ID_UNSUPPORTED;
1337 
1338     MSSTYLES_CloseThemeFile(pt);
1339     return hr;
1340 }
1341 
1342 /**********************************************************************
1343  *      EnumThemeSizes                                     (UXTHEME.10)
1344  *
1345  * Enumerate theme colors available with a particular size
1346  *
1347  * PARAMS
1348  *     pszThemeFileName    Path to a msstyles theme file
1349  *     pszColorName        Theme color to enumerate available sizes
1350  *                         If NULL the default theme color is used
1351  *     dwSizeNum           Size index to retrieve, increment from 0
1352  *     pszSizeNames        Output size names
1353  *
1354  * RETURNS
1355  *     S_OK on success
1356  *     E_PROP_ID_UNSUPPORTED when dwSizeName does not refer to a size
1357  *          or when pszColorName does not refer to a valid color
1358  *
1359  * NOTES
1360  * XP fails with E_POINTER when pszSizeNames points to a buffer smaller than
1361  * sizeof(THEMENAMES).
1362  *
1363  * Not very efficient that I'm opening & validating the theme every call, but
1364  * this is undocumented and almost never called..
1365  * (and this is how windows works too)
1366  */
1367 HRESULT WINAPI EnumThemeSizes(LPWSTR pszThemeFileName, LPWSTR pszColorName,
1368                               DWORD dwSizeNum, PTHEMENAMES pszSizeNames)
1369 {
1370     PTHEME_FILE pt;
1371     HRESULT hr;
1372     LPWSTR tmp;
1373     UINT resourceId = dwSizeNum + 3000;
1374     TRACE("(%s,%s,%d)\n", debugstr_w(pszThemeFileName),
1375           debugstr_w(pszColorName), dwSizeNum);
1376 
1377     if (!g_bThemeHooksActive)
1378         return E_FAIL;
1379 
1380     hr = MSSTYLES_OpenThemeFile(pszThemeFileName, pszColorName, NULL, &pt);
1381     if(FAILED(hr)) return hr;
1382 
1383     tmp = pt->pszAvailSizes;
1384     while(dwSizeNum && *tmp) {
1385         dwSizeNum--;
1386         tmp += lstrlenW(tmp)+1;
1387     }
1388     if(!dwSizeNum && *tmp) {
1389         TRACE("%s\n", debugstr_w(tmp));
1390         lstrcpyW(pszSizeNames->szName, tmp);
1391         LoadStringW (pt->hTheme, resourceId,
1392             pszSizeNames->szDisplayName,
1393             sizeof (pszSizeNames->szDisplayName) / sizeof (WCHAR));
1394         LoadStringW (pt->hTheme, resourceId+1000,
1395             pszSizeNames->szTooltip,
1396             sizeof (pszSizeNames->szTooltip) / sizeof (WCHAR));
1397     }
1398     else
1399         hr = E_PROP_ID_UNSUPPORTED;
1400 
1401     MSSTYLES_CloseThemeFile(pt);
1402     return hr;
1403 }
1404 
1405 /**********************************************************************
1406  *      ParseThemeIniFile                                  (UXTHEME.11)
1407  *
1408  * Enumerate data in a theme INI file.
1409  *
1410  * PARAMS
1411  *     pszIniFileName      Path to a theme ini file
1412  *     pszUnknown          Cannot be NULL, L"" is valid
1413  *     callback            Called for each found entry
1414  *     lpData              Passed through to callback
1415  *
1416  * RETURNS
1417  *     S_OK on success
1418  *     0x800706488 (Unknown property) when enumeration is canceled from callback
1419  *
1420  * NOTES
1421  * When pszUnknown is NULL the callback is never called, the value does not seem to serve
1422  * any other purpose
1423  */
1424 HRESULT WINAPI ParseThemeIniFile(LPCWSTR pszIniFileName, LPWSTR pszUnknown,
1425                                  PARSETHEMEINIFILEPROC callback, LPVOID lpData)
1426 {
1427     FIXME("%s %s: stub\n", debugstr_w(pszIniFileName), debugstr_w(pszUnknown));
1428     return E_NOTIMPL;
1429 }
1430 
1431 /**********************************************************************
1432  *      CheckThemeSignature                                (UXTHEME.29)
1433  *
1434  * Validates the signature of a theme file
1435  *
1436  * PARAMS
1437  *     pszIniFileName      Path to a theme file
1438  *
1439  * RETURNS
1440  *     Success: S_OK
1441  *     Failure: HRESULT error-code
1442  */
1443 HRESULT WINAPI CheckThemeSignature(LPCWSTR pszThemeFileName)
1444 {
1445     PTHEME_FILE pt;
1446     HRESULT hr;
1447     TRACE("(%s)\n", debugstr_w(pszThemeFileName));
1448 
1449     if (!g_bThemeHooksActive)
1450         return E_FAIL;
1451 
1452     hr = MSSTYLES_OpenThemeFile(pszThemeFileName, NULL, NULL, &pt);
1453     if(FAILED(hr))
1454         return hr;
1455     MSSTYLES_CloseThemeFile(pt);
1456     return S_OK;
1457 }
1458