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