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: {