xref: /reactos/dll/win32/uxtheme/msstyles.c (revision 565bf9e3)
1 /*
2  * Win32 5.1 msstyles theme format
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 <wine/unicode.h>
24 
25 /***********************************************************************
26  * Defines and global variables
27  */
28 
29 extern int alphaBlendMode;
30 
31 #define MSSTYLES_VERSION 0x0003
32 
33 static const WCHAR szThemesIniResource[] = {
34     't','h','e','m','e','s','_','i','n','i','\0'
35 };
36 
37 /***********************************************************************/
38 
39 /**********************************************************************
40  *      MSSTYLES_OpenThemeFile
41  *
42  * Load and validate a theme
43  *
44  * PARAMS
45  *     lpThemeFile         Path to theme file to load
46  *     pszColorName        Color name wanted, can be NULL
47  *     pszSizeName         Size name wanted, can be NULL
48  *
49  * NOTES
50  * If pszColorName or pszSizeName are NULL, the default color/size will be used.
51  * If one/both are provided, they are validated against valid color/sizes and if
52  * a match is not found, the function fails.
53  */
54 HRESULT MSSTYLES_OpenThemeFile(LPCWSTR lpThemeFile, LPCWSTR pszColorName, LPCWSTR pszSizeName, PTHEME_FILE *tf)
55 {
56     HMODULE hTheme;
57     HRSRC hrsc;
58     HRESULT hr = S_OK;
59     static const WCHAR szPackThemVersionResource[] = {
60         'P','A','C','K','T','H','E','M','_','V','E','R','S','I','O','N', '\0'
61     };
62     static const WCHAR szColorNamesResource[] = {
63         'C','O','L','O','R','N','A','M','E','S','\0'
64     };
65     static const WCHAR szSizeNamesResource[] = {
66         'S','I','Z','E','N','A','M','E','S','\0'
67     };
68 
69     WORD version;
70     DWORD versize;
71     LPWSTR pszColors;
72     LPWSTR pszSelectedColor = NULL;
73     LPWSTR pszSizes;
74     LPWSTR pszSelectedSize = NULL;
75     LPWSTR tmp;
76 
77     TRACE("Opening %s\n", debugstr_w(lpThemeFile));
78 
79     hTheme = LoadLibraryExW(lpThemeFile, NULL, LOAD_LIBRARY_AS_DATAFILE);
80 
81     /* Validate that this is really a theme */
82     if(!hTheme) {
83         hr = HRESULT_FROM_WIN32(GetLastError());
84         goto invalid_theme;
85     }
86     if(!(hrsc = FindResourceW(hTheme, MAKEINTRESOURCEW(1), szPackThemVersionResource))) {
87         TRACE("No version resource found\n");
88         hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
89         goto invalid_theme;
90     }
91     if((versize = SizeofResource(hTheme, hrsc)) != 2)
92     {
93         TRACE("Version resource found, but wrong size: %d\n", versize);
94         hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
95         goto invalid_theme;
96     }
97     version = *(WORD*)LoadResource(hTheme, hrsc);
98     if(version != MSSTYLES_VERSION)
99     {
100         TRACE("Version of theme file is unsupported: 0x%04x\n", version);
101         hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
102         goto invalid_theme;
103     }
104 
105     if(!(hrsc = FindResourceW(hTheme, MAKEINTRESOURCEW(1), szColorNamesResource))) {
106         TRACE("Color names resource not found\n");
107         hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
108         goto invalid_theme;
109     }
110     pszColors = LoadResource(hTheme, hrsc);
111 
112     if(!(hrsc = FindResourceW(hTheme, MAKEINTRESOURCEW(1), szSizeNamesResource))) {
113         TRACE("Size names resource not found\n");
114         hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
115         goto invalid_theme;
116     }
117     pszSizes = LoadResource(hTheme, hrsc);
118 
119     /* Validate requested color against what's available from the theme */
120     if(pszColorName) {
121         tmp = pszColors;
122         while(*tmp) {
123             if(!lstrcmpiW(pszColorName, tmp)) {
124                 pszSelectedColor = tmp;
125                 break;
126             }
127             tmp += lstrlenW(tmp)+1;
128         }
129     }
130     else
131         pszSelectedColor = pszColors; /* Use the default color */
132 
133     /* Validate requested size against what's available from the theme */
134     if(pszSizeName) {
135         tmp = pszSizes;
136         while(*tmp) {
137             if(!lstrcmpiW(pszSizeName, tmp)) {
138                 pszSelectedSize = tmp;
139                 break;
140             }
141             tmp += lstrlenW(tmp)+1;
142         }
143     }
144     else
145         pszSelectedSize = pszSizes; /* Use the default size */
146 
147     if(!pszSelectedColor || !pszSelectedSize) {
148         TRACE("Requested color/size (%s/%s) not found in theme\n",
149               debugstr_w(pszColorName), debugstr_w(pszSizeName));
150         hr = E_PROP_ID_UNSUPPORTED;
151         goto invalid_theme;
152     }
153 
154     *tf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(THEME_FILE));
155     (*tf)->hTheme = hTheme;
156 
157     GetFullPathNameW(lpThemeFile, MAX_PATH, (*tf)->szThemeFile, NULL);
158 
159     (*tf)->pszAvailColors = pszColors;
160     (*tf)->pszAvailSizes = pszSizes;
161     (*tf)->pszSelectedColor = pszSelectedColor;
162     (*tf)->pszSelectedSize = pszSelectedSize;
163     (*tf)->dwRefCount = 1;
164 
165     TRACE("Theme %p refcount: %d\n", *tf, (*tf)->dwRefCount);
166 
167     return S_OK;
168 
169 invalid_theme:
170     if(hTheme) FreeLibrary(hTheme);
171     return hr;
172 }
173 
174 /***********************************************************************
175  *      MSSTYLES_CloseThemeFile
176  *
177  * Close theme file and free resources
178  */
179 void MSSTYLES_CloseThemeFile(PTHEME_FILE tf)
180 {
181     if(tf) {
182 
183         tf->dwRefCount--;
184         TRACE("Theme %p refcount: %d\n", tf, tf->dwRefCount);
185 
186         if(!tf->dwRefCount) {
187             if(tf->hTheme) FreeLibrary(tf->hTheme);
188             if(tf->classes) {
189                 while(tf->classes) {
190                     PTHEME_CLASS pcls = tf->classes;
191                     tf->classes = pcls->next;
192                     while(pcls->partstate) {
193                         PTHEME_PARTSTATE ps = pcls->partstate;
194 
195                         while(ps->properties) {
196                             PTHEME_PROPERTY prop = ps->properties;
197                             ps->properties = prop->next;
198                             HeapFree(GetProcessHeap(), 0, prop);
199                         }
200 
201                         pcls->partstate = ps->next;
202                         HeapFree(GetProcessHeap(), 0, ps);
203                     }
204                     HeapFree(GetProcessHeap(), 0, pcls);
205                 }
206             }
207             while (tf->images)
208             {
209                 PTHEME_IMAGE img = tf->images;
210                 tf->images = img->next;
211                 DeleteObject (img->image);
212                 HeapFree (GetProcessHeap(), 0, img);
213             }
214             HeapFree(GetProcessHeap(), 0, tf);
215         }
216     }
217 }
218 
219 /***********************************************************************
220  *      MSSTYLES_ReferenceTheme
221  *
222  * Increase the reference count of the theme file
223  */
224 HRESULT MSSTYLES_ReferenceTheme(PTHEME_FILE tf)
225 {
226     tf->dwRefCount++;
227     TRACE("Theme %p refcount: %d\n", tf, tf->dwRefCount);
228     return S_OK;
229 }
230 
231 /***********************************************************************
232  *      MSSTYLES_GetThemeIni
233  *
234  * Retrieves themes.ini from a theme
235  */
236 PUXINI_FILE MSSTYLES_GetThemeIni(PTHEME_FILE tf)
237 {
238     return UXINI_LoadINI(tf->hTheme, szThemesIniResource);
239 }
240 
241 /***********************************************************************
242  *      MSSTYLES_GetActiveThemeIni
243  *
244  * Retrieve the ini file for the selected color/style
245  */
246 static PUXINI_FILE MSSTYLES_GetActiveThemeIni(PTHEME_FILE tf)
247 {
248     static const WCHAR szFileResNamesResource[] = {
249         'F','I','L','E','R','E','S','N','A','M','E','S','\0'
250     };
251     DWORD dwColorCount = 0;
252     DWORD dwSizeCount = 0;
253     DWORD dwColorNum = 0;
254     DWORD dwSizeNum = 0;
255     DWORD i;
256     DWORD dwResourceIndex;
257     LPWSTR tmp;
258     HRSRC hrsc;
259 
260     /* Count the number of available colors & styles, and determine the index number
261        of the color/style we are interested in
262     */
263     tmp = tf->pszAvailColors;
264     while(*tmp) {
265         if(!lstrcmpiW(tf->pszSelectedColor, tmp))
266             dwColorNum = dwColorCount;
267         tmp += lstrlenW(tmp)+1;
268         dwColorCount++;
269     }
270     tmp = tf->pszAvailSizes;
271     while(*tmp) {
272         if(!lstrcmpiW(tf->pszSelectedSize, tmp))
273             dwSizeNum = dwSizeCount;
274         tmp += lstrlenW(tmp)+1;
275         dwSizeCount++;
276     }
277 
278     if(!(hrsc = FindResourceW(tf->hTheme, MAKEINTRESOURCEW(1), szFileResNamesResource))) {
279         TRACE("FILERESNAMES map not found\n");
280         return NULL;
281     }
282     tmp = LoadResource(tf->hTheme, hrsc);
283     dwResourceIndex = (dwSizeCount * dwColorNum) + dwSizeNum;
284     for(i=0; i < dwResourceIndex; i++) {
285         tmp += lstrlenW(tmp)+1;
286     }
287     return UXINI_LoadINI(tf->hTheme, tmp);
288 }
289 
290 
291 /***********************************************************************
292  *      MSSTYLES_ParseIniSectionName
293  *
294  * Parse an ini section name into its component parts
295  * Valid formats are:
296  * [classname]
297  * [classname(state)]
298  * [classname.part]
299  * [classname.part(state)]
300  * [application::classname]
301  * [application::classname(state)]
302  * [application::classname.part]
303  * [application::classname.part(state)]
304  *
305  * PARAMS
306  *     lpSection           Section name
307  *     dwLen               Length of section name
308  *     szAppName           Location to store application name
309  *     szClassName         Location to store class name
310  *     iPartId             Location to store part id
311  *     iStateId            Location to store state id
312  */
313 static BOOL MSSTYLES_ParseIniSectionName(LPCWSTR lpSection, DWORD dwLen, LPWSTR szAppName, LPWSTR szClassName, int *iPartId, int *iStateId)
314 {
315     WCHAR sec[255];
316     WCHAR part[60] = {'\0'};
317     WCHAR state[60] = {'\0'};
318     LPWSTR tmp;
319     LPWSTR comp;
320     lstrcpynW(sec, lpSection, min(dwLen+1, sizeof(sec)/sizeof(sec[0])));
321 
322     *szAppName = 0;
323     *szClassName = 0;
324     *iPartId = 0;
325     *iStateId = 0;
326     comp = sec;
327     /* Get the application name */
328     tmp = strchrW(comp, ':');
329     if(tmp) {
330         *tmp++ = 0;
331         tmp++;
332         lstrcpynW(szAppName, comp, MAX_THEME_APP_NAME);
333         comp = tmp;
334     }
335 
336     tmp = strchrW(comp, '.');
337     if(tmp) {
338         *tmp++ = 0;
339         lstrcpynW(szClassName, comp, MAX_THEME_CLASS_NAME);
340         comp = tmp;
341         /* now get the part & state */
342         tmp = strchrW(comp, '(');
343         if(tmp) {
344             *tmp++ = 0;
345             lstrcpynW(part, comp, sizeof(part)/sizeof(part[0]));
346             comp = tmp;
347             /* now get the state */
348             tmp = strchrW(comp, ')');
349             if (!tmp)
350                 return FALSE;
351             *tmp = 0;
352             lstrcpynW(state, comp, sizeof(state)/sizeof(state[0]));
353         }
354         else {
355             lstrcpynW(part, comp, sizeof(part)/sizeof(part[0]));
356         }
357     }
358     else {
359         tmp = strchrW(comp, '(');
360         if(tmp) {
361             *tmp++ = 0;
362             lstrcpynW(szClassName, comp, MAX_THEME_CLASS_NAME);
363             comp = tmp;
364             /* now get the state */
365             tmp = strchrW(comp, ')');
366             if (!tmp)
367                 return FALSE;
368             *tmp = 0;
369             lstrcpynW(state, comp, sizeof(state)/sizeof(state[0]));
370         }
371         else {
372             lstrcpynW(szClassName, comp, MAX_THEME_CLASS_NAME);
373         }
374     }
375     if(!*szClassName) return FALSE;
376     return MSSTYLES_LookupPartState(szClassName, part[0]?part:NULL, state[0]?state:NULL, iPartId, iStateId);
377 }
378 
379 /***********************************************************************
380  *      MSSTYLES_FindClass
381  *
382  * Find a class
383  *
384  * PARAMS
385  *     tf                  Theme file
386  *     pszAppName          App name to find
387  *     pszClassName        Class name to find
388  *
389  * RETURNS
390  *  The class found, or NULL
391  */
392 static PTHEME_CLASS MSSTYLES_FindClass(PTHEME_FILE tf, LPCWSTR pszAppName, LPCWSTR pszClassName)
393 {
394     PTHEME_CLASS cur = tf->classes;
395     while(cur) {
396         if(!pszAppName) {
397             if(!*cur->szAppName && !lstrcmpiW(pszClassName, cur->szClassName))
398                 return cur;
399         }
400         else {
401             if(!lstrcmpiW(pszAppName, cur->szAppName) && !lstrcmpiW(pszClassName, cur->szClassName))
402                 return cur;
403         }
404         cur = cur->next;
405     }
406     return NULL;
407 }
408 
409 /***********************************************************************
410  *      MSSTYLES_AddClass
411  *
412  * Add a class to a theme file
413  *
414  * PARAMS
415  *     tf                  Theme file
416  *     pszAppName          App name to add
417  *     pszClassName        Class name to add
418  *
419  * RETURNS
420  *  The class added, or a class previously added with the same name
421  */
422 static PTHEME_CLASS MSSTYLES_AddClass(PTHEME_FILE tf, LPCWSTR pszAppName, LPCWSTR pszClassName)
423 {
424     PTHEME_CLASS cur = MSSTYLES_FindClass(tf, pszAppName, pszClassName);
425     if(cur) return cur;
426 
427     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_CLASS));
428     cur->hTheme = tf->hTheme;
429     lstrcpyW(cur->szAppName, pszAppName);
430     lstrcpyW(cur->szClassName, pszClassName);
431     cur->next = tf->classes;
432     cur->partstate = NULL;
433     cur->overrides = NULL;
434     tf->classes = cur;
435     return cur;
436 }
437 
438 /***********************************************************************
439  *      MSSTYLES_FindPartState
440  *
441  * Find a part/state
442  *
443  * PARAMS
444  *     tc                  Class to search
445  *     iPartId             Part ID to find
446  *     iStateId            State ID to find
447  *     tcNext              Receives the next class in the override chain
448  *
449  * RETURNS
450  *  The part/state found, or NULL
451  */
452 PTHEME_PARTSTATE MSSTYLES_FindPartState(PTHEME_CLASS tc, int iPartId, int iStateId, PTHEME_CLASS *tcNext)
453 {
454     PTHEME_PARTSTATE cur = tc->partstate;
455     while(cur) {
456         if(cur->iPartId == iPartId && cur->iStateId == iStateId) {
457             if(tcNext) *tcNext = tc->overrides;
458             return cur;
459         }
460         cur = cur->next;
461     }
462     if(tc->overrides) return MSSTYLES_FindPartState(tc->overrides, iPartId, iStateId, tcNext);
463     return NULL;
464 }
465 
466 /***********************************************************************
467  *      MSSTYLES_AddPartState
468  *
469  * Add a part/state to a class
470  *
471  * PARAMS
472  *     tc                  Theme class
473  *     iPartId             Part ID to add
474  *     iStateId            State ID to add
475  *
476  * RETURNS
477  *  The part/state added, or a part/state previously added with the same IDs
478  */
479 static PTHEME_PARTSTATE MSSTYLES_AddPartState(PTHEME_CLASS tc, int iPartId, int iStateId)
480 {
481     PTHEME_PARTSTATE cur = MSSTYLES_FindPartState(tc, iPartId, iStateId, NULL);
482     if(cur) return cur;
483 
484     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_PARTSTATE));
485     cur->iPartId = iPartId;
486     cur->iStateId = iStateId;
487     cur->properties = NULL;
488     cur->next = tc->partstate;
489     tc->partstate = cur;
490     return cur;
491 }
492 
493 /***********************************************************************
494  *      MSSTYLES_LFindProperty
495  *
496  * Find a property within a property list
497  *
498  * PARAMS
499  *     tp                  property list to scan
500  *     iPropertyPrimitive  Type of value expected
501  *     iPropertyId         ID of the required value
502  *
503  * RETURNS
504  *  The property found, or NULL
505  */
506 static PTHEME_PROPERTY MSSTYLES_LFindProperty(PTHEME_PROPERTY tp, int iPropertyPrimitive, int iPropertyId)
507 {
508     PTHEME_PROPERTY cur = tp;
509     while(cur) {
510         if(cur->iPropertyId == iPropertyId) {
511             if(cur->iPrimitiveType == iPropertyPrimitive) {
512                 return cur;
513             }
514             else {
515                 if(!iPropertyPrimitive)
516                     return cur;
517                 return NULL;
518             }
519         }
520         cur = cur->next;
521     }
522     return NULL;
523 }
524 
525 /***********************************************************************
526  *      MSSTYLES_PSFindProperty
527  *
528  * Find a value within a part/state
529  *
530  * PARAMS
531  *     ps                  Part/state to search
532  *     iPropertyPrimitive  Type of value expected
533  *     iPropertyId         ID of the required value
534  *
535  * RETURNS
536  *  The property found, or NULL
537  */
538 static inline PTHEME_PROPERTY MSSTYLES_PSFindProperty(PTHEME_PARTSTATE ps, int iPropertyPrimitive, int iPropertyId)
539 {
540     return MSSTYLES_LFindProperty(ps->properties, iPropertyPrimitive, iPropertyId);
541 }
542 
543 /***********************************************************************
544  *      MSSTYLES_FindMetric
545  *
546  * Find a metric property for a theme file
547  *
548  * PARAMS
549  *     tf                  Theme file
550  *     iPropertyPrimitive  Type of value expected
551  *     iPropertyId         ID of the required value
552  *
553  * RETURNS
554  *  The property found, or NULL
555  */
556 PTHEME_PROPERTY MSSTYLES_FindMetric(PTHEME_FILE tf, int iPropertyPrimitive, int iPropertyId)
557 {
558     return MSSTYLES_LFindProperty(tf->metrics, iPropertyPrimitive, iPropertyId);
559 }
560 
561 /***********************************************************************
562  *      MSSTYLES_AddProperty
563  *
564  * Add a property to a part/state
565  *
566  * PARAMS
567  *     ps                  Part/state
568  *     iPropertyPrimitive  Primitive type of the property
569  *     iPropertyId         ID of the property
570  *     lpValue             Raw value (non-NULL terminated)
571  *     dwValueLen          Length of the value
572  *
573  * RETURNS
574  *  The property added, or a property previously added with the same IDs
575  */
576 static PTHEME_PROPERTY MSSTYLES_AddProperty(PTHEME_PARTSTATE ps, int iPropertyPrimitive, int iPropertyId, LPCWSTR lpValue, DWORD dwValueLen, BOOL isGlobal)
577 {
578     PTHEME_PROPERTY cur = MSSTYLES_PSFindProperty(ps, iPropertyPrimitive, iPropertyId);
579     /* Should duplicate properties overwrite the original, or be ignored? */
580     if(cur) return cur;
581 
582     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_PROPERTY));
583     cur->iPrimitiveType = iPropertyPrimitive;
584     cur->iPropertyId = iPropertyId;
585     cur->lpValue = lpValue;
586     cur->dwValueLen = dwValueLen;
587 
588     if(ps->iStateId)
589         cur->origin = PO_STATE;
590     else if(ps->iPartId)
591         cur->origin = PO_PART;
592     else if(isGlobal)
593         cur->origin = PO_GLOBAL;
594     else
595         cur->origin = PO_CLASS;
596 
597     cur->next = ps->properties;
598     ps->properties = cur;
599     return cur;
600 }
601 
602 /***********************************************************************
603  *      MSSTYLES_AddMetric
604  *
605  * Add a property to a part/state
606  *
607  * PARAMS
608  *     tf                  Theme file
609  *     iPropertyPrimitive  Primitive type of the property
610  *     iPropertyId         ID of the property
611  *     lpValue             Raw value (non-NULL terminated)
612  *     dwValueLen          Length of the value
613  *
614  * RETURNS
615  *  The property added, or a property previously added with the same IDs
616  */
617 static PTHEME_PROPERTY MSSTYLES_AddMetric(PTHEME_FILE tf, int iPropertyPrimitive, int iPropertyId, LPCWSTR lpValue, DWORD dwValueLen)
618 {
619     PTHEME_PROPERTY cur = MSSTYLES_FindMetric(tf, iPropertyPrimitive, iPropertyId);
620     /* Should duplicate properties overwrite the original, or be ignored? */
621     if(cur) return cur;
622 
623     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_PROPERTY));
624     cur->iPrimitiveType = iPropertyPrimitive;
625     cur->iPropertyId = iPropertyId;
626     cur->lpValue = lpValue;
627     cur->dwValueLen = dwValueLen;
628 
629     cur->origin = PO_GLOBAL;
630 
631     cur->next = tf->metrics;
632     tf->metrics = cur;
633     return cur;
634 }
635 
636 /***********************************************************************
637  *      MSSTYLES_ParseThemeIni
638  *
639  * Parse the theme ini for the selected color/style
640  *
641  * PARAMS
642  *     tf                  Theme to parse
643  */
644 void MSSTYLES_ParseThemeIni(PTHEME_FILE tf)
645 {
646     static const WCHAR szSysMetrics[] = {'S','y','s','M','e','t','r','i','c','s','\0'};
647     static const WCHAR szGlobals[] = {'g','l','o','b','a','l','s','\0'};
648     PTHEME_CLASS cls;
649     PTHEME_CLASS globals;
650     PTHEME_PARTSTATE ps;
651     PUXINI_FILE ini;
652     WCHAR szAppName[MAX_THEME_APP_NAME];
653     WCHAR szClassName[MAX_THEME_CLASS_NAME];
654     WCHAR szPropertyName[MAX_THEME_VALUE_NAME];
655     int iPartId;
656     int iStateId;
657     int iPropertyPrimitive;
658     int iPropertyId;
659     DWORD dwLen;
660     LPCWSTR lpName;
661     DWORD dwValueLen;
662     LPCWSTR lpValue;
663 
664     if(tf->classes)
665         return;
666 
667     ini = MSSTYLES_GetActiveThemeIni(tf);
668 
669     while((lpName=UXINI_GetNextSection(ini, &dwLen)))
670     {
671         if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpName, dwLen, szSysMetrics, -1) == CSTR_EQUAL)
672         {
673             while((lpName=UXINI_GetNextValue(ini, &dwLen, &lpValue, &dwValueLen)))
674             {
675                 lstrcpynW(szPropertyName, lpName, min(dwLen+1, sizeof(szPropertyName)/sizeof(szPropertyName[0])));
676                 if(MSSTYLES_LookupProperty(szPropertyName, &iPropertyPrimitive, &iPropertyId))
677                 {
678                    /* Catch all metrics, including colors */
679                    MSSTYLES_AddMetric(tf, iPropertyPrimitive, iPropertyId, lpValue, dwValueLen);
680                 }
681                 else
682                 {
683                     TRACE("Unknown system metric %s\n", debugstr_w(szPropertyName));
684                 }
685             }
686             continue;
687         }
688 
689         if(MSSTYLES_ParseIniSectionName(lpName, dwLen, szAppName, szClassName, &iPartId, &iStateId))
690         {
691             BOOL isGlobal = FALSE;
692             if(!lstrcmpiW(szClassName, szGlobals))
693             {
694                 isGlobal = TRUE;
695             }
696             cls = MSSTYLES_AddClass(tf, szAppName, szClassName);
697             ps = MSSTYLES_AddPartState(cls, iPartId, iStateId);
698 
699             while((lpName=UXINI_GetNextValue(ini, &dwLen, &lpValue, &dwValueLen)))
700             {
701                 lstrcpynW(szPropertyName, lpName, min(dwLen+1, sizeof(szPropertyName)/sizeof(szPropertyName[0])));
702                 if(MSSTYLES_LookupProperty(szPropertyName, &iPropertyPrimitive, &iPropertyId))
703                 {
704                     MSSTYLES_AddProperty(ps, iPropertyPrimitive, iPropertyId, lpValue, dwValueLen, isGlobal);
705                 }
706                 else
707                 {
708                     TRACE("Unknown property %s\n", debugstr_w(szPropertyName));
709                 }
710             }
711         }
712     }
713 
714     /* App/Class combos override values defined by the base class, map these overrides */
715     globals = MSSTYLES_FindClass(tf, NULL, szGlobals);
716     cls = tf->classes;
717     while(cls)
718     {
719         if(*cls->szAppName)
720         {
721             cls->overrides = MSSTYLES_FindClass(tf, NULL, cls->szClassName);
722             if(!cls->overrides)
723             {
724                 TRACE("No overrides found for app %s class %s\n", debugstr_w(cls->szAppName), debugstr_w(cls->szClassName));
725             }
726             else
727             {
728                 cls->overrides = globals;
729             }
730         }
731         else
732         {
733             /* Everything overrides globals..except globals */
734             if(cls != globals)
735                 cls->overrides = globals;
736         }
737         cls = cls->next;
738     }
739     UXINI_CloseINI(ini);
740 
741     if(!tf->classes) {
742         ERR("Failed to parse theme ini\n");
743     }
744 }
745 
746 /***********************************************************************
747  *      MSSTYLES_OpenThemeClass
748  *
749  * Open a theme class, uses the current active theme
750  *
751  * PARAMS
752  *     pszAppName          Application name, for theme styles specific
753  *                         to a particular application
754  *     pszClassList        List of requested classes, semicolon delimited
755  */
756 PTHEME_CLASS MSSTYLES_OpenThemeClass(PTHEME_FILE tf, LPCWSTR pszAppName, LPCWSTR pszClassList)
757 {
758     PTHEME_CLASS cls = NULL;
759 #ifdef __REACTOS__
760     PTHEME_CLASS defaultCls = NULL;
761 #endif
762     WCHAR szClassName[MAX_THEME_CLASS_NAME];
763     LPCWSTR start;
764     LPCWSTR end;
765     DWORD len;
766 
767     if(!tf->classes) {
768         return NULL;
769     }
770 
771     start = pszClassList;
772     while((end = strchrW(start, ';'))) {
773         len = end-start;
774         lstrcpynW(szClassName, start, min(len+1, sizeof(szClassName)/sizeof(szClassName[0])));
775         start = end+1;
776         cls = MSSTYLES_FindClass(tf, pszAppName, szClassName);
777         if(cls) break;
778 #ifdef __REACTOS__
779         if (!defaultCls)
780             defaultCls = MSSTYLES_FindClass(tf, NULL, szClassName);
781 #endif
782     }
783     if(!cls && *start) {
784         lstrcpynW(szClassName, start, sizeof(szClassName)/sizeof(szClassName[0]));
785         cls = MSSTYLES_FindClass(tf, pszAppName, szClassName);
786 #ifdef __REACTOS__
787         if (!defaultCls)
788             defaultCls = MSSTYLES_FindClass(tf, NULL, szClassName);
789 #endif
790     }
791     if(cls) {
792         TRACE("Opened app %s, class %s from list %s\n", debugstr_w(cls->szAppName), debugstr_w(cls->szClassName), debugstr_w(pszClassList));
793 	cls->tf = tf;
794 	cls->tf->dwRefCount++;
795     TRACE("Theme %p refcount: %d\n", tf, tf->dwRefCount);
796     }
797 #ifdef __REACTOS__
798     else if (defaultCls)
799     {
800         cls = defaultCls;
801         TRACE("Opened default class %s from list %s\n", debugstr_w(cls->szClassName), debugstr_w(pszClassList));
802         cls->tf = tf;
803         cls->tf->dwRefCount++;
804         TRACE("Theme %p refcount: %d\n", tf, tf->dwRefCount);
805     }
806 #endif
807     return cls;
808 }
809 
810 /***********************************************************************
811  *      MSSTYLES_CloseThemeClass
812  *
813  * Close a theme class
814  *
815  * PARAMS
816  *     tc                  Theme class to close
817  *
818  * NOTES
819  *  The MSSTYLES_CloseThemeFile decreases the refcount of the owning
820  *  theme file and cleans it up, if needed.
821  */
822 HRESULT MSSTYLES_CloseThemeClass(PTHEME_CLASS tc)
823 {
824     MSSTYLES_CloseThemeFile (tc->tf);
825     return S_OK;
826 }
827 
828 /***********************************************************************
829  *      MSSTYLES_FindProperty
830  *
831  * Locate a property in a class. Part and state IDs will be used as a
832  * preference, but may be ignored in the attempt to locate the property.
833  * Will scan the entire chain of overrides for this class.
834  */
835 PTHEME_PROPERTY MSSTYLES_FindProperty(PTHEME_CLASS tc, int iPartId, int iStateId, int iPropertyPrimitive, int iPropertyId)
836 {
837     PTHEME_CLASS next = tc;
838     PTHEME_PARTSTATE ps;
839     PTHEME_PROPERTY tp;
840 
841     TRACE("(%p, %d, %d, %d)\n", tc, iPartId, iStateId, iPropertyId);
842      /* Try and find an exact match on part & state */
843     while(next && (ps = MSSTYLES_FindPartState(next, iPartId, iStateId, &next))) {
844         if((tp = MSSTYLES_PSFindProperty(ps, iPropertyPrimitive, iPropertyId))) {
845             return tp;
846         }
847     }
848     /* If that fails, and we didn't already try it, search for just part */
849     if(iStateId != 0)
850         iStateId = 0;
851     /* As a last ditch attempt..go for just class */
852     else if(iPartId != 0)
853         iPartId = 0;
854     else
855         return NULL;
856 
857     if((tp = MSSTYLES_FindProperty(tc, iPartId, iStateId, iPropertyPrimitive, iPropertyId)))
858         return tp;
859     return NULL;
860 }
861 
862 /* Prepare a bitmap to be used for alpha blending */
863 static BOOL prepare_alpha (HBITMAP bmp, BOOL* hasAlpha)
864 {
865     DIBSECTION dib;
866     int n;
867     BYTE* p;
868 
869     *hasAlpha = FALSE;
870 
871     if (!bmp || GetObjectW( bmp, sizeof(dib), &dib ) != sizeof(dib))
872         return FALSE;
873 
874     if(dib.dsBm.bmBitsPixel != 32)
875         /* nothing to do */
876         return TRUE;
877 
878     p = dib.dsBm.bmBits;
879     n = dib.dsBmih.biHeight * dib.dsBmih.biWidth;
880     /* AlphaBlend() wants premultiplied alpha, so do that now */
881     while (n-- > 0)
882     {
883         int a = p[3]+1;
884         p[0] = (p[0] * a) >> 8;
885         p[1] = (p[1] * a) >> 8;
886         p[2] = (p[2] * a) >> 8;
887         p += 4;
888 
889         if (a != 256)
890             *hasAlpha = TRUE;
891     }
892 
893     return TRUE;
894 }
895 
896 HBITMAP MSSTYLES_LoadBitmap (PTHEME_CLASS tc, LPCWSTR lpFilename, BOOL* hasAlpha)
897 {
898     WCHAR szFile[MAX_PATH];
899     LPWSTR tmp;
900     PTHEME_IMAGE img;
901     lstrcpynW(szFile, lpFilename, sizeof(szFile)/sizeof(szFile[0]));
902     tmp = szFile;
903     do {
904         if(*tmp == '\\') *tmp = '_';
905         if(*tmp == '/') *tmp = '_';
906         if(*tmp == '.') *tmp = '_';
907     } while(*tmp++);
908 
909     /* Try to locate in list of loaded images */
910     img = tc->tf->images;
911     while (img)
912     {
913         if (lstrcmpiW (szFile, img->name) == 0)
914         {
915             TRACE ("found %p %s: %p\n", img, debugstr_w (img->name), img->image);
916             *hasAlpha = img->hasAlpha;
917             return img->image;
918         }
919         img = img->next;
920     }
921     /* Not found? Load from resources */
922     img = HeapAlloc (GetProcessHeap(), 0, sizeof (THEME_IMAGE));
923 #ifdef ENABLE_PNG_SUPPORT
924     if (MSSTYLES_TryLoadPng(tc->hTheme, szFile, TEXT(L"IMAGE"), &img->image)) // ...as PNG...
925     {
926         prepare_png_alpha(img->image, hasAlpha);
927     }
928     else // ...or, failing that, as BMP
929     {
930 #endif /* ENABLE_PNG_SUPPORT */
931     img->image = LoadImageW(tc->hTheme, szFile, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
932     prepare_alpha (img->image, hasAlpha);
933 #ifdef ENABLE_PNG_SUPPORT
934     }
935 #endif /* ENABLE_PNG_SUPPORT */
936     img->hasAlpha = *hasAlpha;
937     /* ...and stow away for later reuse. */
938     lstrcpyW (img->name, szFile);
939     img->next = tc->tf->images;
940     tc->tf->images = img;
941     TRACE ("new %p %s: %p\n", img, debugstr_w (img->name), img->image);
942     return img->image;
943 }
944 
945 static BOOL MSSTYLES_GetNextInteger(LPCWSTR lpStringStart, LPCWSTR lpStringEnd, LPCWSTR *lpValEnd, int *value)
946 {
947     LPCWSTR cur = lpStringStart;
948     int total = 0;
949     BOOL gotNeg = FALSE;
950 
951     while(cur < lpStringEnd && ((*cur < '0' || *cur > '9') && *cur != '-')) cur++;
952     if(cur >= lpStringEnd) {
953         return FALSE;
954     }
955     if(*cur == '-') {
956         cur++;
957         gotNeg = TRUE;
958     }
959     while(cur < lpStringEnd && (*cur >= '0' && *cur <= '9')) {
960         total = total * 10 + (*cur - '0');
961         cur++;
962     }
963     if(gotNeg) total = -total;
964     *value = total;
965     if(lpValEnd) *lpValEnd = cur;
966     return TRUE;
967 }
968 
969 static BOOL MSSTYLES_GetNextToken(LPCWSTR lpStringStart, LPCWSTR lpStringEnd, LPCWSTR *lpValEnd, LPWSTR lpBuff, DWORD buffSize) {
970     LPCWSTR cur = lpStringStart;
971     LPCWSTR start;
972     LPCWSTR end;
973 
974     while(cur < lpStringEnd && (isspace(*cur) || *cur == ',')) cur++;
975     if(cur >= lpStringEnd) {
976         return FALSE;
977     }
978     start = cur;
979     while(cur < lpStringEnd && *cur != '\n'&& *cur != ',') cur++;
980     end = cur;
981     while(isspace(*(end-1))) end--;
982 
983     lstrcpynW(lpBuff, start, min(buffSize, end-start+1));
984 
985     if(lpValEnd) *lpValEnd = cur;
986     return TRUE;
987 }
988 
989 /***********************************************************************
990  *      MSSTYLES_GetPropertyBool
991  *
992  * Retrieve a color value for a property
993  */
994 HRESULT MSSTYLES_GetPropertyBool(PTHEME_PROPERTY tp, BOOL *pfVal)
995 {
996     *pfVal = FALSE;
997     if(*tp->lpValue == 't' || *tp->lpValue == 'T')
998         *pfVal = TRUE;
999     return S_OK;
1000 }
1001 
1002 /***********************************************************************
1003  *      MSSTYLES_GetPropertyColor
1004  *
1005  * Retrieve a color value for a property
1006  */
1007 HRESULT MSSTYLES_GetPropertyColor(PTHEME_PROPERTY tp, COLORREF *pColor)
1008 {
1009     LPCWSTR lpEnd;
1010     LPCWSTR lpCur;
1011     int red, green, blue;
1012 
1013     lpCur = tp->lpValue;
1014     lpEnd = tp->lpValue + tp->dwValueLen;
1015 
1016     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &red)) {
1017         TRACE("Could not parse color property\n");
1018         return E_PROP_ID_UNSUPPORTED;
1019     }
1020     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &green)) {
1021         TRACE("Could not parse color property\n");
1022         return E_PROP_ID_UNSUPPORTED;
1023     }
1024     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &blue)) {
1025         TRACE("Could not parse color property\n");
1026         return E_PROP_ID_UNSUPPORTED;
1027     }
1028     *pColor = RGB(red,green,blue);
1029     return S_OK;
1030 }
1031 
1032 /***********************************************************************
1033  *      MSSTYLES_GetPropertyColor
1034  *
1035  * Retrieve a color value for a property
1036  */
1037 static HRESULT MSSTYLES_GetFont (LPCWSTR lpCur, LPCWSTR lpEnd,
1038                                  LPCWSTR *lpValEnd, LOGFONTW* pFont)
1039 {
1040     static const WCHAR szBold[] = {'b','o','l','d','\0'};
1041     static const WCHAR szItalic[] = {'i','t','a','l','i','c','\0'};
1042     static const WCHAR szUnderline[] = {'u','n','d','e','r','l','i','n','e','\0'};
1043     static const WCHAR szStrikeOut[] = {'s','t','r','i','k','e','o','u','t','\0'};
1044     int pointSize;
1045     WCHAR attr[32];
1046 
1047     if(!MSSTYLES_GetNextToken(lpCur, lpEnd, &lpCur, pFont->lfFaceName, LF_FACESIZE)) {
1048         TRACE("Property is there, but failed to get face name\n");
1049         *lpValEnd = lpCur;
1050         return E_PROP_ID_UNSUPPORTED;
1051     }
1052     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pointSize)) {
1053         TRACE("Property is there, but failed to get point size\n");
1054         *lpValEnd = lpCur;
1055         return E_PROP_ID_UNSUPPORTED;
1056     }
1057     if(pointSize > 0)
1058     {
1059         HDC hdc = GetDC(0);
1060         pointSize = -MulDiv(pointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1061         ReleaseDC(0, hdc);
1062     }
1063 
1064     pFont->lfHeight = pointSize;
1065     pFont->lfWeight = FW_REGULAR;
1066     pFont->lfCharSet = DEFAULT_CHARSET;
1067     while(MSSTYLES_GetNextToken(lpCur, lpEnd, &lpCur, attr, sizeof(attr)/sizeof(attr[0]))) {
1068         if(!lstrcmpiW(szBold, attr)) pFont->lfWeight = FW_BOLD;
1069         else if(!lstrcmpiW(szItalic, attr)) pFont->lfItalic = TRUE;
1070         else if(!lstrcmpiW(szUnderline, attr)) pFont->lfUnderline = TRUE;
1071         else if(!lstrcmpiW(szStrikeOut, attr)) pFont->lfStrikeOut = TRUE;
1072     }
1073     *lpValEnd = lpCur;
1074     return S_OK;
1075 }
1076 
1077 HRESULT MSSTYLES_GetPropertyFont(PTHEME_PROPERTY tp, HDC hdc, LOGFONTW *pFont)
1078 {
1079     LPCWSTR lpCur = tp->lpValue;
1080     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1081     HRESULT hr;
1082 
1083     ZeroMemory(pFont, sizeof(LOGFONTW));
1084     hr = MSSTYLES_GetFont (lpCur, lpEnd, &lpCur, pFont);
1085 
1086     return hr;
1087 }
1088 
1089 /***********************************************************************
1090  *      MSSTYLES_GetPropertyInt
1091  *
1092  * Retrieve an int value for a property
1093  */
1094 HRESULT MSSTYLES_GetPropertyInt(PTHEME_PROPERTY tp, int *piVal)
1095 {
1096     if(!MSSTYLES_GetNextInteger(tp->lpValue, (tp->lpValue + tp->dwValueLen), NULL, piVal)) {
1097         TRACE("Could not parse int property\n");
1098         return E_PROP_ID_UNSUPPORTED;
1099     }
1100     return S_OK;
1101 }
1102 
1103 /***********************************************************************
1104  *      MSSTYLES_GetPropertyIntList
1105  *
1106  * Retrieve an int list value for a property
1107  */
1108 HRESULT MSSTYLES_GetPropertyIntList(PTHEME_PROPERTY tp, INTLIST *pIntList)
1109 {
1110     int i;
1111     LPCWSTR lpCur = tp->lpValue;
1112     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1113 
1114     for(i=0; i < MAX_INTLIST_COUNT; i++) {
1115         if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pIntList->iValues[i]))
1116             break;
1117     }
1118     pIntList->iValueCount = i;
1119     return S_OK;
1120 }
1121 
1122 /***********************************************************************
1123  *      MSSTYLES_GetPropertyPosition
1124  *
1125  * Retrieve a position value for a property
1126  */
1127 HRESULT MSSTYLES_GetPropertyPosition(PTHEME_PROPERTY tp, POINT *pPoint)
1128 {
1129     int x,y;
1130     LPCWSTR lpCur = tp->lpValue;
1131     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1132 
1133     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &x)) {
1134         TRACE("Could not parse position property\n");
1135         return E_PROP_ID_UNSUPPORTED;
1136     }
1137     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &y)) {
1138         TRACE("Could not parse position property\n");
1139         return E_PROP_ID_UNSUPPORTED;
1140     }
1141     pPoint->x = x;
1142     pPoint->y = y;
1143     return S_OK;
1144 }
1145 
1146 /***********************************************************************
1147  *      MSSTYLES_GetPropertyString
1148  *
1149  * Retrieve a string value for a property
1150  */
1151 HRESULT MSSTYLES_GetPropertyString(PTHEME_PROPERTY tp, LPWSTR pszBuff, int cchMaxBuffChars)
1152 {
1153     lstrcpynW(pszBuff, tp->lpValue, min(tp->dwValueLen+1, cchMaxBuffChars));
1154     return S_OK;
1155 }
1156 
1157 /***********************************************************************
1158  *      MSSTYLES_GetPropertyRect
1159  *
1160  * Retrieve a rect value for a property
1161  */
1162 HRESULT MSSTYLES_GetPropertyRect(PTHEME_PROPERTY tp, RECT *pRect)
1163 {
1164     LPCWSTR lpCur = tp->lpValue;
1165     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1166 
1167     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pRect->left);
1168     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pRect->top);
1169     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pRect->right);
1170     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pRect->bottom)) {
1171         TRACE("Could not parse rect property\n");
1172         return E_PROP_ID_UNSUPPORTED;
1173     }
1174     return S_OK;
1175 }
1176 
1177 /***********************************************************************
1178  *      MSSTYLES_GetPropertyMargins
1179  *
1180  * Retrieve a margins value for a property
1181  */
1182 HRESULT MSSTYLES_GetPropertyMargins(PTHEME_PROPERTY tp, RECT *prc, MARGINS *pMargins)
1183 {
1184     LPCWSTR lpCur = tp->lpValue;
1185     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1186 
1187     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pMargins->cxLeftWidth);
1188     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pMargins->cxRightWidth);
1189     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pMargins->cyTopHeight);
1190     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pMargins->cyBottomHeight)) {
1191         TRACE("Could not parse margins property\n");
1192         return E_PROP_ID_UNSUPPORTED;
1193     }
1194     return S_OK;
1195 }
1196