Update for the 'Install theme' button.
[wine] / programs / winecfg / theme.c
1 /*
2  * Theme configuration code
3  *
4  * Copyright (c) 2005 by Frank Richter
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25
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 <shlwapi.h>
34 #include <wine/debug.h>
35
36 #include "resource.h"
37 #include "winecfg.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
40
41 /* UXTHEME functions not in the headers */
42
43 typedef struct tagTHEMENAMES
44 {
45     WCHAR szName[MAX_PATH+1];
46     WCHAR szDisplayName[MAX_PATH+1];
47     WCHAR szTooltip[MAX_PATH+1];
48 } THEMENAMES, *PTHEMENAMES;
49
50 typedef void* HTHEMEFILE;
51 typedef BOOL (CALLBACK *EnumThemeProc)(LPVOID lpReserved, 
52                                        LPCWSTR pszThemeFileName,
53                                        LPCWSTR pszThemeName, 
54                                        LPCWSTR pszToolTip, LPVOID lpReserved2,
55                                        LPVOID lpData);
56
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,
64                               DWORD unknown);
65 HRESULT WINAPI CloseThemeFile (HTHEMEFILE hThemeFile);
66 HRESULT WINAPI EnumThemes (LPCWSTR pszThemePath, EnumThemeProc callback,
67                            LPVOID lpData);
68
69 /* A struct to keep both the internal and "fancy" name of a color or size */
70 typedef struct
71 {
72   WCHAR* name;
73   WCHAR* fancyName;
74 } ThemeColorOrSize;
75
76 /* wrapper around DSA that also keeps an item count */
77 typedef struct
78 {
79   HDSA dsa;
80   int count;
81 } WrappedDsa;
82
83 /* Some helper functions to deal with ThemeColorOrSize structs in WrappedDSAs */
84
85 static void color_or_size_dsa_add (WrappedDsa* wdsa, const WCHAR* name,
86                                    const WCHAR* fancyName)
87 {
88     ThemeColorOrSize item;
89     
90     item.name = HeapAlloc (GetProcessHeap(), 0, 
91         (lstrlenW (name) + 1) * sizeof(WCHAR));
92     lstrcpyW (item.name, name);
93
94     item.fancyName = HeapAlloc (GetProcessHeap(), 0, 
95         (lstrlenW (fancyName) + 1) * sizeof(WCHAR));
96     lstrcpyW (item.fancyName, fancyName);
97
98     DSA_InsertItem (wdsa->dsa, wdsa->count, &item);
99     wdsa->count++;
100 }
101
102 static int CALLBACK dsa_destroy_callback (LPVOID p, LPVOID pData)
103 {
104     ThemeColorOrSize* item = (ThemeColorOrSize*)p;
105     HeapFree (GetProcessHeap(), 0, item->name);
106     HeapFree (GetProcessHeap(), 0, item->fancyName);
107     return 1;
108 }
109
110 static void free_color_or_size_dsa (WrappedDsa* wdsa)
111 {
112     DSA_DestroyCallback (wdsa->dsa, dsa_destroy_callback, NULL);
113 }
114
115 static void create_color_or_size_dsa (WrappedDsa* wdsa)
116 {
117     wdsa->dsa = DSA_Create (sizeof (ThemeColorOrSize), 1);
118     wdsa->count = 0;
119 }
120
121 static ThemeColorOrSize* color_or_size_dsa_get (WrappedDsa* wdsa, int index)
122 {
123     return (ThemeColorOrSize*)DSA_GetItemPtr (wdsa->dsa, index);
124 }
125
126 static int color_or_size_dsa_find (WrappedDsa* wdsa, const WCHAR* name)
127 {
128     int i = 0;
129     for (; i < wdsa->count; i++)
130     {
131         ThemeColorOrSize* item = color_or_size_dsa_get (wdsa, i);
132         if (lstrcmpiW (item->name, name) == 0) break;
133     }
134     return i;
135 }
136
137 /* A theme file, contains file name, display name, color and size scheme names */
138 typedef struct
139 {
140     WCHAR* themeFileName;
141     WCHAR* fancyName;
142     WrappedDsa colors;
143     WrappedDsa sizes;
144 } ThemeFile;
145
146 static HDSA themeFiles = NULL;
147 static int themeFilesCount = 0;
148
149 static int CALLBACK theme_dsa_destroy_callback (LPVOID p, LPVOID pData)
150 {
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);
156     return 1;
157 }
158
159 /* Free memory occupied by the theme list */
160 static void free_theme_files(void)
161 {
162     if (themeFiles == NULL) return;
163       
164     DSA_DestroyCallback (themeFiles , theme_dsa_destroy_callback, NULL);
165     themeFiles = NULL;
166     themeFilesCount = 0;
167 }
168
169 typedef HRESULT (WINAPI * EnumTheme) (LPWSTR, LPWSTR, DWORD, PTHEMENAMES);
170
171 /* fill a string list with either colors or sizes of a theme */
172 static void fill_theme_string_array (const WCHAR* filename, 
173                                      WrappedDsa* wdsa,
174                                      EnumTheme enumTheme)
175 {
176     DWORD index = 0;
177     THEMENAMES names;
178
179     WINE_TRACE ("%s %p %p\n", wine_dbgstr_w (filename), wdsa, enumTheme);
180
181     while (SUCCEEDED (enumTheme ((WCHAR*)filename, NULL, index++, &names)))
182     {
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);
186     }
187 }
188
189 /* Theme enumeration callback, adds theme to theme list */
190 static BOOL CALLBACK myEnumThemeProc (LPVOID lpReserved, 
191                                       LPCWSTR pszThemeFileName,
192                                       LPCWSTR pszThemeName, 
193                                       LPCWSTR pszToolTip, 
194                                       LPVOID lpReserved2, LPVOID lpData)
195 {
196     ThemeFile newEntry;
197
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);
203
204     newEntry.themeFileName = HeapAlloc (GetProcessHeap(), 0, 
205         (lstrlenW (pszThemeFileName) + 1) * sizeof(WCHAR));
206     lstrcpyW (newEntry.themeFileName, pszThemeFileName);
207   
208     newEntry.fancyName = HeapAlloc (GetProcessHeap(), 0, 
209         (lstrlenW (pszThemeName) + 1) * sizeof(WCHAR));
210     lstrcpyW (newEntry.fancyName, pszThemeName);
211   
212     /*list_add_tail (&themeFiles, &newEntry->entry);*/
213     DSA_InsertItem (themeFiles, themeFilesCount, &newEntry);
214     themeFilesCount++;
215
216     return TRUE;
217 }
218
219 /* Scan for themes */
220 static void scan_theme_files(void)
221 {
222     static const WCHAR themesSubdir[] = { '\\','T','h','e','m','e','s',0 };
223     WCHAR themesPath[MAX_PATH];
224
225     free_theme_files();
226
227     if (FAILED (SHGetFolderPathW (NULL, CSIDL_RESOURCES, NULL, 
228         SHGFP_TYPE_CURRENT, themesPath))) return;
229
230     themeFiles = DSA_Create (sizeof (ThemeFile), 1);
231     lstrcatW (themesPath, themesSubdir);
232
233     EnumThemes (themesPath, myEnumThemeProc, 0);
234 }
235
236 /* fill the color & size combo boxes for a given theme */
237 static void fill_color_size_combos (ThemeFile* theme, HWND comboColor, 
238                                     HWND comboSize)
239 {
240     int i;
241
242     SendMessageW (comboColor, CB_RESETCONTENT, 0, 0);
243     for (i = 0; i < theme->colors.count; i++)
244     {
245         ThemeColorOrSize* item = color_or_size_dsa_get (&theme->colors, i);
246         SendMessageW (comboColor, CB_ADDSTRING, 0, (LPARAM)item->fancyName);
247     }
248
249     SendMessageW (comboSize, CB_RESETCONTENT, 0, 0);
250     for (i = 0; i < theme->sizes.count; i++)
251     {
252         ThemeColorOrSize* item = color_or_size_dsa_get (&theme->sizes, i);
253         SendMessageW (comboSize, CB_ADDSTRING, 0, (LPARAM)item->fancyName);
254     }
255 }
256
257 /* Select the item of a combo box that matches a theme's color and size 
258  * scheme. */
259 static void select_color_and_size (ThemeFile* theme, 
260                             const WCHAR* colorName, HWND comboColor, 
261                             const WCHAR* sizeName, HWND comboSize)
262 {
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);
267 }
268
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)
272 {
273     WCHAR textNoTheme[256];
274     int themeIndex = 0;
275     BOOL ret = TRUE;
276     int i;
277     WCHAR currentTheme[MAX_PATH];
278     WCHAR currentColor[MAX_PATH];
279     WCHAR currentSize[MAX_PATH];
280     ThemeFile* theme = NULL;
281
282     LoadStringW (GetModuleHandle (NULL), IDS_NOTHEME, textNoTheme,
283         sizeof(textNoTheme) / sizeof(WCHAR));
284
285     SendMessageW (comboTheme, CB_RESETCONTENT, 0, 0);
286     SendMessageW (comboTheme, CB_ADDSTRING, 0, (LPARAM)textNoTheme);
287
288     for (i = 0; i < themeFilesCount; i++)
289     {
290         ThemeFile* item = (ThemeFile*)DSA_GetItemPtr (themeFiles, i);
291         SendMessageW (comboTheme, CB_ADDSTRING, 0, 
292             (LPARAM)item->fancyName);
293     }
294   
295     if (IsThemeActive () && SUCCEEDED (GetCurrentThemeName (currentTheme, 
296             sizeof(currentTheme) / sizeof(WCHAR),
297             currentColor, sizeof(currentColor) / sizeof(WCHAR),
298             currentSize, sizeof(currentSize) / sizeof(WCHAR))))
299     {
300         /* Determine the index of the currently active theme. */
301         BOOL found = FALSE;
302         for (i = 0; i < themeFilesCount; i++)
303         {
304             theme = (ThemeFile*)DSA_GetItemPtr (themeFiles, i);
305             if (lstrcmpiW (theme->themeFileName, currentTheme) == 0)
306             {
307                 found = TRUE;
308                 themeIndex = i+1;
309                 break;
310             }
311         }
312         if (!found)
313         {
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, 
321                 themeFilesCount-1);
322         }
323         fill_color_size_combos (theme, comboColor, comboSize);
324         select_color_and_size (theme, currentColor, comboColor,
325             currentSize, comboSize);
326     }
327     else
328     {
329         /* No theme selected */
330         ret = FALSE;
331     }
332
333     SendMessageW (comboTheme, CB_SETCURSEL, themeIndex, 0);
334     return ret;
335 }
336
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, 
341                                    HWND comboSize)
342 {
343     if (themeIndex == 0)
344     {
345         return FALSE;
346     }
347     else
348     {
349         WCHAR currentTheme[MAX_PATH];
350         WCHAR currentColor[MAX_PATH];
351         WCHAR currentSize[MAX_PATH];
352         ThemeFile* theme = 
353             (ThemeFile*)DSA_GetItemPtr (themeFiles, themeIndex - 1);
354     
355         fill_color_size_combos (theme, comboColor, comboSize);
356       
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))
362         {
363             select_color_and_size (theme, currentColor, comboColor,
364                 currentSize, comboSize);
365         }
366         else
367         {
368             SendMessageW (comboColor, CB_SETCURSEL, 0, 0);
369             SendMessageW (comboSize, CB_SETCURSEL, 0, 0);
370         }
371     }
372     return TRUE;
373 }
374
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)
377 {
378     static char b[] = "\0";
379
380     if (themeIndex == 0)
381     {
382         /* no theme */
383         ApplyTheme (NULL, b, NULL);
384     }
385     else
386     {
387         ThemeFile* theme = 
388             (ThemeFile*)DSA_GetItemPtr (themeFiles, themeIndex-1);
389         const WCHAR* themeFileName = theme->themeFileName;
390         const WCHAR* colorName = NULL;
391         const WCHAR* sizeName = NULL;
392         HTHEMEFILE hTheme;
393         ThemeColorOrSize* item;
394     
395         item = color_or_size_dsa_get (&theme->colors, colorIndex);
396         colorName = item->name;
397         
398         item = color_or_size_dsa_get (&theme->sizes, sizeIndex);
399         sizeName = item->name;
400         
401         if (SUCCEEDED (OpenThemeFile (themeFileName, colorName, sizeName,
402             &hTheme, 0)))
403         {
404             ApplyTheme (hTheme, b, NULL);
405             CloseThemeFile (hTheme);
406         }
407         else
408         {
409             ApplyTheme (NULL, b, NULL);
410         }
411     }
412 }
413
414 int updating_ui;
415 BOOL theme_dirty;
416
417 static void enable_size_and_color_controls (HWND dialog, BOOL enable)
418 {
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);
423 }
424   
425 static void init_dialog (HWND dialog)
426 {
427     updating_ui = TRUE;
428     
429     scan_theme_files();
430     if (!fill_theme_list (GetDlgItem (dialog, IDC_THEME_THEMECOMBO),
431         GetDlgItem (dialog, IDC_THEME_COLORCOMBO),
432         GetDlgItem (dialog, IDC_THEME_SIZECOMBO)))
433     {
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);
437     }
438     else
439     {
440         enable_size_and_color_controls (dialog, TRUE);
441     }
442     theme_dirty = FALSE;
443     
444     updating_ui = FALSE;
445 }
446
447 static void on_theme_changed(HWND dialog) {
448     int index = SendMessageW (GetDlgItem (dialog, IDC_THEME_THEMECOMBO),
449         CB_GETCURSEL, 0, 0);
450     if (!update_color_and_size (index, GetDlgItem (dialog, IDC_THEME_COLORCOMBO),
451         GetDlgItem (dialog, IDC_THEME_SIZECOMBO)))
452     {
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);
456     }
457     else
458     {
459         enable_size_and_color_controls (dialog, TRUE);
460     }
461     theme_dirty = TRUE;
462 }
463
464 static void apply_theme(HWND dialog)
465 {
466     int themeIndex, colorIndex, sizeIndex;
467
468     if (!theme_dirty) return;
469
470     themeIndex = SendMessageW (GetDlgItem (dialog, IDC_THEME_THEMECOMBO),
471         CB_GETCURSEL, 0, 0);
472     colorIndex = SendMessageW (GetDlgItem (dialog, IDC_THEME_COLORCOMBO),
473         CB_GETCURSEL, 0, 0);
474     sizeIndex = SendMessageW (GetDlgItem (dialog, IDC_THEME_SIZECOMBO),
475         CB_GETCURSEL, 0, 0);
476
477     do_apply_theme (themeIndex, colorIndex, sizeIndex);
478     theme_dirty = FALSE;
479 }
480
481 static void on_theme_install(HWND dialog)
482 {
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]);
485   OPENFILENAMEW ofn;
486   WCHAR filetitle[MAX_PATH];
487   WCHAR file[MAX_PATH];
488   WCHAR filter[100];
489   WCHAR title[100];
490
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]));
497
498   ofn.lStructSize = sizeof(OPENFILENAMEW);
499   ofn.hwndOwner = 0;
500   ofn.hInstance = 0;
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;
514   ofn.nFileOffset = 0;
515   ofn.nFileExtension = 0;
516   ofn.lpstrDefExt = NULL;
517   ofn.lCustData = 0;
518   ofn.lpfnHook = NULL;
519   ofn.lpTemplateName = NULL;
520
521   if (GetOpenFileNameW(&ofn))
522   {
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;
527
528       if (FAILED (SHGetFolderPathW (NULL, CSIDL_RESOURCES, NULL, 
529           SHGFP_TYPE_CURRENT, themeFilePath))) return;
530
531       PathRemoveExtensionW (filetitle);
532
533       /* Construct path into which the theme file goes */
534       lstrcatW (themeFilePath, themesSubdir);
535       lstrcatW (themeFilePath, backslash);
536       lstrcatW (themeFilePath, filetitle);
537
538       /* Create the directory */
539       SHCreateDirectoryExW (dialog, themeFilePath, NULL);
540
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;
547
548       /* Do the copying */
549       WINE_TRACE("copying: %s -> %s\n", wine_dbgstr_w (file), 
550           wine_dbgstr_w (themeFilePath));
551       shfop.hwnd = dialog;
552       shfop.wFunc = FO_COPY;
553       shfop.pFrom = file;
554       shfop.pTo = themeFilePath;
555       shfop.fFlags = FOF_NOCONFIRMMKDIR;
556       if (SHFileOperationW (&shfop) == 0)
557       {
558           scan_theme_files();
559           if (!fill_theme_list (GetDlgItem (dialog, IDC_THEME_THEMECOMBO),
560               GetDlgItem (dialog, IDC_THEME_COLORCOMBO),
561               GetDlgItem (dialog, IDC_THEME_SIZECOMBO)))
562           {
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);
566           }
567           else
568           {
569               enable_size_and_color_controls (dialog, TRUE);
570           }
571       }
572       else
573           WINE_TRACE("copy operation failed\n");
574   }
575   else WINE_TRACE("user cancelled\n");
576 }
577
578 INT_PTR CALLBACK
579 ThemeDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
580 {
581     switch (uMsg) {
582         case WM_INITDIALOG:
583             break;
584         
585         case WM_DESTROY:
586             free_theme_files();
587             break;
588
589         case WM_SHOWWINDOW:
590             set_window_title(hDlg);
591             break;
592             
593         case WM_COMMAND:
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))
599                     {
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;
603                     }
604                     break;
605                 }
606                     
607                 default:
608                     break;
609             }
610             switch (LOWORD(wParam))
611             {
612                 case IDC_THEME_INSTALL:
613                     if (HIWORD(wParam) != BN_CLICKED) break;
614                     on_theme_install (hDlg);
615                     break;
616                     
617                 default:
618                     break;
619             }
620             break;
621         
622         
623         case WM_NOTIFY:
624             switch (((LPNMHDR)lParam)->code) {
625                 case PSN_KILLACTIVE: {
626                     SetWindowLongPtr(hDlg, DWLP_MSGRESULT, FALSE);
627                     break;
628                 }
629                 case PSN_APPLY: {
630                     apply();
631                     apply_theme(hDlg);
632                     SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
633                     break;
634                 }
635                 case PSN_SETACTIVE: {
636                     init_dialog (hDlg);
637                     break;
638                 }
639             }
640             break;
641
642         default:
643             break;
644     }
645     return FALSE;
646 }