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