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