2 * Theme configuration code
4 * Copyright (c) 2005 by Frank Richter
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
33 #include <wine/debug.h>
38 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
40 /* UXTHEME functions not in the headers */
41 typedef void* HTHEMEFILE
;
42 typedef BOOL (CALLBACK
*EnumThemeProc
)(LPVOID lpReserved
,
43 LPCWSTR pszThemeFileName
,
45 LPCWSTR pszToolTip
, LPVOID lpReserved2
,
48 HRESULT WINAPI
EnumThemeColors (LPWSTR pszThemeFileName
, LPWSTR pszSizeName
,
49 DWORD dwColorNum
, LPWSTR pszColorName
);
50 HRESULT WINAPI
EnumThemeSizes (LPWSTR pszThemeFileName
, LPWSTR pszColorName
,
51 DWORD dwSizeNum
, LPWSTR pszSizeName
);
52 HRESULT WINAPI
ApplyTheme (HTHEMEFILE hThemeFile
, char* unknown
, HWND hWnd
);
53 HRESULT WINAPI
OpenThemeFile (LPCWSTR pszThemeFileName
, LPCWSTR pszColorName
,
54 LPCWSTR pszSizeName
, HTHEMEFILE
* hThemeFile
,
56 HRESULT WINAPI
CloseThemeFile (HTHEMEFILE hThemeFile
);
57 HRESULT WINAPI
EnumThemes (LPCWSTR pszThemePath
, EnumThemeProc callback
,
60 /* A struct to keep both the internal and "fancy" name of a color or size */
67 /* wrapper around DSA that also keeps an item count */
74 /* Some helper functions to deal with ThemeColorOrSize structs in WrappedDSAs */
76 static void color_or_size_dsa_add (WrappedDsa
* wdsa
, const WCHAR
* name
,
77 const WCHAR
* fancyName
)
79 ThemeColorOrSize item
;
81 item
.name
= HeapAlloc (GetProcessHeap(), 0,
82 (lstrlenW (name
) + 1) * sizeof(WCHAR
));
83 lstrcpyW (item
.name
, name
);
85 item
.fancyName
= HeapAlloc (GetProcessHeap(), 0,
86 (lstrlenW (fancyName
) + 1) * sizeof(WCHAR
));
87 lstrcpyW (item
.fancyName
, fancyName
);
89 DSA_InsertItem (wdsa
->dsa
, wdsa
->count
, &item
);
93 static int CALLBACK
dsa_destroy_callback (LPVOID p
, LPVOID pData
)
95 ThemeColorOrSize
* item
= (ThemeColorOrSize
*)p
;
96 HeapFree (GetProcessHeap(), 0, item
->name
);
97 HeapFree (GetProcessHeap(), 0, item
->fancyName
);
101 static void free_color_or_size_dsa (WrappedDsa
* wdsa
)
103 DSA_DestroyCallback (wdsa
->dsa
, dsa_destroy_callback
, NULL
);
106 static void create_color_or_size_dsa (WrappedDsa
* wdsa
)
108 wdsa
->dsa
= DSA_Create (sizeof (ThemeColorOrSize
), 1);
112 static ThemeColorOrSize
* color_or_size_dsa_get (WrappedDsa
* wdsa
, int index
)
114 return (ThemeColorOrSize
*)DSA_GetItemPtr (wdsa
->dsa
, index
);
117 static int color_or_size_dsa_find (WrappedDsa
* wdsa
, const WCHAR
* name
)
120 for (; i
< wdsa
->count
; i
++)
122 ThemeColorOrSize
* item
= color_or_size_dsa_get (wdsa
, i
);
123 if (lstrcmpiW (item
->name
, name
) == 0) break;
128 /* A theme file, contains file name, display name, color and size scheme names */
131 WCHAR
* themeFileName
;
137 static HDSA themeFiles
= NULL
;
138 static int themeFilesCount
= 0;
140 static int CALLBACK
theme_dsa_destroy_callback (LPVOID p
, LPVOID pData
)
142 ThemeFile
* item
= (ThemeFile
*)p
;
143 HeapFree (GetProcessHeap(), 0, item
->themeFileName
);
144 HeapFree (GetProcessHeap(), 0, item
->fancyName
);
145 free_color_or_size_dsa (&item
->colors
);
146 free_color_or_size_dsa (&item
->sizes
);
150 /* Free memory occupied by the theme list */
151 static void free_theme_files(void)
153 if (themeFiles
== NULL
) return;
155 DSA_DestroyCallback (themeFiles
, theme_dsa_destroy_callback
, NULL
);
160 typedef HRESULT (WINAPI
* EnumTheme
) (LPWSTR
, LPWSTR
, DWORD
, LPWSTR
);
162 /* fill a string list with either colors or sizes of a theme */
163 static void fill_theme_string_array (const WCHAR
* filename
,
168 WCHAR name
[MAX_PATH
];
170 WINE_TRACE ("%s %p %p\n", wine_dbgstr_w (filename
), wdsa
, enumTheme
);
172 while (SUCCEEDED (enumTheme ((WCHAR
*)filename
, NULL
, index
++, name
)))
174 WINE_TRACE ("%s\n", wine_dbgstr_w (name
));
175 color_or_size_dsa_add (wdsa
, name
, name
);
179 /* Theme enumeration callback, adds theme to theme list */
180 static BOOL CALLBACK
myEnumThemeProc (LPVOID lpReserved
,
181 LPCWSTR pszThemeFileName
,
182 LPCWSTR pszThemeName
,
184 LPVOID lpReserved2
, LPVOID lpData
)
188 /* fill size/color lists */
189 create_color_or_size_dsa (&newEntry
.colors
);
190 fill_theme_string_array (pszThemeFileName
, &newEntry
.colors
, EnumThemeColors
);
191 create_color_or_size_dsa (&newEntry
.sizes
);
192 fill_theme_string_array (pszThemeFileName
, &newEntry
.sizes
, EnumThemeSizes
);
194 newEntry
.themeFileName
= HeapAlloc (GetProcessHeap(), 0,
195 (lstrlenW (pszThemeFileName
) + 1) * sizeof(WCHAR
));
196 lstrcpyW (newEntry
.themeFileName
, pszThemeFileName
);
198 newEntry
.fancyName
= HeapAlloc (GetProcessHeap(), 0,
199 (lstrlenW (pszThemeName
) + 1) * sizeof(WCHAR
));
200 lstrcpyW (newEntry
.fancyName
, pszThemeName
);
202 /*list_add_tail (&themeFiles, &newEntry->entry);*/
203 DSA_InsertItem (themeFiles
, themeFilesCount
, &newEntry
);
209 /* Scan for themes */
210 static void scan_theme_files(void)
212 static const WCHAR themesSubdir
[] = { '\\','T','h','e','m','e','s',0 };
213 WCHAR themesPath
[MAX_PATH
];
217 if (FAILED (SHGetFolderPathW (NULL
, CSIDL_RESOURCES
, NULL
,
218 SHGFP_TYPE_CURRENT
, themesPath
))) return;
220 themeFiles
= DSA_Create (sizeof (ThemeFile
), 1);
221 lstrcatW (themesPath
, themesSubdir
);
223 EnumThemes (themesPath
, myEnumThemeProc
, 0);
226 /* fill the color & size combo boxes for a given theme */
227 static void fill_color_size_combos (ThemeFile
* theme
, HWND comboColor
,
232 SendMessageW (comboColor
, CB_RESETCONTENT
, 0, 0);
233 for (i
= 0; i
< theme
->colors
.count
; i
++)
235 ThemeColorOrSize
* item
= color_or_size_dsa_get (&theme
->colors
, i
);
236 SendMessageW (comboColor
, CB_ADDSTRING
, 0, (LPARAM
)item
->fancyName
);
239 SendMessageW (comboSize
, CB_RESETCONTENT
, 0, 0);
240 for (i
= 0; i
< theme
->sizes
.count
; i
++)
242 ThemeColorOrSize
* item
= color_or_size_dsa_get (&theme
->sizes
, i
);
243 SendMessageW (comboSize
, CB_ADDSTRING
, 0, (LPARAM
)item
->fancyName
);
247 /* Select the item of a combo box that matches a theme's color and size
249 static void select_color_and_size (ThemeFile
* theme
,
250 const WCHAR
* colorName
, HWND comboColor
,
251 const WCHAR
* sizeName
, HWND comboSize
)
253 SendMessageW (comboColor
, CB_SETCURSEL
,
254 color_or_size_dsa_find (&theme
->colors
, colorName
), 0);
255 SendMessageW (comboSize
, CB_SETCURSEL
,
256 color_or_size_dsa_find (&theme
->sizes
, sizeName
), 0);
259 /* Fill theme, color and sizes combo boxes with the know themes and select
260 * the entries matching the currently active theme. */
261 static BOOL
fill_theme_list (HWND comboTheme
, HWND comboColor
, HWND comboSize
)
263 WCHAR textNoTheme
[256];
267 WCHAR currentTheme
[MAX_PATH
];
268 WCHAR currentColor
[MAX_PATH
];
269 WCHAR currentSize
[MAX_PATH
];
270 ThemeFile
* theme
= NULL
;
272 LoadStringW (GetModuleHandle (NULL
), IDS_NOTHEME
, textNoTheme
,
273 sizeof(textNoTheme
) / sizeof(WCHAR
));
275 SendMessageW (comboTheme
, CB_RESETCONTENT
, 0, 0);
276 SendMessageW (comboTheme
, CB_ADDSTRING
, 0, (LPARAM
)textNoTheme
);
278 for (i
= 0; i
< themeFilesCount
; i
++)
280 ThemeFile
* item
= (ThemeFile
*)DSA_GetItemPtr (themeFiles
, i
);
281 SendMessageW (comboTheme
, CB_ADDSTRING
, 0,
282 (LPARAM
)item
->fancyName
);
285 if (IsThemeActive () && SUCCEEDED (GetCurrentThemeName (currentTheme
,
286 sizeof(currentTheme
) / sizeof(WCHAR
),
287 currentColor
, sizeof(currentColor
) / sizeof(WCHAR
),
288 currentSize
, sizeof(currentSize
) / sizeof(WCHAR
))))
290 /* Determine the index of the currently active theme. */
292 for (i
= 0; i
< themeFilesCount
; i
++)
294 theme
= (ThemeFile
*)DSA_GetItemPtr (themeFiles
, i
);
295 if (lstrcmpiW (theme
->themeFileName
, currentTheme
) == 0)
304 /* Current theme not found?... add to the list, then... */
305 WINE_TRACE("Theme %s not in list of enumerated themes",
306 wine_dbgstr_w (currentTheme
));
307 myEnumThemeProc (NULL
, currentTheme
, currentTheme
,
308 currentTheme
, NULL
, NULL
);
309 themeIndex
= themeFilesCount
;
310 theme
= (ThemeFile
*)DSA_GetItemPtr (themeFiles
,
313 fill_color_size_combos (theme
, comboColor
, comboSize
);
314 select_color_and_size (theme
, currentColor
, comboColor
,
315 currentSize
, comboSize
);
319 /* No theme selected */
323 SendMessageW (comboTheme
, CB_SETCURSEL
, themeIndex
, 0);
327 /* Update the color & size combo boxes when the selection of the theme
328 * combo changed. Selects the current color and size scheme if the theme
329 * is currently active, otherwise the first color and size. */
330 BOOL
THEME_update_color_and_size (int themeIndex
, HWND comboColor
,
339 WCHAR currentTheme
[MAX_PATH
];
340 WCHAR currentColor
[MAX_PATH
];
341 WCHAR currentSize
[MAX_PATH
];
343 (ThemeFile
*)DSA_GetItemPtr (themeFiles
, themeIndex
- 1);
345 fill_color_size_combos (theme
, comboColor
, comboSize
);
347 if ((SUCCEEDED (GetCurrentThemeName (currentTheme
,
348 sizeof(currentTheme
) / sizeof(WCHAR
),
349 currentColor
, sizeof(currentColor
) / sizeof(WCHAR
),
350 currentSize
, sizeof(currentSize
) / sizeof(WCHAR
))))
351 && (lstrcmpiW (currentTheme
, theme
->themeFileName
) == 0))
353 select_color_and_size (theme
, currentColor
, comboColor
,
354 currentSize
, comboSize
);
358 SendMessageW (comboColor
, CB_SETCURSEL
, 0, 0);
359 SendMessageW (comboSize
, CB_SETCURSEL
, 0, 0);
365 /* Apply a theme from a given theme, color and size combo box item index. */
366 static void do_apply_theme (int themeIndex
, int colorIndex
, int sizeIndex
)
368 static char b
[] = "\0";
373 ApplyTheme (NULL
, b
, NULL
);
378 (ThemeFile
*)DSA_GetItemPtr (themeFiles
, themeIndex
-1);
379 const WCHAR
* themeFileName
= theme
->themeFileName
;
380 const WCHAR
* colorName
= NULL
;
381 const WCHAR
* sizeName
= NULL
;
383 ThemeColorOrSize
* item
;
385 item
= color_or_size_dsa_get (&theme
->colors
, colorIndex
);
386 colorName
= item
->name
;
388 item
= color_or_size_dsa_get (&theme
->sizes
, sizeIndex
);
389 sizeName
= item
->name
;
391 if (SUCCEEDED (OpenThemeFile (themeFileName
, colorName
, sizeName
,
394 ApplyTheme (hTheme
, b
, NULL
);
395 CloseThemeFile (hTheme
);
399 ApplyTheme (NULL
, b
, NULL
);
407 static void enable_size_and_color_controls (HWND dialog
, BOOL enable
)
409 EnableWindow (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), enable
);
410 EnableWindow (GetDlgItem (dialog
, IDC_THEME_COLORTEXT
), enable
);
411 EnableWindow (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), enable
);
412 EnableWindow (GetDlgItem (dialog
, IDC_THEME_SIZETEXT
), enable
);
415 static void init_dialog (HWND dialog
)
420 if (!fill_theme_list (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
421 GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
422 GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
)))
424 SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
425 SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
426 enable_size_and_color_controls (dialog
, FALSE
);
430 enable_size_and_color_controls (dialog
, TRUE
);
437 static void on_theme_changed(HWND dialog
) {
438 int index
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
440 if (!THEME_update_color_and_size (index
, GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
441 GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
)))
443 SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
444 SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
445 enable_size_and_color_controls (dialog
, FALSE
);
449 enable_size_and_color_controls (dialog
, TRUE
);
454 static void apply_theme(HWND dialog
)
456 int themeIndex
, colorIndex
, sizeIndex
;
458 if (!theme_dirty
) return;
460 themeIndex
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
462 colorIndex
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
464 sizeIndex
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
),
467 do_apply_theme (themeIndex
, colorIndex
, sizeIndex
);
472 ThemeDlgProc (HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
483 set_window_title(hDlg
);
487 switch(HIWORD(wParam
)) {
488 case CBN_SELCHANGE
: {
489 if (updating_ui
) break;
490 SendMessage(GetParent(hDlg
), PSM_CHANGED
, 0, 0);
491 switch (LOWORD(wParam
))
493 case IDC_THEME_THEMECOMBO
: on_theme_changed(hDlg
); break;
494 case IDC_THEME_COLORCOMBO
: /* fall through */
495 case IDC_THEME_SIZECOMBO
: theme_dirty
= TRUE
; break;
507 switch (((LPNMHDR
)lParam
)->code
) {
508 case PSN_KILLACTIVE
: {
509 SetWindowLongPtr(hDlg
, DWLP_MSGRESULT
, FALSE
);
515 SetWindowLongPtr(hDlg
, DWLP_MSGRESULT
, PSNRET_NOERROR
);
518 case PSN_SETACTIVE
: {