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
34 #include <wine/debug.h>
39 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
41 /* UXTHEME functions not in the headers */
43 typedef struct tagTHEMENAMES
45 WCHAR szName
[MAX_PATH
+1];
46 WCHAR szDisplayName
[MAX_PATH
+1];
47 WCHAR szTooltip
[MAX_PATH
+1];
48 } THEMENAMES
, *PTHEMENAMES
;
50 typedef void* HTHEMEFILE
;
51 typedef BOOL (CALLBACK
*EnumThemeProc
)(LPVOID lpReserved
,
52 LPCWSTR pszThemeFileName
,
54 LPCWSTR pszToolTip
, LPVOID lpReserved2
,
57 HRESULT WINAPI
EnumThemeColors (LPWSTR pszThemeFileName
, LPWSTR pszSizeName
,
58 DWORD dwColorNum
, PTHEMENAMES pszColorNames
);
59 HRESULT WINAPI
EnumThemeSizes (LPWSTR pszThemeFileName
, LPWSTR pszColorName
,
60 DWORD dwSizeNum
, PTHEMENAMES pszSizeNames
);
61 HRESULT WINAPI
ApplyTheme (HTHEMEFILE hThemeFile
, char* unknown
, HWND hWnd
);
62 HRESULT WINAPI
OpenThemeFile (LPCWSTR pszThemeFileName
, LPCWSTR pszColorName
,
63 LPCWSTR pszSizeName
, HTHEMEFILE
* hThemeFile
,
65 HRESULT WINAPI
CloseThemeFile (HTHEMEFILE hThemeFile
);
66 HRESULT WINAPI
EnumThemes (LPCWSTR pszThemePath
, EnumThemeProc callback
,
69 /* A struct to keep both the internal and "fancy" name of a color or size */
76 /* wrapper around DSA that also keeps an item count */
83 /* Some helper functions to deal with ThemeColorOrSize structs in WrappedDSAs */
85 static void color_or_size_dsa_add (WrappedDsa
* wdsa
, const WCHAR
* name
,
86 const WCHAR
* fancyName
)
88 ThemeColorOrSize item
;
90 item
.name
= HeapAlloc (GetProcessHeap(), 0,
91 (lstrlenW (name
) + 1) * sizeof(WCHAR
));
92 lstrcpyW (item
.name
, name
);
94 item
.fancyName
= HeapAlloc (GetProcessHeap(), 0,
95 (lstrlenW (fancyName
) + 1) * sizeof(WCHAR
));
96 lstrcpyW (item
.fancyName
, fancyName
);
98 DSA_InsertItem (wdsa
->dsa
, wdsa
->count
, &item
);
102 static int CALLBACK
dsa_destroy_callback (LPVOID p
, LPVOID pData
)
104 ThemeColorOrSize
* item
= (ThemeColorOrSize
*)p
;
105 HeapFree (GetProcessHeap(), 0, item
->name
);
106 HeapFree (GetProcessHeap(), 0, item
->fancyName
);
110 static void free_color_or_size_dsa (WrappedDsa
* wdsa
)
112 DSA_DestroyCallback (wdsa
->dsa
, dsa_destroy_callback
, NULL
);
115 static void create_color_or_size_dsa (WrappedDsa
* wdsa
)
117 wdsa
->dsa
= DSA_Create (sizeof (ThemeColorOrSize
), 1);
121 static ThemeColorOrSize
* color_or_size_dsa_get (WrappedDsa
* wdsa
, int index
)
123 return (ThemeColorOrSize
*)DSA_GetItemPtr (wdsa
->dsa
, index
);
126 static int color_or_size_dsa_find (WrappedDsa
* wdsa
, const WCHAR
* name
)
129 for (; i
< wdsa
->count
; i
++)
131 ThemeColorOrSize
* item
= color_or_size_dsa_get (wdsa
, i
);
132 if (lstrcmpiW (item
->name
, name
) == 0) break;
137 /* A theme file, contains file name, display name, color and size scheme names */
140 WCHAR
* themeFileName
;
146 static HDSA themeFiles
= NULL
;
147 static int themeFilesCount
= 0;
149 static int CALLBACK
theme_dsa_destroy_callback (LPVOID p
, LPVOID pData
)
151 ThemeFile
* item
= (ThemeFile
*)p
;
152 HeapFree (GetProcessHeap(), 0, item
->themeFileName
);
153 HeapFree (GetProcessHeap(), 0, item
->fancyName
);
154 free_color_or_size_dsa (&item
->colors
);
155 free_color_or_size_dsa (&item
->sizes
);
159 /* Free memory occupied by the theme list */
160 static void free_theme_files(void)
162 if (themeFiles
== NULL
) return;
164 DSA_DestroyCallback (themeFiles
, theme_dsa_destroy_callback
, NULL
);
169 typedef HRESULT (WINAPI
* EnumTheme
) (LPWSTR
, LPWSTR
, DWORD
, PTHEMENAMES
);
171 /* fill a string list with either colors or sizes of a theme */
172 static void fill_theme_string_array (const WCHAR
* filename
,
179 WINE_TRACE ("%s %p %p\n", wine_dbgstr_w (filename
), wdsa
, enumTheme
);
181 while (SUCCEEDED (enumTheme ((WCHAR
*)filename
, NULL
, index
++, &names
)))
183 WINE_TRACE ("%s: %s\n", wine_dbgstr_w (names
.szName
),
184 wine_dbgstr_w (names
.szDisplayName
));
185 color_or_size_dsa_add (wdsa
, names
.szName
, names
.szDisplayName
);
189 /* Theme enumeration callback, adds theme to theme list */
190 static BOOL CALLBACK
myEnumThemeProc (LPVOID lpReserved
,
191 LPCWSTR pszThemeFileName
,
192 LPCWSTR pszThemeName
,
194 LPVOID lpReserved2
, LPVOID lpData
)
198 /* fill size/color lists */
199 create_color_or_size_dsa (&newEntry
.colors
);
200 fill_theme_string_array (pszThemeFileName
, &newEntry
.colors
, EnumThemeColors
);
201 create_color_or_size_dsa (&newEntry
.sizes
);
202 fill_theme_string_array (pszThemeFileName
, &newEntry
.sizes
, EnumThemeSizes
);
204 newEntry
.themeFileName
= HeapAlloc (GetProcessHeap(), 0,
205 (lstrlenW (pszThemeFileName
) + 1) * sizeof(WCHAR
));
206 lstrcpyW (newEntry
.themeFileName
, pszThemeFileName
);
208 newEntry
.fancyName
= HeapAlloc (GetProcessHeap(), 0,
209 (lstrlenW (pszThemeName
) + 1) * sizeof(WCHAR
));
210 lstrcpyW (newEntry
.fancyName
, pszThemeName
);
212 /*list_add_tail (&themeFiles, &newEntry->entry);*/
213 DSA_InsertItem (themeFiles
, themeFilesCount
, &newEntry
);
219 /* Scan for themes */
220 static void scan_theme_files(void)
222 static const WCHAR themesSubdir
[] = { '\\','T','h','e','m','e','s',0 };
223 WCHAR themesPath
[MAX_PATH
];
227 if (FAILED (SHGetFolderPathW (NULL
, CSIDL_RESOURCES
, NULL
,
228 SHGFP_TYPE_CURRENT
, themesPath
))) return;
230 themeFiles
= DSA_Create (sizeof (ThemeFile
), 1);
231 lstrcatW (themesPath
, themesSubdir
);
233 EnumThemes (themesPath
, myEnumThemeProc
, 0);
236 /* fill the color & size combo boxes for a given theme */
237 static void fill_color_size_combos (ThemeFile
* theme
, HWND comboColor
,
242 SendMessageW (comboColor
, CB_RESETCONTENT
, 0, 0);
243 for (i
= 0; i
< theme
->colors
.count
; i
++)
245 ThemeColorOrSize
* item
= color_or_size_dsa_get (&theme
->colors
, i
);
246 SendMessageW (comboColor
, CB_ADDSTRING
, 0, (LPARAM
)item
->fancyName
);
249 SendMessageW (comboSize
, CB_RESETCONTENT
, 0, 0);
250 for (i
= 0; i
< theme
->sizes
.count
; i
++)
252 ThemeColorOrSize
* item
= color_or_size_dsa_get (&theme
->sizes
, i
);
253 SendMessageW (comboSize
, CB_ADDSTRING
, 0, (LPARAM
)item
->fancyName
);
257 /* Select the item of a combo box that matches a theme's color and size
259 static void select_color_and_size (ThemeFile
* theme
,
260 const WCHAR
* colorName
, HWND comboColor
,
261 const WCHAR
* sizeName
, HWND comboSize
)
263 SendMessageW (comboColor
, CB_SETCURSEL
,
264 color_or_size_dsa_find (&theme
->colors
, colorName
), 0);
265 SendMessageW (comboSize
, CB_SETCURSEL
,
266 color_or_size_dsa_find (&theme
->sizes
, sizeName
), 0);
269 /* Fill theme, color and sizes combo boxes with the know themes and select
270 * the entries matching the currently active theme. */
271 static BOOL
fill_theme_list (HWND comboTheme
, HWND comboColor
, HWND comboSize
)
273 WCHAR textNoTheme
[256];
277 WCHAR currentTheme
[MAX_PATH
];
278 WCHAR currentColor
[MAX_PATH
];
279 WCHAR currentSize
[MAX_PATH
];
280 ThemeFile
* theme
= NULL
;
282 LoadStringW (GetModuleHandle (NULL
), IDS_NOTHEME
, textNoTheme
,
283 sizeof(textNoTheme
) / sizeof(WCHAR
));
285 SendMessageW (comboTheme
, CB_RESETCONTENT
, 0, 0);
286 SendMessageW (comboTheme
, CB_ADDSTRING
, 0, (LPARAM
)textNoTheme
);
288 for (i
= 0; i
< themeFilesCount
; i
++)
290 ThemeFile
* item
= (ThemeFile
*)DSA_GetItemPtr (themeFiles
, i
);
291 SendMessageW (comboTheme
, CB_ADDSTRING
, 0,
292 (LPARAM
)item
->fancyName
);
295 if (IsThemeActive () && SUCCEEDED (GetCurrentThemeName (currentTheme
,
296 sizeof(currentTheme
) / sizeof(WCHAR
),
297 currentColor
, sizeof(currentColor
) / sizeof(WCHAR
),
298 currentSize
, sizeof(currentSize
) / sizeof(WCHAR
))))
300 /* Determine the index of the currently active theme. */
302 for (i
= 0; i
< themeFilesCount
; i
++)
304 theme
= (ThemeFile
*)DSA_GetItemPtr (themeFiles
, i
);
305 if (lstrcmpiW (theme
->themeFileName
, currentTheme
) == 0)
314 /* Current theme not found?... add to the list, then... */
315 WINE_TRACE("Theme %s not in list of enumerated themes",
316 wine_dbgstr_w (currentTheme
));
317 myEnumThemeProc (NULL
, currentTheme
, currentTheme
,
318 currentTheme
, NULL
, NULL
);
319 themeIndex
= themeFilesCount
;
320 theme
= (ThemeFile
*)DSA_GetItemPtr (themeFiles
,
323 fill_color_size_combos (theme
, comboColor
, comboSize
);
324 select_color_and_size (theme
, currentColor
, comboColor
,
325 currentSize
, comboSize
);
329 /* No theme selected */
333 SendMessageW (comboTheme
, CB_SETCURSEL
, themeIndex
, 0);
337 /* Update the color & size combo boxes when the selection of the theme
338 * combo changed. Selects the current color and size scheme if the theme
339 * is currently active, otherwise the first color and size. */
340 static BOOL
update_color_and_size (int themeIndex
, HWND comboColor
,
349 WCHAR currentTheme
[MAX_PATH
];
350 WCHAR currentColor
[MAX_PATH
];
351 WCHAR currentSize
[MAX_PATH
];
353 (ThemeFile
*)DSA_GetItemPtr (themeFiles
, themeIndex
- 1);
355 fill_color_size_combos (theme
, comboColor
, comboSize
);
357 if ((SUCCEEDED (GetCurrentThemeName (currentTheme
,
358 sizeof(currentTheme
) / sizeof(WCHAR
),
359 currentColor
, sizeof(currentColor
) / sizeof(WCHAR
),
360 currentSize
, sizeof(currentSize
) / sizeof(WCHAR
))))
361 && (lstrcmpiW (currentTheme
, theme
->themeFileName
) == 0))
363 select_color_and_size (theme
, currentColor
, comboColor
,
364 currentSize
, comboSize
);
368 SendMessageW (comboColor
, CB_SETCURSEL
, 0, 0);
369 SendMessageW (comboSize
, CB_SETCURSEL
, 0, 0);
375 /* Apply a theme from a given theme, color and size combo box item index. */
376 static void do_apply_theme (int themeIndex
, int colorIndex
, int sizeIndex
)
378 static char b
[] = "\0";
383 ApplyTheme (NULL
, b
, NULL
);
388 (ThemeFile
*)DSA_GetItemPtr (themeFiles
, themeIndex
-1);
389 const WCHAR
* themeFileName
= theme
->themeFileName
;
390 const WCHAR
* colorName
= NULL
;
391 const WCHAR
* sizeName
= NULL
;
393 ThemeColorOrSize
* item
;
395 item
= color_or_size_dsa_get (&theme
->colors
, colorIndex
);
396 colorName
= item
->name
;
398 item
= color_or_size_dsa_get (&theme
->sizes
, sizeIndex
);
399 sizeName
= item
->name
;
401 if (SUCCEEDED (OpenThemeFile (themeFileName
, colorName
, sizeName
,
404 ApplyTheme (hTheme
, b
, NULL
);
405 CloseThemeFile (hTheme
);
409 ApplyTheme (NULL
, b
, NULL
);
417 static void enable_size_and_color_controls (HWND dialog
, BOOL enable
)
419 EnableWindow (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), enable
);
420 EnableWindow (GetDlgItem (dialog
, IDC_THEME_COLORTEXT
), enable
);
421 EnableWindow (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), enable
);
422 EnableWindow (GetDlgItem (dialog
, IDC_THEME_SIZETEXT
), enable
);
425 static void init_dialog (HWND dialog
)
430 if (!fill_theme_list (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
431 GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
432 GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
)))
434 SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
435 SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
436 enable_size_and_color_controls (dialog
, FALSE
);
440 enable_size_and_color_controls (dialog
, TRUE
);
447 static void on_theme_changed(HWND dialog
) {
448 int index
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
450 if (!update_color_and_size (index
, GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
451 GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
)))
453 SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
454 SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
455 enable_size_and_color_controls (dialog
, FALSE
);
459 enable_size_and_color_controls (dialog
, TRUE
);
464 static void apply_theme(HWND dialog
)
466 int themeIndex
, colorIndex
, sizeIndex
;
468 if (!theme_dirty
) return;
470 themeIndex
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
472 colorIndex
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
474 sizeIndex
= SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
),
477 do_apply_theme (themeIndex
, colorIndex
, sizeIndex
);
481 static void on_theme_install(HWND dialog
)
483 static const WCHAR filterMask
[] = {0,'*','.','m','s','s','t','y','l','e','s',0,0};
484 const int filterMaskLen
= sizeof(filterMask
)/sizeof(filterMask
[0]);
486 WCHAR filetitle
[MAX_PATH
];
487 WCHAR file
[MAX_PATH
];
491 LoadStringW (GetModuleHandle (NULL
), IDS_THEMEFILE
,
492 filter
, sizeof (filter
) / sizeof (filter
[0]) - filterMaskLen
);
493 memcpy (filter
+ lstrlenW (filter
), filterMask
,
494 filterMaskLen
* sizeof (WCHAR
));
495 LoadStringW (GetModuleHandle (NULL
), IDS_THEMEFILE_SELECT
,
496 title
, sizeof (title
) / sizeof (title
[0]));
498 ofn
.lStructSize
= sizeof(OPENFILENAMEW
);
501 ofn
.lpstrFilter
= filter
;
502 ofn
.lpstrCustomFilter
= NULL
;
503 ofn
.nMaxCustFilter
= 0;
504 ofn
.nFilterIndex
= 0;
505 ofn
.lpstrFile
= file
;
506 ofn
.lpstrFile
[0] = '\0';
507 ofn
.nMaxFile
= sizeof(file
)/sizeof(filetitle
[0]);
508 ofn
.lpstrFileTitle
= filetitle
;
509 ofn
.lpstrFileTitle
[0] = '\0';
510 ofn
.nMaxFileTitle
= sizeof(filetitle
)/sizeof(filetitle
[0]);
511 ofn
.lpstrInitialDir
= NULL
;
512 ofn
.lpstrTitle
= title
;
513 ofn
.Flags
= OFN_FILEMUSTEXIST
| OFN_PATHMUSTEXIST
| OFN_HIDEREADONLY
;
515 ofn
.nFileExtension
= 0;
516 ofn
.lpstrDefExt
= NULL
;
519 ofn
.lpTemplateName
= NULL
;
521 if (GetOpenFileNameW(&ofn
))
523 static const WCHAR themesSubdir
[] = { '\\','T','h','e','m','e','s',0 };
524 static const WCHAR backslash
[] = { '\\',0 };
525 WCHAR themeFilePath
[MAX_PATH
];
526 SHFILEOPSTRUCTW shfop
;
528 if (FAILED (SHGetFolderPathW (NULL
, CSIDL_RESOURCES
, NULL
,
529 SHGFP_TYPE_CURRENT
, themeFilePath
))) return;
531 PathRemoveExtensionW (filetitle
);
533 /* Construct path into which the theme file goes */
534 lstrcatW (themeFilePath
, themesSubdir
);
535 lstrcatW (themeFilePath
, backslash
);
536 lstrcatW (themeFilePath
, filetitle
);
538 /* Create the directory */
539 SHCreateDirectoryExW (dialog
, themeFilePath
, NULL
);
541 /* Append theme file name itself */
542 lstrcatW (themeFilePath
, backslash
);
543 lstrcatW (themeFilePath
, PathFindFileNameW (file
));
544 /* SHFileOperation() takes lists as input, so double-nullterminate */
545 themeFilePath
[lstrlenW (themeFilePath
)+1] = 0;
546 file
[lstrlenW (file
)+1] = 0;
549 WINE_TRACE("copying: %s -> %s\n", wine_dbgstr_w (file
),
550 wine_dbgstr_w (themeFilePath
));
552 shfop
.wFunc
= FO_COPY
;
554 shfop
.pTo
= themeFilePath
;
555 shfop
.fFlags
= FOF_NOCONFIRMMKDIR
;
556 if (SHFileOperationW (&shfop
) == 0)
559 if (!fill_theme_list (GetDlgItem (dialog
, IDC_THEME_THEMECOMBO
),
560 GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
),
561 GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
)))
563 SendMessageW (GetDlgItem (dialog
, IDC_THEME_COLORCOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
564 SendMessageW (GetDlgItem (dialog
, IDC_THEME_SIZECOMBO
), CB_SETCURSEL
, (WPARAM
)-1, 0);
565 enable_size_and_color_controls (dialog
, FALSE
);
569 enable_size_and_color_controls (dialog
, TRUE
);
573 WINE_TRACE("copy operation failed\n");
575 else WINE_TRACE("user cancelled\n");
579 ThemeDlgProc (HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
590 set_window_title(hDlg
);
594 switch(HIWORD(wParam
)) {
595 case CBN_SELCHANGE
: {
596 if (updating_ui
) break;
597 SendMessage(GetParent(hDlg
), PSM_CHANGED
, 0, 0);
598 switch (LOWORD(wParam
))
600 case IDC_THEME_THEMECOMBO
: on_theme_changed(hDlg
); break;
601 case IDC_THEME_COLORCOMBO
: /* fall through */
602 case IDC_THEME_SIZECOMBO
: theme_dirty
= TRUE
; break;
610 switch (LOWORD(wParam
))
612 case IDC_THEME_INSTALL
:
613 if (HIWORD(wParam
) != BN_CLICKED
) break;
614 on_theme_install (hDlg
);
624 switch (((LPNMHDR
)lParam
)->code
) {
625 case PSN_KILLACTIVE
: {
626 SetWindowLongPtr(hDlg
, DWLP_MSGRESULT
, FALSE
);
632 SetWindowLongPtr(hDlg
, DWLP_MSGRESULT
, PSNRET_NOERROR
);
635 case PSN_SETACTIVE
: {