Add some missing includes which contain used prototypes.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "winuser.h"
29 #include "winnls.h"
30 #include "wingdi.h"
31 #include "uxtheme.h"
32 #include "tmschema.h"
33
34 #include "uxthemedll.h"
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 BOOL MSSTYLES_GetNextInteger(LPCWSTR lpStringStart, LPCWSTR lpStringEnd, LPCWSTR *lpValEnd, int *value);
47 BOOL MSSTYLES_GetNextToken(LPCWSTR lpStringStart, LPCWSTR lpStringEnd, LPCWSTR *lpValEnd, LPWSTR lpBuff, DWORD buffSize);
48 void MSSTYLES_ParseThemeIni(PTHEME_FILE tf, BOOL setMetrics);
49
50 extern HINSTANCE hDllInst;
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 PTHEME_FILE tfActiveTheme = NULL;
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: %ld\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 = (LPWSTR)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 = (LPWSTR)LoadResource(hTheme, hrsc);
142
143     /* Validate requested color against whats 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 whats 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 = (LPWSTR)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             *strchrW(comp, ')') = 0;
367             lstrcpynW(state, comp, sizeof(state)/sizeof(state[0]));
368         }
369         else {
370             lstrcpynW(part, comp, sizeof(part)/sizeof(part[0]));
371         }
372     }
373     else {
374         tmp = strchrW(comp, '(');
375         if(tmp) {
376             *tmp++ = 0;
377             lstrcpynW(szClassName, comp, MAX_THEME_CLASS_NAME);
378             comp = tmp;
379             /* now get the state */
380             *strchrW(comp, ')') = 0;
381             lstrcpynW(state, comp, sizeof(state)/sizeof(state[0]));
382         }
383         else {
384             lstrcpynW(szClassName, comp, MAX_THEME_CLASS_NAME);
385         }
386     }
387     if(!*szClassName) return FALSE;
388     return MSSTYLES_LookupPartState(szClassName, part[0]?part:NULL, state[0]?state:NULL, iPartId, iStateId);
389 }
390
391 /***********************************************************************
392  *      MSSTYLES_FindClass
393  *
394  * Find a class
395  *
396  * PARAMS
397  *     tf                  Theme file
398  *     pszAppName          App name to find
399  *     pszClassName        Class name to find
400  *
401  * RETURNS
402  *  The class found, or NULL
403  */
404 PTHEME_CLASS MSSTYLES_FindClass(PTHEME_FILE tf, LPCWSTR pszAppName, LPCWSTR pszClassName)
405 {
406     PTHEME_CLASS cur = tf->classes;
407     while(cur) {
408         if(!pszAppName) {
409             if(!*cur->szAppName && !lstrcmpiW(pszClassName, cur->szClassName))
410                 return cur;
411         }
412         else {
413             if(!lstrcmpiW(pszAppName, cur->szAppName) && !lstrcmpiW(pszClassName, cur->szClassName))
414                 return cur;
415         }
416         cur = cur->next;
417     }
418     return NULL;
419 }
420
421 /***********************************************************************
422  *      MSSTYLES_AddClass
423  *
424  * Add a class to a theme file
425  *
426  * PARAMS
427  *     tf                  Theme file
428  *     pszAppName          App name to add
429  *     pszClassName        Class name to add
430  *
431  * RETURNS
432  *  The class added, or a class previously added with the same name
433  */
434 static PTHEME_CLASS MSSTYLES_AddClass(PTHEME_FILE tf, LPCWSTR pszAppName, LPCWSTR pszClassName)
435 {
436     PTHEME_CLASS cur = MSSTYLES_FindClass(tf, pszAppName, pszClassName);
437     if(cur) return cur;
438
439     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_CLASS));
440     cur->hTheme = tf->hTheme;
441     lstrcpyW(cur->szAppName, pszAppName);
442     lstrcpyW(cur->szClassName, pszClassName);
443     cur->next = tf->classes;
444     cur->partstate = NULL;
445     cur->overrides = NULL;
446     tf->classes = cur;
447     return cur;
448 }
449
450 /***********************************************************************
451  *      MSSTYLES_FindPartState
452  *
453  * Find a part/state
454  *
455  * PARAMS
456  *     tc                  Class to search
457  *     iPartId             Part ID to find
458  *     iStateId            State ID to find
459  *     tcNext              Receives the next class in the override chain
460  *
461  * RETURNS
462  *  The part/state found, or NULL
463  */
464 PTHEME_PARTSTATE MSSTYLES_FindPartState(PTHEME_CLASS tc, int iPartId, int iStateId, PTHEME_CLASS *tcNext)
465 {
466     PTHEME_PARTSTATE cur = tc->partstate;
467     while(cur) {
468         if(cur->iPartId == iPartId && cur->iStateId == iStateId) {
469             if(tcNext) *tcNext = tc->overrides;
470             return cur;
471         }
472         cur = cur->next;
473     }
474     if(tc->overrides) return MSSTYLES_FindPartState(tc->overrides, iPartId, iStateId, tcNext);
475     return NULL;
476 }
477
478 /***********************************************************************
479  *      MSSTYLES_AddPartState
480  *
481  * Add a part/state to a class
482  *
483  * PARAMS
484  *     tc                  Theme class
485  *     iPartId             Part ID to add
486  *     iStateId            State ID to add
487  *
488  * RETURNS
489  *  The part/state added, or a part/state previously added with the same IDs
490  */
491 static PTHEME_PARTSTATE MSSTYLES_AddPartState(PTHEME_CLASS tc, int iPartId, int iStateId)
492 {
493     PTHEME_PARTSTATE cur = MSSTYLES_FindPartState(tc, iPartId, iStateId, NULL);
494     if(cur) return cur;
495
496     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_PARTSTATE));
497     cur->iPartId = iPartId;
498     cur->iStateId = iStateId;
499     cur->properties = NULL;
500     cur->next = tc->partstate;
501     tc->partstate = cur;
502     return cur;
503 }
504
505 /***********************************************************************
506  *      MSSTYLES_LFindProperty
507  *
508  * Find a property within a property list
509  *
510  * PARAMS
511  *     tp                  property list to scan
512  *     iPropertyPrimitive  Type of value expected
513  *     iPropertyId         ID of the required value
514  *
515  * RETURNS
516  *  The property found, or NULL
517  */
518 static PTHEME_PROPERTY MSSTYLES_LFindProperty(PTHEME_PROPERTY tp, int iPropertyPrimitive, int iPropertyId)
519 {
520     PTHEME_PROPERTY cur = tp;
521     while(cur) {
522         if(cur->iPropertyId == iPropertyId) {
523             if(cur->iPrimitiveType == iPropertyPrimitive) {
524                 return cur;
525             }
526             else {
527                 if(!iPropertyPrimitive)
528                     return cur;
529                 return NULL;
530             }
531         }
532         cur = cur->next;
533     }
534     return NULL;
535 }
536
537 /***********************************************************************
538  *      MSSTYLES_PSFindProperty
539  *
540  * Find a value within a part/state
541  *
542  * PARAMS
543  *     ps                  Part/state to search
544  *     iPropertyPrimitive  Type of value expected
545  *     iPropertyId         ID of the required value
546  *
547  * RETURNS
548  *  The property found, or NULL
549  */
550 static inline PTHEME_PROPERTY MSSTYLES_PSFindProperty(PTHEME_PARTSTATE ps, int iPropertyPrimitive, int iPropertyId)
551 {
552     return MSSTYLES_LFindProperty(ps->properties, iPropertyPrimitive, iPropertyId);
553 }
554
555 /***********************************************************************
556  *      MSSTYLES_FFindMetric
557  *
558  * Find a metric property for a theme file
559  *
560  * PARAMS
561  *     tf                  Theme file
562  *     iPropertyPrimitive  Type of value expected
563  *     iPropertyId         ID of the required value
564  *
565  * RETURNS
566  *  The property found, or NULL
567  */
568 static inline PTHEME_PROPERTY MSSTYLES_FFindMetric(PTHEME_FILE tf, int iPropertyPrimitive, int iPropertyId)
569 {
570     return MSSTYLES_LFindProperty(tf->metrics, iPropertyPrimitive, iPropertyId);
571 }
572
573 /***********************************************************************
574  *      MSSTYLES_FindMetric
575  *
576  * Find a metric property for the current installed theme
577  *
578  * PARAMS
579  *     tf                  Theme file
580  *     iPropertyPrimitive  Type of value expected
581  *     iPropertyId         ID of the required value
582  *
583  * RETURNS
584  *  The property found, or NULL
585  */
586 PTHEME_PROPERTY MSSTYLES_FindMetric(int iPropertyPrimitive, int iPropertyId)
587 {
588     if(!tfActiveTheme) return NULL;
589     return MSSTYLES_FFindMetric(tfActiveTheme, iPropertyPrimitive, iPropertyId);
590 }
591
592 /***********************************************************************
593  *      MSSTYLES_AddProperty
594  *
595  * Add a property to a part/state
596  *
597  * PARAMS
598  *     ps                  Part/state
599  *     iPropertyPrimitive  Primitive type of the property
600  *     iPropertyId         ID of the property
601  *     lpValue             Raw value (non-NULL terminated)
602  *     dwValueLen          Length of the value
603  *
604  * RETURNS
605  *  The property added, or a property previously added with the same IDs
606  */
607 static PTHEME_PROPERTY MSSTYLES_AddProperty(PTHEME_PARTSTATE ps, int iPropertyPrimitive, int iPropertyId, LPCWSTR lpValue, DWORD dwValueLen, BOOL isGlobal)
608 {
609     PTHEME_PROPERTY cur = MSSTYLES_PSFindProperty(ps, iPropertyPrimitive, iPropertyId);
610     /* Should duplicate properties overwrite the original, or be ignored? */
611     if(cur) return cur;
612
613     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_PROPERTY));
614     cur->iPrimitiveType = iPropertyPrimitive;
615     cur->iPropertyId = iPropertyId;
616     cur->lpValue = lpValue;
617     cur->dwValueLen = dwValueLen;
618
619     if(ps->iStateId)
620         cur->origin = PO_STATE;
621     else if(ps->iPartId)
622         cur->origin = PO_PART;
623     else if(isGlobal)
624         cur->origin = PO_GLOBAL;
625     else
626         cur->origin = PO_CLASS;
627
628     cur->next = ps->properties;
629     ps->properties = cur;
630     return cur;
631 }
632
633 /***********************************************************************
634  *      MSSTYLES_AddMetric
635  *
636  * Add a property to a part/state
637  *
638  * PARAMS
639  *     tf                  Theme file
640  *     iPropertyPrimitive  Primitive type of the property
641  *     iPropertyId         ID of the property
642  *     lpValue             Raw value (non-NULL terminated)
643  *     dwValueLen          Length of the value
644  *
645  * RETURNS
646  *  The property added, or a property previously added with the same IDs
647  */
648 static PTHEME_PROPERTY MSSTYLES_AddMetric(PTHEME_FILE tf, int iPropertyPrimitive, int iPropertyId, LPCWSTR lpValue, DWORD dwValueLen)
649 {
650     PTHEME_PROPERTY cur = MSSTYLES_FFindMetric(tf, iPropertyPrimitive, iPropertyId);
651     /* Should duplicate properties overwrite the original, or be ignored? */
652     if(cur) return cur;
653
654     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_PROPERTY));
655     cur->iPrimitiveType = iPropertyPrimitive;
656     cur->iPropertyId = iPropertyId;
657     cur->lpValue = lpValue;
658     cur->dwValueLen = dwValueLen;
659
660     cur->origin = PO_GLOBAL;
661
662     cur->next = tf->metrics;
663     tf->metrics = cur;
664     return cur;
665 }
666
667 /***********************************************************************
668  *      MSSTYLES_ParseThemeIni
669  *
670  * Parse the theme ini for the selected color/style
671  *
672  * PARAMS
673  *     tf                  Theme to parse
674  */
675 void MSSTYLES_ParseThemeIni(PTHEME_FILE tf, BOOL setMetrics)
676 {
677     static const WCHAR szSysMetrics[] = {'S','y','s','M','e','t','r','i','c','s','\0'};
678     static const WCHAR szGlobals[] = {'g','l','o','b','a','l','s','\0'};
679     PTHEME_CLASS cls;
680     PTHEME_CLASS globals;
681     PTHEME_PARTSTATE ps;
682     PUXINI_FILE ini;
683     WCHAR szAppName[MAX_THEME_APP_NAME];
684     WCHAR szClassName[MAX_THEME_CLASS_NAME];
685     WCHAR szPropertyName[MAX_THEME_VALUE_NAME];
686     int iPartId;
687     int iStateId;
688     int iPropertyPrimitive;
689     int iPropertyId;
690     DWORD dwLen;
691     LPCWSTR lpName;
692     DWORD dwValueLen;
693     LPCWSTR lpValue;
694
695     ini = MSSTYLES_GetActiveThemeIni(tf);
696
697     while((lpName=UXINI_GetNextSection(ini, &dwLen))) {
698         if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpName, dwLen, szSysMetrics, -1) == CSTR_EQUAL) {
699             int colorCount = 0;
700             int colorElements[TMT_LASTCOLOR-TMT_FIRSTCOLOR];
701             COLORREF colorRgb[TMT_LASTCOLOR-TMT_FIRSTCOLOR];
702             LPCWSTR lpValueEnd;
703
704             while((lpName=UXINI_GetNextValue(ini, &dwLen, &lpValue, &dwValueLen))) {
705                 lstrcpynW(szPropertyName, lpName, min(dwLen+1, sizeof(szPropertyName)/sizeof(szPropertyName[0])));
706                 if(MSSTYLES_LookupProperty(szPropertyName, &iPropertyPrimitive, &iPropertyId)) {
707                     if(iPropertyId >= TMT_FIRSTCOLOR && iPropertyId <= TMT_LASTCOLOR) {
708                         int r,g,b;
709                         lpValueEnd = lpValue + dwValueLen;
710                         MSSTYLES_GetNextInteger(lpValue, lpValueEnd, &lpValue, &r);
711                         MSSTYLES_GetNextInteger(lpValue, lpValueEnd, &lpValue, &g);
712                         if(MSSTYLES_GetNextInteger(lpValue, lpValueEnd, &lpValue, &b)) {
713                             colorElements[colorCount] = iPropertyId - TMT_FIRSTCOLOR;
714                             colorRgb[colorCount++] = RGB(r,g,b);
715                         }
716                         else {
717                             FIXME("Invalid color value for %s\n", debugstr_w(szPropertyName));
718                         }
719                     }
720                     else if (setMetrics && (iPropertyId == TMT_FLATMENUS)) {
721                         BOOL flatMenus = (*lpValue == 'T') || (*lpValue == 't');
722                         SystemParametersInfoW (SPI_SETFLATMENU, 0, (PVOID)(INT_PTR)flatMenus, 0);
723                     }
724                     /* Catch all metrics, including colors */
725                     MSSTYLES_AddMetric(tf, iPropertyPrimitive, iPropertyId, lpValue, dwValueLen);
726                 }
727                 else {
728                     TRACE("Unknown system metric %s\n", debugstr_w(szPropertyName));
729                 }
730             }
731             if (setMetrics && (colorCount > 0))
732                 SetSysColors(colorCount, colorElements, colorRgb);
733             continue;
734         }
735         if(MSSTYLES_ParseIniSectionName(lpName, dwLen, szAppName, szClassName, &iPartId, &iStateId)) {
736             BOOL isGlobal = FALSE;
737             if(!lstrcmpiW(szClassName, szGlobals)) {
738                 isGlobal = TRUE;
739             }
740             cls = MSSTYLES_AddClass(tf, szAppName, szClassName);
741             ps = MSSTYLES_AddPartState(cls, iPartId, iStateId);
742
743             while((lpName=UXINI_GetNextValue(ini, &dwLen, &lpValue, &dwValueLen))) {
744                 lstrcpynW(szPropertyName, lpName, min(dwLen+1, sizeof(szPropertyName)/sizeof(szPropertyName[0])));
745                 if(MSSTYLES_LookupProperty(szPropertyName, &iPropertyPrimitive, &iPropertyId)) {
746                     MSSTYLES_AddProperty(ps, iPropertyPrimitive, iPropertyId, lpValue, dwValueLen, isGlobal);
747                 }
748                 else {
749                     TRACE("Unknown property %s\n", debugstr_w(szPropertyName));
750                 }
751             }
752         }
753     }
754
755     /* App/Class combos override values defined by the base class, map these overrides */
756     globals = MSSTYLES_FindClass(tf, NULL, szGlobals);
757     cls = tf->classes;
758     while(cls) {
759         if(*cls->szAppName) {
760             cls->overrides = MSSTYLES_FindClass(tf, NULL, cls->szClassName);
761             if(!cls->overrides) {
762                 TRACE("No overrides found for app %s class %s\n", debugstr_w(cls->szAppName), debugstr_w(cls->szClassName));
763             }
764             else {
765                 cls->overrides = globals;
766             }
767         }
768         else {
769             /* Everything overrides globals..except globals */
770             if(cls != globals) cls->overrides = globals;
771         }
772         cls = cls->next;
773     }
774     UXINI_CloseINI(ini);
775
776     if(!tf->classes) {
777         ERR("Failed to parse theme ini\n");
778     }
779 }
780
781 /***********************************************************************
782  *      MSSTYLES_OpenThemeClass
783  *
784  * Open a theme class, uses the current active theme
785  *
786  * PARAMS
787  *     pszAppName          Application name, for theme styles specific
788  *                         to a particular application
789  *     pszClassList        List of requested classes, semicolon delimited
790  */
791 PTHEME_CLASS MSSTYLES_OpenThemeClass(LPCWSTR pszAppName, LPCWSTR pszClassList)
792 {
793     PTHEME_CLASS cls = NULL;
794     WCHAR szClassName[MAX_THEME_CLASS_NAME];
795     LPCWSTR start;
796     LPCWSTR end;
797     DWORD len;
798
799     if(!tfActiveTheme) {
800         TRACE("there is no active theme\n");
801         return NULL;
802     }
803     if(!tfActiveTheme->classes) {
804         return NULL;
805     }
806
807     start = pszClassList;
808     while((end = strchrW(start, ';'))) {
809         len = end-start;
810         lstrcpynW(szClassName, start, min(len+1, sizeof(szClassName)/sizeof(szClassName[0])));
811         start = end+1;
812         cls = MSSTYLES_FindClass(tfActiveTheme, pszAppName, szClassName);
813         if(cls) break;
814     }
815     if(!cls && *start) {
816         lstrcpynW(szClassName, start, sizeof(szClassName)/sizeof(szClassName[0]));
817         cls = MSSTYLES_FindClass(tfActiveTheme, pszAppName, szClassName);
818     }
819     if(cls) {
820         TRACE("Opened app %s, class %s from list %s\n", debugstr_w(cls->szAppName), debugstr_w(cls->szClassName), debugstr_w(pszClassList));
821         cls->tf = tfActiveTheme;
822         cls->tf->dwRefCount++;
823     }
824     return cls;
825 }
826
827 /***********************************************************************
828  *      MSSTYLES_CloseThemeClass
829  *
830  * Close a theme class
831  *
832  * PARAMS
833  *     tc                  Theme class to close
834  *
835  * NOTES
836  *  The MSSTYLES_CloseThemeFile decreases the refcount of the owning
837  *  theme file and cleans it up, if needed.
838  */
839 HRESULT MSSTYLES_CloseThemeClass(PTHEME_CLASS tc)
840 {
841     MSSTYLES_CloseThemeFile (tc->tf);
842     return S_OK;
843 }
844
845 /***********************************************************************
846  *      MSSTYLES_FindProperty
847  *
848  * Locate a property in a class. Part and state IDs will be used as a
849  * preference, but may be ignored in the attempt to locate the property.
850  * Will scan the entire chain of overrides for this class.
851  */
852 PTHEME_PROPERTY MSSTYLES_FindProperty(PTHEME_CLASS tc, int iPartId, int iStateId, int iPropertyPrimitive, int iPropertyId)
853 {
854     PTHEME_CLASS next = tc;
855     PTHEME_PARTSTATE ps;
856     PTHEME_PROPERTY tp;
857
858     TRACE("(%p, %d, %d, %d)\n", tc, iPartId, iStateId, iPropertyId);
859      /* Try and find an exact match on part & state */
860     while(next && (ps = MSSTYLES_FindPartState(next, iPartId, iStateId, &next))) {
861         if((tp = MSSTYLES_PSFindProperty(ps, iPropertyPrimitive, iPropertyId))) {
862             return tp;
863         }
864     }
865     /* If that fails, and we didn't already try it, search for just part */
866     if(iStateId != 0)
867         iStateId = 0;
868     /* As a last ditch attempt..go for just class */
869     else if(iPartId != 0)
870         iPartId = 0;
871     else
872         return NULL;
873
874     if((tp = MSSTYLES_FindProperty(tc, iPartId, iStateId, iPropertyPrimitive, iPropertyId)))
875         return tp;
876     return NULL;
877 }
878
879 /* Prepare a bitmap to be used for alpha blending */
880 static BOOL prepare_alpha (HBITMAP bmp, BOOL* hasAlpha)
881 {
882     DIBSECTION dib;
883     int n;
884     BYTE* p;
885
886     *hasAlpha = FALSE;
887
888     if (!bmp || GetObjectW( bmp, sizeof(dib), &dib ) != sizeof(dib))
889         return FALSE;
890
891     if(dib.dsBm.bmBitsPixel != 32)
892         /* nothing to do */
893         return TRUE;
894
895     *hasAlpha = TRUE;
896     p = (BYTE*)dib.dsBm.bmBits;
897     n = abs(dib.dsBmih.biHeight) * dib.dsBmih.biWidth;
898     /* AlphaBlend() wants premultiplied alpha, so do that now */
899     while (n-- > 0)
900     {
901         int a = p[3]+1;
902         p[0] = (p[0] * a) >> 8;
903         p[1] = (p[1] * a) >> 8;
904         p[2] = (p[2] * a) >> 8;
905         p += 4;
906     }
907
908     return TRUE;
909 }
910
911 HBITMAP MSSTYLES_LoadBitmap (PTHEME_CLASS tc, LPCWSTR lpFilename, BOOL* hasAlpha)
912 {
913     WCHAR szFile[MAX_PATH];
914     LPWSTR tmp;
915     PTHEME_IMAGE img;
916     lstrcpynW(szFile, lpFilename, sizeof(szFile)/sizeof(szFile[0]));
917     tmp = szFile;
918     do {
919         if(*tmp == '\\') *tmp = '_';
920         if(*tmp == '/') *tmp = '_';
921         if(*tmp == '.') *tmp = '_';
922     } while(*tmp++);
923
924     /* Try to locate in list of loaded images */
925     img = tc->tf->images;
926     while (img)
927     {
928         if (lstrcmpiW (szFile, img->name) == 0)
929         {
930             TRACE ("found %p %s: %p\n", img, debugstr_w (img->name), img->image);
931             *hasAlpha = img->hasAlpha;
932             return img->image;
933         }
934         img = img->next;
935     }
936     /* Not found? Load from resources */
937     img = HeapAlloc (GetProcessHeap(), 0, sizeof (THEME_IMAGE));
938     img->image = LoadImageW(tc->hTheme, szFile, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
939     prepare_alpha (img->image, hasAlpha);
940     img->hasAlpha = *hasAlpha;
941     /* ...and stow away for later reuse. */
942     lstrcpyW (img->name, szFile);
943     img->next = tc->tf->images;
944     tc->tf->images = img;
945     TRACE ("new %p %s: %p\n", img, debugstr_w (img->name), img->image);
946     return img->image;
947 }
948
949 BOOL MSSTYLES_GetNextInteger(LPCWSTR lpStringStart, LPCWSTR lpStringEnd, LPCWSTR *lpValEnd, int *value)
950 {
951     LPCWSTR cur = lpStringStart;
952     int total = 0;
953     BOOL gotNeg = FALSE;
954
955     while(cur < lpStringEnd && (*cur < '0' || *cur > '9' || *cur == '-')) cur++;
956     if(cur >= lpStringEnd) {
957         return FALSE;
958     }
959     if(*cur == '-') {
960         cur++;
961         gotNeg = TRUE;
962     }
963     while(cur < lpStringEnd && (*cur >= '0' && *cur <= '9')) {
964         total = total * 10 + (*cur - '0');
965         cur++;
966     }
967     if(gotNeg) total = -total;
968     *value = total;
969     if(lpValEnd) *lpValEnd = cur;
970     return TRUE;
971 }
972
973 BOOL MSSTYLES_GetNextToken(LPCWSTR lpStringStart, LPCWSTR lpStringEnd, LPCWSTR *lpValEnd, LPWSTR lpBuff, DWORD buffSize) {
974     LPCWSTR cur = lpStringStart;
975     LPCWSTR start;
976     LPCWSTR end;
977
978     while(cur < lpStringEnd && (isspace(*cur) || *cur == ',')) cur++;
979     if(cur >= lpStringEnd) {
980         return FALSE;
981     }
982     start = cur;
983     while(cur < lpStringEnd && *cur != ',') cur++;
984     end = cur;
985     while(isspace(*end)) end--;
986
987     lstrcpynW(lpBuff, start, min(buffSize, end-start+1));
988
989     if(lpValEnd) *lpValEnd = cur;
990     return TRUE;
991 }
992
993 /***********************************************************************
994  *      MSSTYLES_GetPropertyBool
995  *
996  * Retrieve a color value for a property 
997  */
998 HRESULT MSSTYLES_GetPropertyBool(PTHEME_PROPERTY tp, BOOL *pfVal)
999 {
1000     *pfVal = FALSE;
1001     if(*tp->lpValue == 't' || *tp->lpValue == 'T')
1002         *pfVal = TRUE;
1003     return S_OK;
1004 }
1005
1006 /***********************************************************************
1007  *      MSSTYLES_GetPropertyColor
1008  *
1009  * Retrieve a color value for a property 
1010  */
1011 HRESULT MSSTYLES_GetPropertyColor(PTHEME_PROPERTY tp, COLORREF *pColor)
1012 {
1013     LPCWSTR lpEnd;
1014     LPCWSTR lpCur;
1015     int red, green, blue;
1016
1017     lpCur = tp->lpValue;
1018     lpEnd = tp->lpValue + tp->dwValueLen;
1019
1020     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &red);
1021     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &green);
1022     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &blue)) {
1023         TRACE("Could not parse color property\n");
1024         return E_PROP_ID_UNSUPPORTED;
1025     }
1026     *pColor = RGB(red,green,blue);
1027     return S_OK;
1028 }
1029
1030 /***********************************************************************
1031  *      MSSTYLES_GetPropertyColor
1032  *
1033  * Retrieve a color value for a property 
1034  */
1035 HRESULT MSSTYLES_GetPropertyFont(PTHEME_PROPERTY tp, HDC hdc, LOGFONTW *pFont)
1036 {
1037     static const WCHAR szBold[] = {'b','o','l','d','\0'};
1038     static const WCHAR szItalic[] = {'i','t','a','l','i','c','\0'};
1039     static const WCHAR szUnderline[] = {'u','n','d','e','r','l','i','n','e','\0'};
1040     static const WCHAR szStrikeOut[] = {'s','t','r','i','k','e','o','u','t','\0'};
1041     int pointSize;
1042     WCHAR attr[32];
1043     LPCWSTR lpCur = tp->lpValue;
1044     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1045
1046     ZeroMemory(pFont, sizeof(LOGFONTW));
1047
1048     if(!MSSTYLES_GetNextToken(lpCur, lpEnd, &lpCur, pFont->lfFaceName, LF_FACESIZE)) {
1049         TRACE("Property is there, but failed to get face name\n");
1050         return E_PROP_ID_UNSUPPORTED;
1051     }
1052     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pointSize)) {
1053         TRACE("Property is there, but failed to get point size\n");
1054         return E_PROP_ID_UNSUPPORTED;
1055     }
1056     pFont->lfHeight = -MulDiv(pointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1057     pFont->lfWeight = FW_REGULAR;
1058     pFont->lfCharSet = DEFAULT_CHARSET;
1059     while(MSSTYLES_GetNextToken(lpCur, lpEnd, &lpCur, attr, sizeof(attr)/sizeof(attr[0]))) {
1060         if(!lstrcmpiW(szBold, attr)) pFont->lfWeight = FW_BOLD;
1061         else if(!!lstrcmpiW(szItalic, attr)) pFont->lfItalic = TRUE;
1062         else if(!!lstrcmpiW(szUnderline, attr)) pFont->lfUnderline = TRUE;
1063         else if(!!lstrcmpiW(szStrikeOut, attr)) pFont->lfStrikeOut = TRUE;
1064     }
1065     return S_OK;
1066 }
1067
1068 /***********************************************************************
1069  *      MSSTYLES_GetPropertyInt
1070  *
1071  * Retrieve an int value for a property 
1072  */
1073 HRESULT MSSTYLES_GetPropertyInt(PTHEME_PROPERTY tp, int *piVal)
1074 {
1075     if(!MSSTYLES_GetNextInteger(tp->lpValue, (tp->lpValue + tp->dwValueLen), NULL, piVal)) {
1076         TRACE("Could not parse int property\n");
1077         return E_PROP_ID_UNSUPPORTED;
1078     }
1079     return S_OK;
1080 }
1081
1082 /***********************************************************************
1083  *      MSSTYLES_GetPropertyIntList
1084  *
1085  * Retrieve an int list value for a property 
1086  */
1087 HRESULT MSSTYLES_GetPropertyIntList(PTHEME_PROPERTY tp, INTLIST *pIntList)
1088 {
1089     int i;
1090     LPCWSTR lpCur = tp->lpValue;
1091     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1092
1093     for(i=0; i < MAX_INTLIST_COUNT; i++) {
1094         if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pIntList->iValues[i]))
1095             break;
1096     }
1097     pIntList->iValueCount = i;
1098     return S_OK;
1099 }
1100
1101 /***********************************************************************
1102  *      MSSTYLES_GetPropertyPosition
1103  *
1104  * Retrieve a position value for a property 
1105  */
1106 HRESULT MSSTYLES_GetPropertyPosition(PTHEME_PROPERTY tp, POINT *pPoint)
1107 {
1108     int x,y;
1109     LPCWSTR lpCur = tp->lpValue;
1110     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1111
1112     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &x);
1113     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &y)) {
1114         TRACE("Could not parse position property\n");
1115         return E_PROP_ID_UNSUPPORTED;
1116     }
1117     pPoint->x = x;
1118     pPoint->y = y;
1119     return S_OK;
1120 }
1121
1122 /***********************************************************************
1123  *      MSSTYLES_GetPropertyString
1124  *
1125  * Retrieve a string value for a property 
1126  */
1127 HRESULT MSSTYLES_GetPropertyString(PTHEME_PROPERTY tp, LPWSTR pszBuff, int cchMaxBuffChars)
1128 {
1129     lstrcpynW(pszBuff, tp->lpValue, min(tp->dwValueLen+1, cchMaxBuffChars));
1130     return S_OK;
1131 }
1132
1133 /***********************************************************************
1134  *      MSSTYLES_GetPropertyRect
1135  *
1136  * Retrieve a rect value for a property 
1137  */
1138 HRESULT MSSTYLES_GetPropertyRect(PTHEME_PROPERTY tp, RECT *pRect)
1139 {
1140     LPCWSTR lpCur = tp->lpValue;
1141     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1142
1143     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, (int*)&pRect->left);
1144     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, (int*)&pRect->top);
1145     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, (int*)&pRect->right);
1146     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, (int*)&pRect->bottom)) {
1147         TRACE("Could not parse rect property\n");
1148         return E_PROP_ID_UNSUPPORTED;
1149     }
1150     return S_OK;
1151 }
1152
1153 /***********************************************************************
1154  *      MSSTYLES_GetPropertyMargins
1155  *
1156  * Retrieve a margins value for a property 
1157  */
1158 HRESULT MSSTYLES_GetPropertyMargins(PTHEME_PROPERTY tp, RECT *prc, MARGINS *pMargins)
1159 {
1160     LPCWSTR lpCur = tp->lpValue;
1161     LPCWSTR lpEnd = tp->lpValue + tp->dwValueLen;
1162
1163     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pMargins->cxLeftWidth);
1164     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pMargins->cxRightWidth);
1165     MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pMargins->cyTopHeight);
1166     if(!MSSTYLES_GetNextInteger(lpCur, lpEnd, &lpCur, &pMargins->cyBottomHeight)) {
1167         TRACE("Could not parse margins property\n");
1168         return E_PROP_ID_UNSUPPORTED;
1169     }
1170     return S_OK;
1171 }