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
31 #include <wine/debug.h>
36 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
38 /* UXTHEME functions not in the headers */
40 typedef struct tagTHEMENAMES
42 WCHAR szName
[MAX_PATH
+1];
43 WCHAR szDisplayName
[MAX_PATH
+1];
44 WCHAR szTooltip
[MAX_PATH
+1];
45 } THEMENAMES
, *PTHEMENAMES
;
47 typedef void* HTHEMEFILE
;
48 typedef BOOL (CALLBACK
*EnumThemeProc
)(LPVOID lpReserved
,
49 LPCWSTR pszThemeFileName
,
51 LPCWSTR pszToolTip
, LPVOID lpReserved2
,
54 HRESULT WINAPI
EnumThemeColors (LPWSTR pszThemeFileName
, LPWSTR pszSizeName
,
55 DWORD dwColorNum
, PTHEMENAMES pszColorNames
);
56 HRESULT WINAPI
EnumThemeSizes (LPWSTR pszThemeFileName
, LPWSTR pszColorName
,
57 DWORD dwSizeNum
, PTHEMENAMES pszSizeNames
);
58 HRESULT WINAPI
ApplyTheme (HTHEMEFILE hThemeFile
, char* unknown
, HWND hWnd
);
59 HRESULT WINAPI
OpenThemeFile (LPCWSTR pszThemeFileName
, LPCWSTR pszColorName
,
60 LPCWSTR pszSizeName
, HTHEMEFILE
* hThemeFile
,
62 HRESULT WINAPI
CloseThemeFile (HTHEMEFILE hThemeFile
);
63 HRESULT WINAPI
EnumThemes (LPCWSTR pszThemePath
, EnumThemeProc callback
,
66 /* A struct to keep both the internal and "fancy" name of a color or size */
73 /* wrapper around DSA that also keeps an item count */
80 /* Some helper functions to deal with ThemeColorOrSize structs in WrappedDSAs */
82 static void color_or_size_dsa_add (WrappedDsa
* wdsa
, const WCHAR
* name
,
83 const WCHAR
* fancyName
)
85 ThemeColorOrSize item
;
87 item
.name
= HeapAlloc (GetProcessHeap(), 0,
88 (lstrlenW (name
) + 1) * sizeof(WCHAR
));
89 lstrcpyW (item
.name
, name
);
91 item
.fancyName
= HeapAlloc (GetProcessHeap(), 0,
92 (lstrlenW (fancyName
) + 1) * sizeof(WCHAR
));
93 lstrcpyW (item
.fancyName
, fancyName
);
95 DSA_InsertItem (wdsa
->dsa
, wdsa
->count
, &item
);
99 static int CALLBACK
dsa_destroy_callback (LPVOID p
, LPVOID pData
)
101 ThemeColorOrSize
* item
= (ThemeColorOrSize
*)p
;
102 HeapFree (GetProcessHeap(), 0, item
->name
);
103 HeapFree (GetProcessHeap(), 0, item
->fancyName
);
107 static void free_color_or_size_dsa (WrappedDsa
* wdsa
)
109 DSA_DestroyCallback (wdsa
->dsa
, dsa_destroy_callback
, NULL
);
112 static void create_color_or_size_dsa (WrappedDsa
* wdsa
)
114 wdsa
->dsa
= DSA_Create (sizeof (ThemeColorOrSize
), 1);
118 static ThemeColorOrSize
* color_or_size_dsa_get (WrappedDsa
* wdsa
, int index
)
120 return (ThemeColorOrSize
*)DSA_GetItemPtr (wdsa
->dsa
, index
);
123 static int color_or_size_dsa_find (WrappedDsa
* wdsa
, const WCHAR
* name
)
126 for (; i
< wdsa
->count
; i
++)
128 ThemeColorOrSize
* item
= color_or_size_dsa_get (wdsa
, i
);
129 if (lstrcmpiW (item
->name
, name
) == 0) break;
134 /* A theme file, contains file name, display name, color and size scheme names */
137 WCHAR
* themeFileName
;
143 static HDSA themeFiles
= NULL
;
144 static int themeFilesCount
= 0;
146 static int CALLBACK
theme_dsa_destroy_callback (LPVOID p
, LPVOID pData
)
148 ThemeFile
* item
= (ThemeFile
*)p
;
149 HeapFree (GetProcessHeap(), 0, item
->themeFileName
);
150 HeapFree (GetProcessHeap(), 0, item
->fancyName
);
151 free_color_or_size_dsa (&item
->colors
);
152 free_color_or_size_dsa (&item
->sizes
);
156 /* Free memory occupied by the theme list */
157 static void free_theme_files(void)
159 if (themeFiles
== NULL
) return;
161 DSA_DestroyCallback (themeFiles
, theme_dsa_destroy_callback
, NULL
);
166 typedef HRESULT (WINAPI
* EnumTheme
) (LPWSTR
, LPWSTR
, DWORD
, PTHEMENAMES
);
168 /* fill a string list with either colors or sizes of a theme */
169 static void fill_theme_string_array (const WCHAR
* filename
,
176 WINE_TRACE ("%s %p %p\n", wine_dbgstr_w (filename
), wdsa
, enumTheme
);
178 while (SUCCEEDED (enumTheme ((WCHAR
*)filename
, NULL
, index
++, &names
)))
180 WINE_TRACE ("%s: %s\n", wine_dbgstr_w (names
.szName
),
181 wine_dbgstr_w (names
.szDisplayName
));
182 color_or_size_dsa_add (wdsa
, names
.szName
, names
.szDisplayName
);
186 /* Theme enumeration callback, adds theme to theme list */
187 static BOOL CALLBACK
myEnumThemeProc (LPVOID lpReserved
,
188 LPCWSTR pszThemeFileName
,
189 LPCWSTR pszThemeName
,
191 LPVOID lpReserved2
, LPVOID lpData
)
195 /* fill size/color lists */
196 create_color_or_size_dsa (&newEntry
.colors
);
197 fill_theme_string_array (pszThemeFileName
, &newEntry
.colors
, EnumThemeColors
);
198 create_color_or_size_dsa (&newEntry
.sizes
);
199 fill_theme_string_array (pszThemeFileName
, &newEntry
.sizes
, EnumThemeSizes
);
201 newEntry
.themeFileName
= HeapAlloc (GetProcessHeap(), 0,
202 (lstrlenW (pszThemeFileName
) + 1) * sizeof(WCHAR
));
203 lstrcpyW (newEntry
.themeFileName
, pszThemeFileName
);
205 newEntry
.fancyName
= HeapAlloc (GetProcessHeap(), 0,
206 (lstrlenW (pszThemeName
) + 1) * sizeof(WCHAR
));
207 lstrcpyW (newEntry
.fancyName
, pszThemeName
);
209 /*list_add_tail (&themeFiles, &newEntry->entry);*/
210 DSA_InsertItem (themeFiles
, themeFilesCount
, &newEntry
);
216 /* Scan for themes */
217 static void scan_theme_files(void)
219 static const WCHAR themesSubdir
[] = { '\\','T','h','e','m','e','s',0 };
220 WCHAR themesPath
[MAX_PATH
];
224 if (FAILED (SHGetFolderPathW (NULL
, CSIDL_RESOURCES
, NULL
,
225 SHGFP_TYPE_CURRENT
, themesPath
))) return;
227 themeFiles
= DSA_Create (sizeof (ThemeFile
), 1);
228 lstrcatW (themesPath
, themesSubdir
);
230 EnumThemes (themesPath
, myEnumThemeProc
, 0);
233 /* fill the color & size combo boxes for a given theme */
234 static void fill_color_size_combos (ThemeFile
* theme
, HWND comboColor
,
239 SendMessageW (comboColor
, CB_RESETCONTENT
, 0, 0);
240 for (i
= 0; i
< theme
->colors
.count
; i
++)
242 ThemeColorOrSize
* item
= color_or_size_dsa_get (&theme
->colors
, i
);
243 SendMessageW (comboColor
, CB_ADDSTRING
, 0, (LPARAM
)item
->fancyName
);
246 SendMessageW (comboSize
, CB_RESETCONTENT
, 0, 0);
247 for (i
= 0; i
< theme
->sizes
.count
; i
++)
249 ThemeColorOrSize
* item
= color_or_size_dsa_get (&theme
->sizes
, i
);
250 SendMessageW (comboSize
, CB_ADDSTRING
, 0, (LPARAM
)item
->fancyName
);
254 /* Select the item of a combo box that matches a theme's color and size
256 static void select_color_and_size (ThemeFile
* theme
,
257 const WCHAR
* colorName
, HWND comboColor
,
258 const WCHAR
* sizeName
, HWND comboSize
)
260 SendMessageW (comboColor
, CB_SETCURSEL
,
261 color_or_size_dsa_find (&theme
->colors
, colorName
), 0);
262 SendMessageW (comboSize
, CB_SETCURSEL
,
263 color_or_size_dsa_find (&theme
->sizes
, sizeName
), 0);
266 /* Fill theme, color and sizes combo boxes with the know themes and select
267 * the entries matching the currently active theme. */
268 static BOOL
fill_theme_list (HWND comboTheme
, HWND comboColor
, HWND comboSize
)
270 WCHAR textNoTheme
[256];
274 WCHAR currentTheme
[MAX_PATH
];
275 WCHAR currentColor
[MAX_PATH
];
276 WCHAR currentSize
[MAX_PATH
];
277 ThemeFile
* theme
= NULL
;
279 LoadStringW (GetModuleHandle (NULL
), IDS_NOTHEME
, textNoTheme
,
280 sizeof(textNoTheme
) / sizeof(WCHAR
));
282 SendMessageW (comboTheme
, CB_RESETCONTENT
, 0, 0);
283 SendMessageW (comboTheme
, CB_ADDSTRING
, 0, (LPARAM
)textNoTheme
);
285 for (i
= 0; i
< themeFilesCount
; i
++)
287 ThemeFile
* item
= (ThemeFile
*)DSA_GetItemPtr (themeFiles
, i
);
288 SendMessageW (comboTheme
, CB_ADDSTRING
, 0,
289 (LPARAM
)item
->fancyName
);
292 if (IsThemeActive () && SUCCEEDED (GetCurrentThemeName (currentTheme
,
293 sizeof(currentTheme
) / sizeof(WCHAR
),
294 currentColor
, sizeof(currentColor
) / sizeof(WCHAR
),
295 currentSize
, sizeof(currentSize
) / sizeof(WCHAR
))))
297 /* Determine the index of the currently active theme. */
299 for (i
= 0; i
< themeFilesCount
; i
++)
301 theme
= (ThemeFile
*)DSA_GetItemPtr (themeFiles
, i
);
302 if (lstrcmpiW (theme
->themeFileName
, currentTheme
) == 0)
311 /* Current theme not found?... add to the list, then... */
312 WINE_TRACE("Theme %s not in list of enumerated themes",
313 wine_dbgstr_w (currentTheme
));
314 myEnumThemeProc (NULL
, currentTheme
, currentTheme
,
315 currentTheme
, NULL
, NULL
);
316 themeIndex
= themeFilesCount
;
317 theme
= (ThemeFile
*)DSA_GetItemPtr (themeFiles
,
320 fill_color_size_combos (theme
, comboColor
, comboSize
);
321 select_color_and_size (theme
, currentColor
, comboColor
,
322 currentSize
, comboSize
);
326 /* No theme selected */
330 SendMessageW (comboTheme
, CB_SETCURSEL
, themeIndex
, 0);
334 /* Update the color & size combo boxes when the selection of the theme
335 * combo changed. Selects the current color and size scheme if the theme
336 * is currently active, otherwise the first color and size. */
337 static BOOL
update_color_and_size (int themeIndex
, HWND comboColor
,
346 WCHAR currentTheme
[MAX_PATH
];
347 WCHAR currentColor
[MAX_PATH
];
348 WCHAR currentSize
[MAX_PATH
];
350 (ThemeFile
*)DSA_GetItemPtr (themeFiles
, themeIndex
- 1);
352 fill_color_size_combos (theme
, comboColor
, comboSize
);
354 if ((SUCCEEDED (GetCurrentThemeName (currentTheme
,
355 sizeof(currentTheme
) / sizeof(WCHAR
),
356 currentColor
, sizeof(currentColor
) / sizeof(WCHAR
),
357 currentSize
, sizeof(currentSize
) / sizeof(WCHAR
))))
358 && (lstrcmpiW (currentTheme
, theme
->themeFileName
) == 0))
360 select_color_and_size (theme
, currentColor
, comboColor
,
361 currentSize
, comboSize
);
365 SendMessageW (comboColor
, CB_SETCURSEL
, 0, 0);
366 SendMessageW (comboSize
, CB_SETCURSEL
, 0, 0);
372 /* Apply a theme from a given theme, color and size combo box item index. */
373 static void do_apply_theme (int themeIndex
, int colorIndex
, int sizeIndex
)
375 static char b
[] = "\0";
380 ApplyTheme (NULL
, b
, NULL
);
385 (ThemeFile
*)DSA_GetItemPtr (themeFiles
, themeIndex
-1);
386 const WCHAR
* themeFileName
= theme
->themeFileName
;
387 const WCHAR
* colorName
= NULL
;
388 const WCHAR
* sizeName
= NULL
;
390 ThemeColorOrSize
* item
;
392 item
= color_or_size_dsa_get (&theme
->colors
, colorIndex
);
393 colorName
= item
->name
;
395 item
= color_or_size_dsa_get (&theme
->sizes
, sizeIndex
);
396 sizeName
= item
->name
;
398 if (SUCCEEDED (OpenThemeFile (themeFileName
, colorName
, sizeName
,
401 ApplyTheme (hTheme
, b
, NULL
);
402 CloseThemeFile (hTheme
);
406 ApplyTheme (NULL
, b
, NULL
);
414 static void enable_size_and_color_controls (HWND dialog
, BOOL enable
)
416 EnableWindow (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), enable
);
417 EnableWindow (GetDlgItem (dialog
, IDC_THEME_COLORTEXT
), enable
);
418 EnableWindow (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), enable
);
419 EnableWindow (GetDlgItem (dialog
, IDC_THEME_SIZETEXT
), enable
);
422 static void init_dialog (HWND dialog
)
427 if (!fill_theme_list (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
428 GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
429 GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
)))
431 SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
432 SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
433 enable_size_and_color_controls (dialog
, FALSE
);
437 enable_size_and_color_controls (dialog
, TRUE
);
444 static void on_theme_changed(HWND dialog
) {
445 int index
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
447 if (!update_color_and_size (index
, GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
448 GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
)))
450 SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
451 SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
452 enable_size_and_color_controls (dialog
, FALSE
);
456 enable_size_and_color_controls (dialog
, TRUE
);
461 static void apply_theme(HWND dialog
)
463 int themeIndex
, colorIndex
, sizeIndex
;
465 if (!theme_dirty
) return;
467 themeIndex
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
469 colorIndex
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
471 sizeIndex
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
),
474 do_apply_theme (themeIndex
, colorIndex
, sizeIndex
);
478 static void on_theme_install(HWND dialog
)
480 static const WCHAR filterMask
[] = {0,'*','.','m','s','s','t','y','l','e','s',0,0};
481 const int filterMaskLen
= sizeof(filterMask
)/sizeof(filterMask
[0]);
483 WCHAR filetitle
[MAX_PATH
];
484 WCHAR file
[MAX_PATH
];
488 LoadStringW (GetModuleHandle (NULL
), IDS_THEMEFILE
,
489 filter
, sizeof (filter
) / sizeof (filter
[0]) - filterMaskLen
);
490 memcpy (filter
+ lstrlenW (filter
), filterMask
,
491 filterMaskLen
* sizeof (WCHAR
));
492 LoadStringW (GetModuleHandle (NULL
), IDS_THEMEFILE_SELECT
,
493 title
, sizeof (title
) / sizeof (title
[0]));
495 ofn
.lStructSize
= sizeof(OPENFILENAMEW
);
498 ofn
.lpstrFilter
= filter
;
499 ofn
.lpstrCustomFilter
= NULL
;
500 ofn
.nMaxCustFilter
= 0;
501 ofn
.nFilterIndex
= 0;
502 ofn
.lpstrFile
= file
;
503 ofn
.lpstrFile
[0] = '\0';
504 ofn
.nMaxFile
= sizeof(file
)/sizeof(filetitle
[0]);
505 ofn
.lpstrFileTitle
= filetitle
;
506 ofn
.lpstrFileTitle
[0] = '\0';
507 ofn
.nMaxFileTitle
= sizeof(filetitle
)/sizeof(filetitle
[0]);
508 ofn
.lpstrInitialDir
= NULL
;
509 ofn
.lpstrTitle
= title
;
510 ofn
.Flags
= OFN_FILEMUSTEXIST
| OFN_PATHMUSTEXIST
| OFN_HIDEREADONLY
;
512 ofn
.nFileExtension
= 0;
513 ofn
.lpstrDefExt
= NULL
;
516 ofn
.lpTemplateName
= NULL
;
518 if (GetOpenFileNameW(&ofn
))
520 static const WCHAR themesSubdir
[] = { '\\','T','h','e','m','e','s',0 };
521 static const WCHAR backslash
[] = { '\\',0 };
522 WCHAR themeFilePath
[MAX_PATH
];
523 SHFILEOPSTRUCTW shfop
;
525 if (FAILED (SHGetFolderPathW (NULL
, CSIDL_RESOURCES
, NULL
,
526 SHGFP_TYPE_CURRENT
, themeFilePath
))) return;
528 PathRemoveExtensionW (filetitle
);
530 /* Construct path into which the theme file goes */
531 lstrcatW (themeFilePath
, themesSubdir
);
532 lstrcatW (themeFilePath
, backslash
);
533 lstrcatW (themeFilePath
, filetitle
);
535 /* Create the directory */
536 SHCreateDirectoryExW (dialog
, themeFilePath
, NULL
);
538 /* Append theme file name itself */
539 lstrcatW (themeFilePath
, backslash
);
540 lstrcatW (themeFilePath
, PathFindFileNameW (file
));
541 /* SHFileOperation() takes lists as input, so double-nullterminate */
542 themeFilePath
[lstrlenW (themeFilePath
)+1] = 0;
543 file
[lstrlenW (file
)+1] = 0;
546 WINE_TRACE("copying: %s -> %s\n", wine_dbgstr_w (file
),
547 wine_dbgstr_w (themeFilePath
));
549 shfop
.wFunc
= FO_COPY
;
551 shfop
.pTo
= themeFilePath
;
552 shfop
.fFlags
= FOF_NOCONFIRMMKDIR
;
553 if (SHFileOperationW (&shfop
) == 0)
556 if (!fill_theme_list (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
557 GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
558 GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
)))
560 SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
561 SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
562 enable_size_and_color_controls (dialog
, FALSE
);
566 enable_size_and_color_controls (dialog
, TRUE
);
570 WINE_TRACE("copy operation failed\n");
572 else WINE_TRACE("user cancelled\n");
576 ThemeDlgProc (HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
587 set_window_title(hDlg
);
591 switch(HIWORD(wParam
)) {
592 case CBN_SELCHANGE
: {
593 if (updating_ui
) break;
594 SendMessage(GetParent(hDlg
), PSM_CHANGED
, 0, 0);
595 switch (LOWORD(wParam
))
597 case IDC_THEME_THEMECOMBO
: on_theme_changed(hDlg
); break;
598 case IDC_THEME_COLORCOMBO
: /* fall through */
599 case IDC_THEME_SIZECOMBO
: theme_dirty
= TRUE
; break;
607 switch (LOWORD(wParam
))
609 case IDC_THEME_INSTALL
:
610 if (HIWORD(wParam
) != BN_CLICKED
) break;
611 on_theme_install (hDlg
);
621 switch (((LPNMHDR
)lParam
)->code
) {
622 case PSN_KILLACTIVE
: {
623 SetWindowLongPtr(hDlg
, DWLP_MSGRESULT
, FALSE
);
629 SetWindowLongPtr(hDlg
, DWLP_MSGRESULT
, PSNRET_NOERROR
);
632 case PSN_SETACTIVE
: {