Add an "Appearance" tab to control the currently active theme.
[wine.git] / programs / winecfg / theme.c
blobdcad25b5b982bcf643e5959522c486dad040efb7
1 /*
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
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <stdio.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <uxtheme.h>
31 #include <tmschema.h>
32 #include <shlobj.h>
33 #include <wine/debug.h>
35 #include "resource.h"
36 #include "winecfg.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,
44 LPCWSTR pszThemeName,
45 LPCWSTR pszToolTip, LPVOID lpReserved2,
46 LPVOID lpData);
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,
55 DWORD unknown);
56 HRESULT WINAPI CloseThemeFile (HTHEMEFILE hThemeFile);
57 HRESULT WINAPI EnumThemes (LPCWSTR pszThemePath, EnumThemeProc callback,
58 LPVOID lpData);
60 /* A struct to keep both the internal and "fancy" name of a color or size */
61 typedef struct
63 WCHAR* name;
64 WCHAR* fancyName;
65 } ThemeColorOrSize;
67 /* wrapper around DSA that also keeps an item count */
68 typedef struct
70 HDSA dsa;
71 int count;
72 } WrappedDsa;
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);
90 wdsa->count++;
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);
98 return 1;
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);
109 wdsa->count = 0;
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)
119 int i = 0;
120 for (; i < wdsa->count; i++)
122 ThemeColorOrSize* item = color_or_size_dsa_get (wdsa, i);
123 if (lstrcmpiW (item->name, name) == 0) break;
125 return i;
128 /* A theme file, contains file name, display name, color and size scheme names */
129 typedef struct
131 WCHAR* themeFileName;
132 WCHAR* fancyName;
133 WrappedDsa colors;
134 WrappedDsa sizes;
135 } ThemeFile;
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);
147 return 1;
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);
156 themeFiles = NULL;
157 themeFilesCount = 0;
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,
164 WrappedDsa* wdsa,
165 EnumTheme enumTheme)
167 DWORD index = 0;
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,
183 LPCWSTR pszToolTip,
184 LPVOID lpReserved2, LPVOID lpData)
186 ThemeFile newEntry;
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);
204 themeFilesCount++;
206 return TRUE;
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];
215 free_theme_files();
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,
228 HWND comboSize)
230 int i;
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
248 * scheme. */
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];
264 int themeIndex = 0;
265 BOOL ret = TRUE;
266 int i;
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. */
291 BOOL found = FALSE;
292 for (i = 0; i < themeFilesCount; i++)
294 theme = (ThemeFile*)DSA_GetItemPtr (themeFiles, i);
295 if (lstrcmpiW (theme->themeFileName, currentTheme) == 0)
297 found = TRUE;
298 themeIndex = i+1;
299 break;
302 if (!found)
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,
311 themeFilesCount-1);
313 fill_color_size_combos (theme, comboColor, comboSize);
314 select_color_and_size (theme, currentColor, comboColor,
315 currentSize, comboSize);
317 else
319 /* No theme selected */
320 ret = FALSE;
323 SendMessageW (comboTheme, CB_SETCURSEL, themeIndex, 0);
324 return ret;
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,
331 HWND comboSize)
333 if (themeIndex == 0)
335 return FALSE;
337 else
339 WCHAR currentTheme[MAX_PATH];
340 WCHAR currentColor[MAX_PATH];
341 WCHAR currentSize[MAX_PATH];
342 ThemeFile* theme =
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);
356 else
358 SendMessageW (comboColor, CB_SETCURSEL, 0, 0);
359 SendMessageW (comboSize, CB_SETCURSEL, 0, 0);
362 return TRUE;
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";
370 if (themeIndex == 0)
372 /* no theme */
373 ApplyTheme (NULL, b, NULL);
375 else
377 ThemeFile* theme =
378 (ThemeFile*)DSA_GetItemPtr (themeFiles, themeIndex-1);
379 const WCHAR* themeFileName = theme->themeFileName;
380 const WCHAR* colorName = NULL;
381 const WCHAR* sizeName = NULL;
382 HTHEMEFILE hTheme;
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,
392 &hTheme, 0)))
394 ApplyTheme (hTheme, b, NULL);
395 CloseThemeFile (hTheme);
397 else
399 ApplyTheme (NULL, b, NULL);
404 int updating_ui;
405 BOOL theme_dirty;
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)
417 updating_ui = TRUE;
419 scan_theme_files();
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);
428 else
430 enable_size_and_color_controls (dialog, TRUE);
432 theme_dirty = FALSE;
434 updating_ui = FALSE;
437 static void on_theme_changed(HWND dialog) {
438 int index = SendMessageW (GetDlgItem (dialog, IDC_THEME_THEMECOMBO),
439 CB_GETCURSEL, 0, 0);
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);
447 else
449 enable_size_and_color_controls (dialog, TRUE);
451 theme_dirty = 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),
461 CB_GETCURSEL, 0, 0);
462 colorIndex = SendMessageW (GetDlgItem (dialog, IDC_THEME_COLORCOMBO),
463 CB_GETCURSEL, 0, 0);
464 sizeIndex = SendMessageW (GetDlgItem (dialog, IDC_THEME_SIZECOMBO),
465 CB_GETCURSEL, 0, 0);
467 do_apply_theme (themeIndex, colorIndex, sizeIndex);
468 theme_dirty = FALSE;
471 INT_PTR CALLBACK
472 ThemeDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
474 switch (uMsg) {
475 case WM_INITDIALOG:
476 break;
478 case WM_DESTROY:
479 free_theme_files();
480 break;
482 case WM_SHOWWINDOW:
483 set_window_title(hDlg);
484 break;
486 case WM_COMMAND:
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;
497 break;
500 default:
501 break;
503 break;
506 case WM_NOTIFY:
507 switch (((LPNMHDR)lParam)->code) {
508 case PSN_KILLACTIVE: {
509 SetWindowLongPtr(hDlg, DWLP_MSGRESULT, FALSE);
510 break;
512 case PSN_APPLY: {
513 apply();
514 apply_theme(hDlg);
515 SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
516 break;
518 case PSN_SETACTIVE: {
519 init_dialog (hDlg);
520 break;
523 break;
525 default:
526 break;
528 return FALSE;