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