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