2 * Win32 5.1 msstyles theme format
4 * Copyright (C) 2003 Kevin Koltzau
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.
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.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #define NO_SHLWAPI_REG
35 #include "uxthemedll.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(uxtheme);
42 /***********************************************************************
43 * Defines and global variables
46 extern HINSTANCE hDllInst;
48 #define MSSTYLES_VERSION 0x0003
50 static const WCHAR szThemesIniResource[] = {
51 't','h','e','m','e','s','_','i','n','i','\0'
54 PTHEME_FILE tfActiveTheme = NULL;
56 /***********************************************************************/
58 /**********************************************************************
59 * MSSTYLES_OpenThemeFile
61 * Load and validate a theme
64 * lpThemeFile Path to theme file to load
65 * pszColorName Color name wanted, can be NULL
66 * pszSizeName Size name wanted, can be NULL
69 * If pszColorName or pszSizeName are NULL, the default color/size will be used.
70 * If one/both are provided, they are validated against valid color/sizes and if
71 * a match is not found, the function fails.
73 HRESULT MSSTYLES_OpenThemeFile(LPCWSTR lpThemeFile, LPCWSTR pszColorName, LPCWSTR pszSizeName, PTHEME_FILE *tf)
78 WCHAR szPackThemVersionResource[] = {
79 'P','A','C','K','T','H','E','M','_','V','E','R','S','I','O','N', '\0'
81 WCHAR szColorNamesResource[] = {
82 'C','O','L','O','R','N','A','M','E','S','\0'
84 WCHAR szSizeNamesResource[] = {
85 'S','I','Z','E','N','A','M','E','S','\0'
91 LPWSTR pszSelectedColor = NULL;
93 LPWSTR pszSelectedSize = NULL;
96 TRACE("Opening %s\n", debugstr_w(lpThemeFile));
98 hTheme = LoadLibraryExW(lpThemeFile, NULL, LOAD_LIBRARY_AS_DATAFILE);
100 /* Validate that this is really a theme */
101 if(!hTheme) goto invalid_theme;
102 if(!(hrsc = FindResourceW(hTheme, MAKEINTRESOURCEW(1), szPackThemVersionResource))) {
103 TRACE("No version resource found\n");
106 if((versize = SizeofResource(hTheme, hrsc)) != 2)
108 TRACE("Version resource found, but wrong size: %ld\n", versize);
111 version = *(WORD*)LoadResource(hTheme, hrsc);
112 if(version != MSSTYLES_VERSION)
114 TRACE("Version of theme file is unsupported: 0x%04x\n", version);
118 if(!(hrsc = FindResourceW(hTheme, MAKEINTRESOURCEW(1), szColorNamesResource))) {
119 TRACE("Color names resource not found\n");
122 pszColors = (LPWSTR)LoadResource(hTheme, hrsc);
124 if(!(hrsc = FindResourceW(hTheme, MAKEINTRESOURCEW(1), szSizeNamesResource))) {
125 TRACE("Size names resource not found\n");
128 pszSizes = (LPWSTR)LoadResource(hTheme, hrsc);
130 /* Validate requested color against whats available from the theme */
134 if(!lstrcmpiW(pszColorName, tmp)) {
135 pszSelectedColor = tmp;
138 tmp += lstrlenW(tmp)+1;
142 pszSelectedColor = pszColors; /* Use the default color */
144 /* Validate requested size against whats available from the theme */
148 if(!lstrcmpiW(pszSizeName, tmp)) {
149 pszSelectedSize = tmp;
152 tmp += lstrlenW(tmp)+1;
156 pszSelectedSize = pszSizes; /* Use the default size */
158 if(!pszSelectedColor || !pszSelectedSize) {
159 TRACE("Requested color/size (%s/%s) not found in theme\n",
160 debugstr_w(pszColorName), debugstr_w(pszSizeName));
161 hr = E_PROP_ID_UNSUPPORTED;
165 *tf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(THEME_FILE));
166 (*tf)->hTheme = hTheme;
167 (*tf)->pszAvailColors = pszColors;
168 (*tf)->pszAvailSizes = pszSizes;
169 (*tf)->pszSelectedColor = pszSelectedColor;
170 (*tf)->pszSelectedSize = pszSelectedSize;
171 (*tf)->dwRefCount = 1;
175 if(hTheme) FreeLibrary(hTheme);
176 if(!hr) hr = HRESULT_FROM_WIN32(GetLastError());
180 /***********************************************************************
181 * MSSTYLES_CloseThemeFile
183 * Close theme file and free resources
185 void MSSTYLES_CloseThemeFile(PTHEME_FILE tf)
189 if(!tf->dwRefCount) {
190 if(tf->hTheme) FreeLibrary(tf->hTheme);
193 PTHEME_CLASS pcls = tf->classes;
194 tf->classes = pcls->next;
195 while(pcls->partstate) {
196 PTHEME_PARTSTATE ps = pcls->partstate;
197 pcls->partstate = ps->next;
198 HeapFree(GetProcessHeap(), 0, ps);
200 HeapFree(GetProcessHeap(), 0, pcls);
203 HeapFree(GetProcessHeap(), 0, tf);
208 /***********************************************************************
209 * MSSTYLES_SetActiveTheme
211 * Set the current active theme
213 HRESULT MSSTYLES_SetActiveTheme(PTHEME_FILE tf)
216 MSSTYLES_CloseThemeFile(tfActiveTheme);
218 tfActiveTheme->dwRefCount++;
222 /***********************************************************************
223 * MSSTYLES_GetThemeIni
225 * Retrieves themes.ini from a theme
227 PUXINI_FILE MSSTYLES_GetThemeIni(PTHEME_FILE tf)
229 return UXINI_LoadINI(tf->hTheme, szThemesIniResource);
232 /***********************************************************************
233 * MSSTYLES_GetActiveThemeIni
235 * Retrieve the ini file for the selected color/style
237 PUXINI_FILE MSSTYLES_GetActiveThemeIni(PTHEME_FILE tf)
239 WCHAR szFileResNamesResource[] = {
240 'F','I','L','E','R','E','S','N','A','M','E','S','\0'
242 DWORD dwColorCount = 0;
243 DWORD dwSizeCount = 0;
244 DWORD dwColorNum = 0;
247 DWORD dwResourceIndex;
251 /* Count the number of available colors & styles, and determine the index number
252 of the color/style we are interested in
254 tmp = tf->pszAvailColors;
256 if(!lstrcmpiW(tf->pszSelectedColor, tmp))
257 dwColorNum = dwColorCount;
258 tmp += lstrlenW(tmp)+1;
261 tmp = tf->pszAvailSizes;
263 if(!lstrcmpiW(tf->pszSelectedSize, tmp))
264 dwSizeNum = dwSizeCount;
265 tmp += lstrlenW(tmp)+1;
269 if(!(hrsc = FindResourceW(tf->hTheme, MAKEINTRESOURCEW(1), szFileResNamesResource))) {
270 TRACE("FILERESNAMES map not found\n");
273 tmp = (LPWSTR)LoadResource(tf->hTheme, hrsc);
274 dwResourceIndex = (dwSizeCount * dwColorNum) + dwSizeNum;
275 for(i=0; i < dwResourceIndex; i++) {
276 tmp += lstrlenW(tmp)+1;
278 return UXINI_LoadINI(tf->hTheme, tmp);
282 /***********************************************************************
283 * MSSTYLES_ParseIniSectionName
285 * Parse an ini section name into its component parts
290 * [classname.part(state)]
291 * [application::classname]
292 * [application::classname(state)]
293 * [application::classname.part]
294 * [application::classname.part(state)]
297 * lpSection Section name
298 * dwLen Length of section name
299 * szAppName Location to store application name
300 * szClassName Location to store class name
301 * iPartId Location to store part id
302 * iStateId Location to store state id
304 BOOL MSSTYLES_ParseIniSectionName(LPCWSTR lpSection, DWORD dwLen, LPWSTR szAppName, LPWSTR szClassName, int *iPartId, int *iStateId)
307 WCHAR part[60] = {'\0'};
308 WCHAR state[60] = {'\0'};
311 lstrcpynW(sec, lpSection, min(dwLen+1, sizeof(sec)/sizeof(sec[0])));
318 /* Get the application name */
319 tmp = StrChrW(comp, ':');
323 lstrcpynW(szAppName, comp, MAX_THEME_APP_NAME);
327 tmp = StrChrW(comp, '.');
330 lstrcpynW(szClassName, comp, MAX_THEME_CLASS_NAME);
332 /* now get the part & state */
333 tmp = StrChrW(comp, '(');
336 lstrcpynW(part, comp, sizeof(part)/sizeof(part[0]));
338 /* now get the state */
339 *StrChrW(comp, ')') = 0;
340 lstrcpynW(state, comp, sizeof(state)/sizeof(state[0]));
343 lstrcpynW(part, comp, sizeof(part)/sizeof(part[0]));
347 tmp = StrChrW(comp, '(');
350 lstrcpynW(szClassName, comp, MAX_THEME_CLASS_NAME);
352 /* now get the state */
353 *StrChrW(comp, ')') = 0;
354 lstrcpynW(state, comp, sizeof(state)/sizeof(state[0]));
357 lstrcpynW(szClassName, comp, MAX_THEME_CLASS_NAME);
360 if(!*szClassName) return FALSE;
361 return MSSTYLES_LookupPartState(szClassName, part[0]?part:NULL, state[0]?state:NULL, iPartId, iStateId);
364 /***********************************************************************
371 * pszAppName App name to find
372 * pszClassName Class name to find
375 * The class found, or NULL
377 PTHEME_CLASS MSSTYLES_FindClass(PTHEME_FILE tf, LPCWSTR pszAppName, LPCWSTR pszClassName)
379 PTHEME_CLASS cur = tf->classes;
382 if(!*cur->szAppName && !lstrcmpiW(pszClassName, cur->szClassName))
386 if(!lstrcmpiW(pszAppName, cur->szAppName) && !lstrcmpiW(pszClassName, cur->szClassName))
394 /***********************************************************************
397 * Add a class to a theme file
401 * pszAppName App name to add
402 * pszClassName Class name to add
405 * The class added, or a class previously added with the same name
407 PTHEME_CLASS MSSTYLES_AddClass(PTHEME_FILE tf, LPCWSTR pszAppName, LPCWSTR pszClassName)
409 PTHEME_CLASS cur = MSSTYLES_FindClass(tf, pszAppName, pszClassName);
412 cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_CLASS));
413 cur->hTheme = tf->hTheme;
414 lstrcpyW(cur->szAppName, pszAppName);
415 lstrcpyW(cur->szClassName, pszClassName);
416 cur->next = tf->classes;
417 cur->partstate = NULL;
418 cur->overrides = NULL;
423 /***********************************************************************
424 * MSSTYLES_FindPartState
430 * iPartId Part ID to find
431 * iStateId State ID to find
432 * tcNext Receives the next class in the override chain
435 * The part/state found, or NULL
437 PTHEME_PARTSTATE MSSTYLES_FindPartState(PTHEME_CLASS tc, int iPartId, int iStateId, PTHEME_CLASS *tcNext)
439 PTHEME_PARTSTATE cur = tc->partstate;
441 if(cur->iPartId == iPartId && cur->iStateId == iStateId) {
442 if(tcNext) *tcNext = tc->overrides;
447 if(tc->overrides) return MSSTYLES_FindPartState(tc->overrides, iPartId, iStateId, tcNext);
451 /***********************************************************************
452 * MSSTYLES_AddPartState
454 * Add a part/state to a class
458 * iPartId Part ID to add
459 * iStateId State ID to add
462 * The part/state added, or a part/state previously added with the same IDs
464 PTHEME_PARTSTATE MSSTYLES_AddPartState(PTHEME_CLASS tc, int iPartId, int iStateId)
466 PTHEME_PARTSTATE cur = MSSTYLES_FindPartState(tc, iPartId, iStateId, NULL);
469 cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_PARTSTATE));
470 cur->iPartId = iPartId;
471 cur->iStateId = iStateId;
472 cur->properties = NULL;
473 cur->next = tc->partstate;
478 /***********************************************************************
479 * MSSTYLES_PSFindProperty
481 * Find a value within a part/state
484 * ps Part/state to search
485 * iPropertyPrimitive Type of value expected
486 * iPropertyId ID of the required value
489 * The property found, or NULL
491 PTHEME_PROPERTY MSSTYLES_PSFindProperty(PTHEME_PARTSTATE ps, int iPropertyPrimitive, int iPropertyId)
493 PTHEME_PROPERTY cur = ps->properties;
495 if(cur->iPropertyId == iPropertyId) {
496 if(cur->iPrimitiveType == iPropertyPrimitive) {
500 if(!iPropertyPrimitive)
510 /***********************************************************************
511 * MSSTYLES_AddProperty
513 * Add a property to a part/state
517 * iPropertyPrimitive Primitive type of the property
518 * iPropertyId ID of the property
519 * lpValue Raw value (non-NULL terminated)
520 * dwValueLen Length of the value
523 * The property added, or a property previously added with the same IDs
525 PTHEME_PROPERTY MSSTYLES_AddProperty(PTHEME_PARTSTATE ps, int iPropertyPrimitive, int iPropertyId, LPCWSTR lpValue, DWORD dwValueLen, BOOL isGlobal)
527 PTHEME_PROPERTY cur = MSSTYLES_PSFindProperty(ps, iPropertyPrimitive, iPropertyId);
528 /* Should duplicate properties overwrite the original, or be ignored? */
531 cur = HeapAlloc(GetProcessHeap(), 0, sizeof(THEME_PROPERTY));
532 cur->iPrimitiveType = iPropertyPrimitive;
533 cur->iPropertyId = iPropertyId;
534 cur->lpValue = lpValue;
535 cur->dwValueLen = dwValueLen;
538 cur->origin = PO_STATE;
540 cur->origin = PO_PART;
542 cur->origin = PO_GLOBAL;
544 cur->origin = PO_CLASS;
546 cur->next = ps->properties;
547 ps->properties = cur;
551 /***********************************************************************
552 * MSSTYLES_ParseThemeIni
554 * Parse the theme ini for the selected color/style
559 void MSSTYLES_ParseThemeIni(PTHEME_FILE tf)
561 WCHAR szSysMetrics[] = {'S','y','s','M','e','t','r','i','c','s','\0'};
562 WCHAR szGlobals[] = {'g','l','o','b','a','l','s','\0'};
564 PTHEME_CLASS globals;
567 WCHAR szAppName[MAX_THEME_APP_NAME];
568 WCHAR szClassName[MAX_THEME_CLASS_NAME];
569 WCHAR szPropertyName[MAX_THEME_VALUE_NAME];
572 int iPropertyPrimitive;
579 ini = MSSTYLES_GetActiveThemeIni(tf);
581 while((lpName=UXINI_GetNextSection(ini, &dwLen))) {
582 if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpName, dwLen, szSysMetrics, -1) == CSTR_EQUAL) {
583 FIXME("Process system metrics\n");
586 if(MSSTYLES_ParseIniSectionName(lpName, dwLen, szAppName, szClassName, &iPartId, &iStateId)) {
587 BOOL isGlobal = FALSE;
588 if(!lstrcmpiW(szClassName, szGlobals)) {
591 cls = MSSTYLES_AddClass(tf, szAppName, szClassName);
592 ps = MSSTYLES_AddPartState(cls, iPartId, iStateId);
594 while((lpName=UXINI_GetNextValue(ini, &dwLen, &lpValue, &dwValueLen))) {
595 lstrcpynW(szPropertyName, lpName, min(dwLen+1, sizeof(szPropertyName)/sizeof(szPropertyName[0])));
596 if(MSSTYLES_LookupProperty(szPropertyName, &iPropertyPrimitive, &iPropertyId)) {
597 MSSTYLES_AddProperty(ps, iPropertyPrimitive, iPropertyId, lpValue, dwValueLen, isGlobal);
600 TRACE("Unknown property %s\n", debugstr_w(szPropertyName));
606 /* App/Class combos override values defined by the base class, map these overrides */
607 globals = MSSTYLES_FindClass(tf, NULL, szGlobals);
610 if(*cls->szAppName) {
611 cls->overrides = MSSTYLES_FindClass(tf, NULL, cls->szClassName);
612 if(!cls->overrides) {
613 TRACE("No overrides found for app %s class %s\n", debugstr_w(cls->szAppName), debugstr_w(cls->szClassName));
616 cls->overrides = globals;
620 /* Everything overrides globals..except globals */
621 if(cls != globals) cls->overrides = globals;
628 ERR("Failed to parse theme ini\n");
632 /***********************************************************************
633 * MSSTYLES_OpenThemeClass
635 * Open a theme class, uses the current active theme
638 * pszAppName Application name, for theme styles specific
639 * to a particular application
640 * pszClassList List of requested classes, semicolon delimited
642 PTHEME_CLASS MSSTYLES_OpenThemeClass(LPCWSTR pszAppName, LPCWSTR pszClassList)
644 PTHEME_CLASS cls = NULL;
645 WCHAR szClassName[MAX_THEME_CLASS_NAME];
651 TRACE("there is no active theme\n");
654 if(!tfActiveTheme->classes) {
655 MSSTYLES_ParseThemeIni(tfActiveTheme);
656 if(!tfActiveTheme->classes)
660 start = pszClassList;
661 while((end = StrChrW(start, ';'))) {
663 lstrcpynW(szClassName, start, min(len+1, sizeof(szClassName)/sizeof(szClassName[0])));
665 cls = MSSTYLES_FindClass(tfActiveTheme, pszAppName, szClassName);
669 lstrcpynW(szClassName, start, sizeof(szClassName)/sizeof(szClassName[0]));
670 cls = MSSTYLES_FindClass(tfActiveTheme, pszAppName, szClassName);
673 TRACE("Opened app %s, class %s from list %s\n", debugstr_w(cls->szAppName), debugstr_w(cls->szClassName), debugstr_w(pszClassList));
678 /***********************************************************************
679 * MSSTYLES_CloseThemeClass
681 * Close a theme class
684 * tc Theme class to close
687 * There is currently no need clean anything up for theme classes,
688 * so do nothing for now
690 HRESULT MSSTYLES_CloseThemeClass(PTHEME_CLASS tc)
695 /***********************************************************************
696 * MSSTYLES_FindProperty
698 * Locate a property in a class. Part and state IDs will be used as a
699 * preference, but may be ignored in the attempt to locate the property.
700 * Will scan the entire chain of overrides for this class.
702 PTHEME_PROPERTY MSSTYLES_FindProperty(PTHEME_CLASS tc, int iPartId, int iStateId, int iPropertyPrimitive, int iPropertyId)
704 PTHEME_CLASS next = tc;
708 TRACE("(%p, %d, %d, %d)\n", tc, iPartId, iStateId, iPropertyId);
709 /* Try and find an exact match on part & state */
710 while(next && (ps = MSSTYLES_FindPartState(next, iPartId, iStateId, &next))) {
711 if((tp = MSSTYLES_PSFindProperty(ps, iPropertyPrimitive, iPropertyId))) {
715 /* If that fails, and we didn't already try it, search for just part */
718 /* As a last ditch attempt..go for just class */
719 else if(iPartId != 0)
724 if((tp = MSSTYLES_FindProperty(tc, iPartId, iStateId, iPropertyPrimitive, iPropertyId)))