winecfg: Don't try to load the arts driver, the arts library is too buggy.
[wine] / programs / winecfg / theme.c
1 /*
2  * Desktop Integration
3  * - Theme configuration code
4  * - User Shell Folder mapping
5  *
6  * Copyright (c) 2005 by Frank Richter
7  * Copyright (c) 2006 by Michael Jung
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  *
23  */
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30
31 #define COBJMACROS
32
33 #include <windows.h>
34 #include <uxtheme.h>
35 #include <tmschema.h>
36 #include <shlobj.h>
37 #include <shlwapi.h>
38 #include <wine/debug.h>
39 #include <wine/unicode.h>
40
41 #include "resource.h"
42 #include "winecfg.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
45
46 /* UXTHEME functions not in the headers */
47
48 typedef struct tagTHEMENAMES
49 {
50     WCHAR szName[MAX_PATH+1];
51     WCHAR szDisplayName[MAX_PATH+1];
52     WCHAR szTooltip[MAX_PATH+1];
53 } THEMENAMES, *PTHEMENAMES;
54
55 typedef void* HTHEMEFILE;
56 typedef BOOL (CALLBACK *EnumThemeProc)(LPVOID lpReserved, 
57                                        LPCWSTR pszThemeFileName,
58                                        LPCWSTR pszThemeName, 
59                                        LPCWSTR pszToolTip, LPVOID lpReserved2,
60                                        LPVOID lpData);
61
62 HRESULT WINAPI EnumThemeColors (LPWSTR pszThemeFileName, LPWSTR pszSizeName,
63                                 DWORD dwColorNum, PTHEMENAMES pszColorNames);
64 HRESULT WINAPI EnumThemeSizes (LPWSTR pszThemeFileName, LPWSTR pszColorName,
65                                DWORD dwSizeNum, PTHEMENAMES pszSizeNames);
66 HRESULT WINAPI ApplyTheme (HTHEMEFILE hThemeFile, char* unknown, HWND hWnd);
67 HRESULT WINAPI OpenThemeFile (LPCWSTR pszThemeFileName, LPCWSTR pszColorName,
68                               LPCWSTR pszSizeName, HTHEMEFILE* hThemeFile,
69                               DWORD unknown);
70 HRESULT WINAPI CloseThemeFile (HTHEMEFILE hThemeFile);
71 HRESULT WINAPI EnumThemes (LPCWSTR pszThemePath, EnumThemeProc callback,
72                            LPVOID lpData);
73
74 /* A struct to keep both the internal and "fancy" name of a color or size */
75 typedef struct
76 {
77   WCHAR* name;
78   WCHAR* fancyName;
79 } ThemeColorOrSize;
80
81 /* wrapper around DSA that also keeps an item count */
82 typedef struct
83 {
84   HDSA dsa;
85   int count;
86 } WrappedDsa;
87
88 /* Some helper functions to deal with ThemeColorOrSize structs in WrappedDSAs */
89
90 static void color_or_size_dsa_add (WrappedDsa* wdsa, const WCHAR* name,
91                                    const WCHAR* fancyName)
92 {
93     ThemeColorOrSize item;
94     
95     item.name = HeapAlloc (GetProcessHeap(), 0, 
96         (lstrlenW (name) + 1) * sizeof(WCHAR));
97     lstrcpyW (item.name, name);
98
99     item.fancyName = HeapAlloc (GetProcessHeap(), 0, 
100         (lstrlenW (fancyName) + 1) * sizeof(WCHAR));
101     lstrcpyW (item.fancyName, fancyName);
102
103     DSA_InsertItem (wdsa->dsa, wdsa->count, &item);
104     wdsa->count++;
105 }
106
107 static int CALLBACK dsa_destroy_callback (LPVOID p, LPVOID pData)
108 {
109     ThemeColorOrSize* item = (ThemeColorOrSize*)p;
110     HeapFree (GetProcessHeap(), 0, item->name);
111     HeapFree (GetProcessHeap(), 0, item->fancyName);
112     return 1;
113 }
114
115 static void free_color_or_size_dsa (WrappedDsa* wdsa)
116 {
117     DSA_DestroyCallback (wdsa->dsa, dsa_destroy_callback, NULL);
118 }
119
120 static void create_color_or_size_dsa (WrappedDsa* wdsa)
121 {
122     wdsa->dsa = DSA_Create (sizeof (ThemeColorOrSize), 1);
123     wdsa->count = 0;
124 }
125
126 static ThemeColorOrSize* color_or_size_dsa_get (WrappedDsa* wdsa, int index)
127 {
128     return (ThemeColorOrSize*)DSA_GetItemPtr (wdsa->dsa, index);
129 }
130
131 static int color_or_size_dsa_find (WrappedDsa* wdsa, const WCHAR* name)
132 {
133     int i = 0;
134     for (; i < wdsa->count; i++)
135     {
136         ThemeColorOrSize* item = color_or_size_dsa_get (wdsa, i);
137         if (lstrcmpiW (item->name, name) == 0) break;
138     }
139     return i;
140 }
141
142 /* A theme file, contains file name, display name, color and size scheme names */
143 typedef struct
144 {
145     WCHAR* themeFileName;
146     WCHAR* fancyName;
147     WrappedDsa colors;
148     WrappedDsa sizes;
149 } ThemeFile;
150
151 static HDSA themeFiles = NULL;
152 static int themeFilesCount = 0;
153
154 static int CALLBACK theme_dsa_destroy_callback (LPVOID p, LPVOID pData)
155 {
156     ThemeFile* item = (ThemeFile*)p;
157     HeapFree (GetProcessHeap(), 0, item->themeFileName);
158     HeapFree (GetProcessHeap(), 0, item->fancyName);
159     free_color_or_size_dsa (&item->colors);
160     free_color_or_size_dsa (&item->sizes);
161     return 1;
162 }
163
164 /* Free memory occupied by the theme list */
165 static void free_theme_files(void)
166 {
167     if (themeFiles == NULL) return;
168       
169     DSA_DestroyCallback (themeFiles , theme_dsa_destroy_callback, NULL);
170     themeFiles = NULL;
171     themeFilesCount = 0;
172 }
173
174 typedef HRESULT (WINAPI * EnumTheme) (LPWSTR, LPWSTR, DWORD, PTHEMENAMES);
175
176 /* fill a string list with either colors or sizes of a theme */
177 static void fill_theme_string_array (const WCHAR* filename, 
178                                      WrappedDsa* wdsa,
179                                      EnumTheme enumTheme)
180 {
181     DWORD index = 0;
182     THEMENAMES names;
183
184     WINE_TRACE ("%s %p %p\n", wine_dbgstr_w (filename), wdsa, enumTheme);
185
186     while (SUCCEEDED (enumTheme ((WCHAR*)filename, NULL, index++, &names)))
187     {
188         WINE_TRACE ("%s: %s\n", wine_dbgstr_w (names.szName), 
189             wine_dbgstr_w (names.szDisplayName));
190         color_or_size_dsa_add (wdsa, names.szName, names.szDisplayName);
191     }
192 }
193
194 /* Theme enumeration callback, adds theme to theme list */
195 static BOOL CALLBACK myEnumThemeProc (LPVOID lpReserved, 
196                                       LPCWSTR pszThemeFileName,
197                                       LPCWSTR pszThemeName, 
198                                       LPCWSTR pszToolTip, 
199                                       LPVOID lpReserved2, LPVOID lpData)
200 {
201     ThemeFile newEntry;
202
203     /* fill size/color lists */
204     create_color_or_size_dsa (&newEntry.colors);
205     fill_theme_string_array (pszThemeFileName, &newEntry.colors, EnumThemeColors);
206     create_color_or_size_dsa (&newEntry.sizes);
207     fill_theme_string_array (pszThemeFileName, &newEntry.sizes, EnumThemeSizes);
208
209     newEntry.themeFileName = HeapAlloc (GetProcessHeap(), 0, 
210         (lstrlenW (pszThemeFileName) + 1) * sizeof(WCHAR));
211     lstrcpyW (newEntry.themeFileName, pszThemeFileName);
212   
213     newEntry.fancyName = HeapAlloc (GetProcessHeap(), 0, 
214         (lstrlenW (pszThemeName) + 1) * sizeof(WCHAR));
215     lstrcpyW (newEntry.fancyName, pszThemeName);
216   
217     /*list_add_tail (&themeFiles, &newEntry->entry);*/
218     DSA_InsertItem (themeFiles, themeFilesCount, &newEntry);
219     themeFilesCount++;
220
221     return TRUE;
222 }
223
224 /* Scan for themes */
225 static void scan_theme_files(void)
226 {
227     static const WCHAR themesSubdir[] = { '\\','T','h','e','m','e','s',0 };
228     WCHAR themesPath[MAX_PATH];
229
230     free_theme_files();
231
232     if (FAILED (SHGetFolderPathW (NULL, CSIDL_RESOURCES, NULL, 
233         SHGFP_TYPE_CURRENT, themesPath))) return;
234
235     themeFiles = DSA_Create (sizeof (ThemeFile), 1);
236     lstrcatW (themesPath, themesSubdir);
237
238     EnumThemes (themesPath, myEnumThemeProc, 0);
239 }
240
241 /* fill the color & size combo boxes for a given theme */
242 static void fill_color_size_combos (ThemeFile* theme, HWND comboColor, 
243                                     HWND comboSize)
244 {
245     int i;
246
247     SendMessageW (comboColor, CB_RESETCONTENT, 0, 0);
248     for (i = 0; i < theme->colors.count; i++)
249     {
250         ThemeColorOrSize* item = color_or_size_dsa_get (&theme->colors, i);
251         SendMessageW (comboColor, CB_ADDSTRING, 0, (LPARAM)item->fancyName);
252     }
253
254     SendMessageW (comboSize, CB_RESETCONTENT, 0, 0);
255     for (i = 0; i < theme->sizes.count; i++)
256     {
257         ThemeColorOrSize* item = color_or_size_dsa_get (&theme->sizes, i);
258         SendMessageW (comboSize, CB_ADDSTRING, 0, (LPARAM)item->fancyName);
259     }
260 }
261
262 /* Select the item of a combo box that matches a theme's color and size 
263  * scheme. */
264 static void select_color_and_size (ThemeFile* theme, 
265                             const WCHAR* colorName, HWND comboColor, 
266                             const WCHAR* sizeName, HWND comboSize)
267 {
268     SendMessageW (comboColor, CB_SETCURSEL, 
269         color_or_size_dsa_find (&theme->colors, colorName), 0);
270     SendMessageW (comboSize, CB_SETCURSEL, 
271         color_or_size_dsa_find (&theme->sizes, sizeName), 0);
272 }
273
274 /* Fill theme, color and sizes combo boxes with the know themes and select
275  * the entries matching the currently active theme. */
276 static BOOL fill_theme_list (HWND comboTheme, HWND comboColor, HWND comboSize)
277 {
278     WCHAR textNoTheme[256];
279     int themeIndex = 0;
280     BOOL ret = TRUE;
281     int i;
282     WCHAR currentTheme[MAX_PATH];
283     WCHAR currentColor[MAX_PATH];
284     WCHAR currentSize[MAX_PATH];
285     ThemeFile* theme = NULL;
286
287     LoadStringW (GetModuleHandle (NULL), IDS_NOTHEME, textNoTheme,
288         sizeof(textNoTheme) / sizeof(WCHAR));
289
290     SendMessageW (comboTheme, CB_RESETCONTENT, 0, 0);
291     SendMessageW (comboTheme, CB_ADDSTRING, 0, (LPARAM)textNoTheme);
292
293     for (i = 0; i < themeFilesCount; i++)
294     {
295         ThemeFile* item = (ThemeFile*)DSA_GetItemPtr (themeFiles, i);
296         SendMessageW (comboTheme, CB_ADDSTRING, 0, 
297             (LPARAM)item->fancyName);
298     }
299   
300     if (IsThemeActive () && SUCCEEDED (GetCurrentThemeName (currentTheme, 
301             sizeof(currentTheme) / sizeof(WCHAR),
302             currentColor, sizeof(currentColor) / sizeof(WCHAR),
303             currentSize, sizeof(currentSize) / sizeof(WCHAR))))
304     {
305         /* Determine the index of the currently active theme. */
306         BOOL found = FALSE;
307         for (i = 0; i < themeFilesCount; i++)
308         {
309             theme = (ThemeFile*)DSA_GetItemPtr (themeFiles, i);
310             if (lstrcmpiW (theme->themeFileName, currentTheme) == 0)
311             {
312                 found = TRUE;
313                 themeIndex = i+1;
314                 break;
315             }
316         }
317         if (!found)
318         {
319             /* Current theme not found?... add to the list, then... */
320             WINE_TRACE("Theme %s not in list of enumerated themes\n",
321                 wine_dbgstr_w (currentTheme));
322             myEnumThemeProc (NULL, currentTheme, currentTheme, 
323                 currentTheme, NULL, NULL);
324             themeIndex = themeFilesCount;
325             theme = (ThemeFile*)DSA_GetItemPtr (themeFiles, 
326                 themeFilesCount-1);
327         }
328         fill_color_size_combos (theme, comboColor, comboSize);
329         select_color_and_size (theme, currentColor, comboColor,
330             currentSize, comboSize);
331     }
332     else
333     {
334         /* No theme selected */
335         ret = FALSE;
336     }
337
338     SendMessageW (comboTheme, CB_SETCURSEL, themeIndex, 0);
339     return ret;
340 }
341
342 /* Update the color & size combo boxes when the selection of the theme
343  * combo changed. Selects the current color and size scheme if the theme
344  * is currently active, otherwise the first color and size. */
345 static BOOL update_color_and_size (int themeIndex, HWND comboColor, 
346                                    HWND comboSize)
347 {
348     if (themeIndex == 0)
349     {
350         return FALSE;
351     }
352     else
353     {
354         WCHAR currentTheme[MAX_PATH];
355         WCHAR currentColor[MAX_PATH];
356         WCHAR currentSize[MAX_PATH];
357         ThemeFile* theme = 
358             (ThemeFile*)DSA_GetItemPtr (themeFiles, themeIndex - 1);
359     
360         fill_color_size_combos (theme, comboColor, comboSize);
361       
362         if ((SUCCEEDED (GetCurrentThemeName (currentTheme, 
363             sizeof(currentTheme) / sizeof(WCHAR),
364             currentColor, sizeof(currentColor) / sizeof(WCHAR),
365             currentSize, sizeof(currentSize) / sizeof(WCHAR))))
366             && (lstrcmpiW (currentTheme, theme->themeFileName) == 0))
367         {
368             select_color_and_size (theme, currentColor, comboColor,
369                 currentSize, comboSize);
370         }
371         else
372         {
373             SendMessageW (comboColor, CB_SETCURSEL, 0, 0);
374             SendMessageW (comboSize, CB_SETCURSEL, 0, 0);
375         }
376     }
377     return TRUE;
378 }
379
380 /* Apply a theme from a given theme, color and size combo box item index. */
381 static void do_apply_theme (int themeIndex, int colorIndex, int sizeIndex)
382 {
383     static char b[] = "\0";
384
385     if (themeIndex == 0)
386     {
387         /* no theme */
388         ApplyTheme (NULL, b, NULL);
389     }
390     else
391     {
392         ThemeFile* theme = 
393             (ThemeFile*)DSA_GetItemPtr (themeFiles, themeIndex-1);
394         const WCHAR* themeFileName = theme->themeFileName;
395         const WCHAR* colorName = NULL;
396         const WCHAR* sizeName = NULL;
397         HTHEMEFILE hTheme;
398         ThemeColorOrSize* item;
399     
400         item = color_or_size_dsa_get (&theme->colors, colorIndex);
401         colorName = item->name;
402         
403         item = color_or_size_dsa_get (&theme->sizes, sizeIndex);
404         sizeName = item->name;
405         
406         if (SUCCEEDED (OpenThemeFile (themeFileName, colorName, sizeName,
407             &hTheme, 0)))
408         {
409             ApplyTheme (hTheme, b, NULL);
410             CloseThemeFile (hTheme);
411         }
412         else
413         {
414             ApplyTheme (NULL, b, NULL);
415         }
416     }
417 }
418
419 int updating_ui;
420 BOOL theme_dirty;
421
422 static void enable_size_and_color_controls (HWND dialog, BOOL enable)
423 {
424     EnableWindow (GetDlgItem (dialog, IDC_THEME_COLORCOMBO), enable);
425     EnableWindow (GetDlgItem (dialog, IDC_THEME_COLORTEXT), enable);
426     EnableWindow (GetDlgItem (dialog, IDC_THEME_SIZECOMBO), enable);
427     EnableWindow (GetDlgItem (dialog, IDC_THEME_SIZETEXT), enable);
428 }
429   
430 static void init_dialog (HWND dialog)
431 {
432     updating_ui = TRUE;
433     
434     scan_theme_files();
435     if (!fill_theme_list (GetDlgItem (dialog, IDC_THEME_THEMECOMBO),
436         GetDlgItem (dialog, IDC_THEME_COLORCOMBO),
437         GetDlgItem (dialog, IDC_THEME_SIZECOMBO)))
438     {
439         SendMessageW (GetDlgItem (dialog, IDC_THEME_COLORCOMBO), CB_SETCURSEL, (WPARAM)-1, 0);
440         SendMessageW (GetDlgItem (dialog, IDC_THEME_SIZECOMBO), CB_SETCURSEL, (WPARAM)-1, 0);
441         enable_size_and_color_controls (dialog, FALSE);
442     }
443     else
444     {
445         enable_size_and_color_controls (dialog, TRUE);
446     }
447     theme_dirty = FALSE;
448
449     SendDlgItemMessageW(dialog, IDC_SYSPARAM_SIZE_UD, UDM_SETBUDDY, (WPARAM)GetDlgItem(dialog, IDC_SYSPARAM_SIZE), 0);
450     SendDlgItemMessageW(dialog, IDC_SYSPARAM_SIZE_UD, UDM_SETRANGE, 0, MAKELONG(100, 8));
451
452     updating_ui = FALSE;
453 }
454
455 static void on_theme_changed(HWND dialog) {
456     int index = SendMessageW (GetDlgItem (dialog, IDC_THEME_THEMECOMBO),
457         CB_GETCURSEL, 0, 0);
458     if (!update_color_and_size (index, GetDlgItem (dialog, IDC_THEME_COLORCOMBO),
459         GetDlgItem (dialog, IDC_THEME_SIZECOMBO)))
460     {
461         SendMessageW (GetDlgItem (dialog, IDC_THEME_COLORCOMBO), CB_SETCURSEL, (WPARAM)-1, 0);
462         SendMessageW (GetDlgItem (dialog, IDC_THEME_SIZECOMBO), CB_SETCURSEL, (WPARAM)-1, 0);
463         enable_size_and_color_controls (dialog, FALSE);
464     }
465     else
466     {
467         enable_size_and_color_controls (dialog, TRUE);
468     }
469     theme_dirty = TRUE;
470 }
471
472 static void apply_theme(HWND dialog)
473 {
474     int themeIndex, colorIndex, sizeIndex;
475
476     if (!theme_dirty) return;
477
478     themeIndex = SendMessageW (GetDlgItem (dialog, IDC_THEME_THEMECOMBO),
479         CB_GETCURSEL, 0, 0);
480     colorIndex = SendMessageW (GetDlgItem (dialog, IDC_THEME_COLORCOMBO),
481         CB_GETCURSEL, 0, 0);
482     sizeIndex = SendMessageW (GetDlgItem (dialog, IDC_THEME_SIZECOMBO),
483         CB_GETCURSEL, 0, 0);
484
485     do_apply_theme (themeIndex, colorIndex, sizeIndex);
486     theme_dirty = FALSE;
487 }
488
489 static void on_theme_install(HWND dialog)
490 {
491   static const WCHAR filterMask[] = {0,'*','.','m','s','s','t','y','l','e','s',0,0};
492   const int filterMaskLen = sizeof(filterMask)/sizeof(filterMask[0]);
493   OPENFILENAMEW ofn;
494   WCHAR filetitle[MAX_PATH];
495   WCHAR file[MAX_PATH];
496   WCHAR filter[100];
497   WCHAR title[100];
498
499   LoadStringW (GetModuleHandle (NULL), IDS_THEMEFILE, 
500       filter, sizeof (filter) / sizeof (filter[0]) - filterMaskLen);
501   memcpy (filter + lstrlenW (filter), filterMask, 
502       filterMaskLen * sizeof (WCHAR));
503   LoadStringW (GetModuleHandle (NULL), IDS_THEMEFILE_SELECT, 
504       title, sizeof (title) / sizeof (title[0]));
505
506   ofn.lStructSize = sizeof(OPENFILENAMEW);
507   ofn.hwndOwner = 0;
508   ofn.hInstance = 0;
509   ofn.lpstrFilter = filter;
510   ofn.lpstrCustomFilter = NULL;
511   ofn.nMaxCustFilter = 0;
512   ofn.nFilterIndex = 0;
513   ofn.lpstrFile = file;
514   ofn.lpstrFile[0] = '\0';
515   ofn.nMaxFile = sizeof(file)/sizeof(filetitle[0]);
516   ofn.lpstrFileTitle = filetitle;
517   ofn.lpstrFileTitle[0] = '\0';
518   ofn.nMaxFileTitle = sizeof(filetitle)/sizeof(filetitle[0]);
519   ofn.lpstrInitialDir = NULL;
520   ofn.lpstrTitle = title;
521   ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
522   ofn.nFileOffset = 0;
523   ofn.nFileExtension = 0;
524   ofn.lpstrDefExt = NULL;
525   ofn.lCustData = 0;
526   ofn.lpfnHook = NULL;
527   ofn.lpTemplateName = NULL;
528
529   if (GetOpenFileNameW(&ofn))
530   {
531       static const WCHAR themesSubdir[] = { '\\','T','h','e','m','e','s',0 };
532       static const WCHAR backslash[] = { '\\',0 };
533       WCHAR themeFilePath[MAX_PATH];
534       SHFILEOPSTRUCTW shfop;
535
536       if (FAILED (SHGetFolderPathW (NULL, CSIDL_RESOURCES|CSIDL_FLAG_CREATE, NULL, 
537           SHGFP_TYPE_CURRENT, themeFilePath))) return;
538
539       PathRemoveExtensionW (filetitle);
540
541       /* Construct path into which the theme file goes */
542       lstrcatW (themeFilePath, themesSubdir);
543       lstrcatW (themeFilePath, backslash);
544       lstrcatW (themeFilePath, filetitle);
545
546       /* Create the directory */
547       SHCreateDirectoryExW (dialog, themeFilePath, NULL);
548
549       /* Append theme file name itself */
550       lstrcatW (themeFilePath, backslash);
551       lstrcatW (themeFilePath, PathFindFileNameW (file));
552       /* SHFileOperation() takes lists as input, so double-nullterminate */
553       themeFilePath[lstrlenW (themeFilePath)+1] = 0;
554       file[lstrlenW (file)+1] = 0;
555
556       /* Do the copying */
557       WINE_TRACE("copying: %s -> %s\n", wine_dbgstr_w (file), 
558           wine_dbgstr_w (themeFilePath));
559       shfop.hwnd = dialog;
560       shfop.wFunc = FO_COPY;
561       shfop.pFrom = file;
562       shfop.pTo = themeFilePath;
563       shfop.fFlags = FOF_NOCONFIRMMKDIR;
564       if (SHFileOperationW (&shfop) == 0)
565       {
566           scan_theme_files();
567           if (!fill_theme_list (GetDlgItem (dialog, IDC_THEME_THEMECOMBO),
568               GetDlgItem (dialog, IDC_THEME_COLORCOMBO),
569               GetDlgItem (dialog, IDC_THEME_SIZECOMBO)))
570           {
571               SendMessageW (GetDlgItem (dialog, IDC_THEME_COLORCOMBO), CB_SETCURSEL, (WPARAM)-1, 0);
572               SendMessageW (GetDlgItem (dialog, IDC_THEME_SIZECOMBO), CB_SETCURSEL, (WPARAM)-1, 0);
573               enable_size_and_color_controls (dialog, FALSE);
574           }
575           else
576           {
577               enable_size_and_color_controls (dialog, TRUE);
578           }
579       }
580       else
581           WINE_TRACE("copy operation failed\n");
582   }
583   else WINE_TRACE("user cancelled\n");
584 }
585
586 /* Information about symbolic link targets of certain User Shell Folders. */
587 struct ShellFolderInfo {
588     int nFolder;
589     char szLinkTarget[FILENAME_MAX];
590 };
591
592 static struct ShellFolderInfo asfiInfo[] = {
593     { CSIDL_DESKTOP,  "" },
594     { CSIDL_PERSONAL, "" },
595     { CSIDL_MYPICTURES, "" },
596     { CSIDL_MYMUSIC, "" },
597     { CSIDL_MYVIDEO, "" }
598 };
599
600 static struct ShellFolderInfo *psfiSelected = NULL;
601
602 #define NUM_ELEMS(x) (sizeof(x)/sizeof(*(x)))
603
604 static void init_shell_folder_listview_headers(HWND dialog) {
605     LVCOLUMN listColumn;
606     RECT viewRect;
607     char szShellFolder[64] = "Shell Folder";
608     char szLinksTo[64] = "Links to";
609     int width;
610
611     LoadString(GetModuleHandle(NULL), IDS_SHELL_FOLDER, szShellFolder, sizeof(szShellFolder));
612     LoadString(GetModuleHandle(NULL), IDS_LINKS_TO, szLinksTo, sizeof(szLinksTo));
613     
614     GetClientRect(GetDlgItem(dialog, IDC_LIST_SFPATHS), &viewRect);
615     width = (viewRect.right - viewRect.left) / 4;
616
617     listColumn.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
618     listColumn.pszText = szShellFolder;
619     listColumn.cchTextMax = lstrlen(listColumn.pszText);
620     listColumn.cx = width;
621
622     SendDlgItemMessage(dialog, IDC_LIST_SFPATHS, LVM_INSERTCOLUMN, 0, (LPARAM) &listColumn);
623
624     listColumn.pszText = szLinksTo;
625     listColumn.cchTextMax = lstrlen(listColumn.pszText);
626     listColumn.cx = viewRect.right - viewRect.left - width - 1;
627
628     SendDlgItemMessage(dialog, IDC_LIST_SFPATHS, LVM_INSERTCOLUMN, 1, (LPARAM) &listColumn);
629 }
630
631 /* Reads the currently set shell folder symbol link targets into asfiInfo. */
632 static void read_shell_folder_link_targets() {
633     WCHAR wszPath[MAX_PATH];
634     HRESULT hr;
635     int i;
636    
637     for (i=0; i<NUM_ELEMS(asfiInfo); i++) {
638         asfiInfo[i].szLinkTarget[0] = '\0';
639         hr = SHGetFolderPathW(NULL, asfiInfo[i].nFolder|CSIDL_FLAG_DONT_VERIFY, NULL, 
640                               SHGFP_TYPE_CURRENT, wszPath);
641         if (SUCCEEDED(hr)) {
642             char *pszUnixPath = wine_get_unix_file_name(wszPath);
643             if (pszUnixPath) {
644                 struct stat statPath;
645                 if (!lstat(pszUnixPath, &statPath) && S_ISLNK(statPath.st_mode)) {
646                     int cLen = readlink(pszUnixPath, asfiInfo[i].szLinkTarget, FILENAME_MAX-1);
647                     if (cLen >= 0) asfiInfo[i].szLinkTarget[cLen] = '\0';
648                 }
649                 HeapFree(GetProcessHeap(), 0, pszUnixPath);
650             }
651         } 
652     }    
653 }
654
655 static void update_shell_folder_listview(HWND dialog) {
656     int i;
657     LVITEM item;
658     LONG lSelected = SendDlgItemMessage(dialog, IDC_LIST_SFPATHS, LVM_GETNEXTITEM, (WPARAM)-1, 
659                                         MAKELPARAM(LVNI_SELECTED,0));
660     
661     SendDlgItemMessage(dialog, IDC_LIST_SFPATHS, LVM_DELETEALLITEMS, 0, 0);
662
663     for (i=0; i<NUM_ELEMS(asfiInfo); i++) {
664         char buffer[MAX_PATH];
665         HRESULT hr;
666         LPITEMIDLIST pidlCurrent;
667
668         /* Some acrobatic to get the localized name of the shell folder */
669         hr = SHGetFolderLocation(dialog, asfiInfo[i].nFolder, NULL, 0, &pidlCurrent);
670         if (SUCCEEDED(hr)) { 
671             LPSHELLFOLDER psfParent;
672             LPCITEMIDLIST pidlLast;
673             hr = SHBindToParent(pidlCurrent, &IID_IShellFolder, (LPVOID*)&psfParent, &pidlLast);
674             if (SUCCEEDED(hr)) {
675                 STRRET strRet;
676                 hr = IShellFolder_GetDisplayNameOf(psfParent, pidlLast, SHGDN_FORADDRESSBAR, &strRet);
677                 if (SUCCEEDED(hr)) {
678                     hr = StrRetToBufA(&strRet, pidlLast, buffer, 256);
679                 }
680                 IShellFolder_Release(psfParent);
681             }
682             ILFree(pidlCurrent);
683         }
684
685         /* If there's a dangling symlink for the current shell folder, SHGetFolderLocation
686          * will fail above. We fall back to the (non-verified) path of the shell folder. */
687         if (FAILED(hr)) {
688             hr = SHGetFolderPath(dialog, asfiInfo[i].nFolder|CSIDL_FLAG_DONT_VERIFY, NULL, 
689                                  SHGFP_TYPE_CURRENT, buffer);
690         }
691     
692         item.mask = LVIF_TEXT | LVIF_PARAM;
693         item.iItem = i;
694         item.iSubItem = 0;
695         item.pszText = buffer;
696         item.lParam = (LPARAM)&asfiInfo[i];
697         SendDlgItemMessage(dialog, IDC_LIST_SFPATHS, LVM_INSERTITEM, 0, (LPARAM)&item);
698
699         item.mask = LVIF_TEXT;
700         item.iItem = i;
701         item.iSubItem = 1;
702         item.pszText = asfiInfo[i].szLinkTarget;
703         SendDlgItemMessage(dialog, IDC_LIST_SFPATHS, LVM_SETITEM, 0, (LPARAM)&item);
704     }
705
706     /* Ensure that the previously selected item is selected again. */
707     if (lSelected >= 0) {
708         item.mask = LVIF_STATE;
709         item.state = LVIS_SELECTED;
710         item.stateMask = LVIS_SELECTED;
711         SendDlgItemMessage(dialog, IDC_LIST_SFPATHS, LVM_SETITEMSTATE, (WPARAM)lSelected, 
712                            (LPARAM)&item);
713     }
714 }
715
716 static void on_shell_folder_selection_changed(HWND hDlg, LPNMLISTVIEW lpnm) {
717     if (lpnm->uNewState & LVIS_SELECTED) {
718         psfiSelected = (struct ShellFolderInfo *)lpnm->lParam;
719         EnableWindow(GetDlgItem(hDlg, IDC_LINK_SFPATH), 1);
720         if (strlen(psfiSelected->szLinkTarget)) {
721             CheckDlgButton(hDlg, IDC_LINK_SFPATH, BST_CHECKED);
722             EnableWindow(GetDlgItem(hDlg, IDC_EDIT_SFPATH), 1);
723             EnableWindow(GetDlgItem(hDlg, IDC_BROWSE_SFPATH), 1);
724             SetWindowText(GetDlgItem(hDlg, IDC_EDIT_SFPATH), psfiSelected->szLinkTarget);
725         } else {
726             CheckDlgButton(hDlg, IDC_LINK_SFPATH, BST_UNCHECKED);
727             EnableWindow(GetDlgItem(hDlg, IDC_EDIT_SFPATH), 0);
728             EnableWindow(GetDlgItem(hDlg, IDC_BROWSE_SFPATH), 0);
729             SetWindowText(GetDlgItem(hDlg, IDC_EDIT_SFPATH), "");
730         }
731     } else {
732         psfiSelected = NULL;
733         CheckDlgButton(hDlg, IDC_LINK_SFPATH, BST_UNCHECKED);
734         SetWindowText(GetDlgItem(hDlg, IDC_EDIT_SFPATH), "");
735         EnableWindow(GetDlgItem(hDlg, IDC_LINK_SFPATH), 0);
736         EnableWindow(GetDlgItem(hDlg, IDC_EDIT_SFPATH), 0);
737         EnableWindow(GetDlgItem(hDlg, IDC_BROWSE_SFPATH), 0);
738     }
739 }
740
741 /* Keep the contents of the edit control, the listview control and the symlink 
742  * information in sync. */
743 static void on_shell_folder_edit_changed(HWND hDlg) {
744     LVITEM item;
745     char *text = get_text(hDlg, IDC_EDIT_SFPATH);
746     LONG iSel = SendDlgItemMessage(hDlg, IDC_LIST_SFPATHS, LVM_GETNEXTITEM, -1,
747                                    MAKELPARAM(LVNI_SELECTED,0));
748     
749     if (!text || !psfiSelected || iSel < 0) {
750         HeapFree(GetProcessHeap(), 0, text);
751         return;
752     }
753
754     strncpy(psfiSelected->szLinkTarget, text, FILENAME_MAX);
755     HeapFree(GetProcessHeap(), 0, text);
756
757     item.mask = LVIF_TEXT;
758     item.iItem = iSel;
759     item.iSubItem = 1;
760     item.pszText = psfiSelected->szLinkTarget;
761     SendDlgItemMessage(hDlg, IDC_LIST_SFPATHS, LVM_SETITEM, 0, (LPARAM)&item);
762
763     SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
764 }
765
766 static void apply_shell_folder_changes() {
767     WCHAR wszPath[MAX_PATH];
768     char szBackupPath[FILENAME_MAX], szUnixPath[FILENAME_MAX], *pszUnixPath = NULL;
769     int i, cUnixPathLen;
770     struct stat statPath;
771     HRESULT hr;
772
773     for (i=0; i<NUM_ELEMS(asfiInfo); i++) {
774         /* Ignore nonexistent link targets */
775         if (asfiInfo[i].szLinkTarget[0] && stat(asfiInfo[i].szLinkTarget, &statPath))
776             continue;
777         
778         hr = SHGetFolderPathW(NULL, asfiInfo[i].nFolder|CSIDL_FLAG_CREATE, NULL, 
779                               SHGFP_TYPE_CURRENT, wszPath);
780         if (FAILED(hr)) continue;
781
782         /* Retrieve the corresponding unix path. */
783         pszUnixPath = wine_get_unix_file_name(wszPath);
784         if (!pszUnixPath) continue;
785         lstrcpyA(szUnixPath, pszUnixPath);
786         HeapFree(GetProcessHeap(), 0, pszUnixPath);
787             
788         /* Derive name for folder backup. */
789         cUnixPathLen = lstrlenA(szUnixPath);    
790         lstrcpyA(szBackupPath, szUnixPath);
791         lstrcatA(szBackupPath, ".winecfg");
792         
793         if (lstat(szUnixPath, &statPath)) continue;
794     
795         /* Move old folder/link out of the way. */
796         if (S_ISLNK(statPath.st_mode)) {
797             if (unlink(szUnixPath)) continue; /* Unable to remove link. */
798         } else { 
799             if (!*asfiInfo[i].szLinkTarget) {
800                 continue; /* We are done. Old was real folder, as new shall be. */
801             } else { 
802                 if (rename(szUnixPath, szBackupPath)) { /* Move folder out of the way. */
803                     continue; /* Unable to move old folder. */
804                 }
805             }
806         }
807     
808         /* Create new link/folder. */
809         if (*asfiInfo[i].szLinkTarget) {
810             symlink(asfiInfo[i].szLinkTarget, szUnixPath);
811         } else {
812             /* If there's a backup folder, restore it. Else create new folder. */
813             if (!lstat(szBackupPath, &statPath) && S_ISDIR(statPath.st_mode)) {
814                 rename(szBackupPath, szUnixPath);
815             } else {
816                 mkdir(szUnixPath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
817             }
818         }
819     }
820 }
821
822 static struct
823 {
824     int sm_idx, color_idx;
825     const char *color_reg;
826     int size;
827     COLORREF color;
828 } metrics[] =
829 {
830     {-1,                COLOR_BTNFACE,          "ButtonFace"    }, /* IDC_SYSPARAMS_BUTTON */
831     {-1,                COLOR_BTNTEXT,          "ButtonText"    }, /* IDC_SYSPARAMS_BUTTON_TEXT */
832     {-1,                COLOR_BACKGROUND,       "Background"    }, /* IDC_SYSPARAMS_DESKTOP */
833     {SM_CXMENUSIZE,     COLOR_MENU,             "Menu"          }, /* IDC_SYSPARAMS_MENU */
834     {-1,                COLOR_MENUTEXT,         "MenuText"      }, /* IDC_SYSPARAMS_MENU_TEXT */
835     {SM_CXVSCROLL,      COLOR_SCROLLBAR,        "Scrollbar"     }, /* IDC_SYSPARAMS_SCROLLBAR */
836     {-1,                COLOR_HIGHLIGHT,        "Hilight"       }, /* IDC_SYSPARAMS_SELECTION */
837     {-1,                COLOR_HIGHLIGHTTEXT,    "HilightText"   }, /* IDC_SYSPARAMS_SELECTION_TEXT */
838     {-1,                COLOR_INFOBK,           "InfoWindow"    }, /* IDC_SYSPARAMS_TOOLTIP */
839     {-1,                COLOR_INFOTEXT,         "InfoText"      }, /* IDC_SYSPARAMS_TOOLTIP_TEXT */
840     {-1,                COLOR_WINDOW,           "Window"        }, /* IDC_SYSPARAMS_WINDOW */
841     {-1,                COLOR_WINDOWTEXT,       "WindowText"    }, /* IDC_SYSPARAMS_WINDOW_TEXT */
842     {SM_CXSIZE,         COLOR_ACTIVECAPTION,    "ActiveTitle"   }, /* IDC_SYSPARAMS_ACTIVE_TITLE */
843     {-1,                COLOR_CAPTIONTEXT,      "TitleText"     }, /* IDC_SYSPARAMS_ACTIVE_TITLE_TEXT */
844     {-1,                COLOR_INACTIVECAPTION,  "InactiveTitle" }, /* IDC_SYSPARAMS_INACTIVE_TITLE */
845     {-1,                COLOR_INACTIVECAPTIONTEXT,"InactiveTitleText" }  /* IDC_SYSPARAMS_INACTIVE_TITLE_TEXT */
846 };
847
848 static void save_sys_color(int idx, COLORREF clr)
849 {
850     char buffer[13];
851
852     sprintf(buffer, "%d %d %d",  GetRValue (clr), GetGValue (clr), GetBValue (clr));
853     set_reg_key(HKEY_CURRENT_USER, "Control Panel\\Colors", metrics[idx].color_reg, buffer);
854 }
855
856 static void read_sysparams(HWND hDlg)
857 {
858     WCHAR buffer[256];
859     HWND list = GetDlgItem(hDlg, IDC_SYSPARAM_COMBO);
860     int i, idx;
861
862     for (i = 0; i < sizeof(metrics) / sizeof(metrics[0]); i++)
863     {
864         LoadStringW(GetModuleHandle(NULL), i + IDC_SYSPARAMS_BUTTON, buffer,
865                     sizeof(buffer) / sizeof(buffer[0]));
866         idx = SendMessageW(list, CB_ADDSTRING, 0, (LPARAM)buffer);
867         if (idx != CB_ERR) SendMessageW(list, CB_SETITEMDATA, idx, i);
868
869         if (metrics[i].sm_idx != -1)
870             metrics[i].size = GetSystemMetrics(metrics[i].sm_idx);
871         if (metrics[i].color_idx != -1)
872             metrics[i].color = GetSysColor(metrics[i].color_idx);
873     }
874 }
875
876 static void apply_sysparams(void)
877 {
878     NONCLIENTMETRICSW nonclient_metrics;
879     int i, cnt = 0;
880     int colors_idx[sizeof(metrics) / sizeof(metrics[0])];
881     COLORREF colors[sizeof(metrics) / sizeof(metrics[0])];
882
883     nonclient_metrics.cbSize = sizeof(nonclient_metrics);
884     SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(nonclient_metrics), &nonclient_metrics, 0);
885
886     nonclient_metrics.iMenuWidth = nonclient_metrics.iMenuHeight =
887             metrics[IDC_SYSPARAMS_MENU - IDC_SYSPARAMS_BUTTON].size;
888     nonclient_metrics.iCaptionWidth = nonclient_metrics.iCaptionHeight =
889             metrics[IDC_SYSPARAMS_ACTIVE_TITLE - IDC_SYSPARAMS_BUTTON].size;
890     nonclient_metrics.iScrollWidth = nonclient_metrics.iScrollHeight =
891             metrics[IDC_SYSPARAMS_SCROLLBAR - IDC_SYSPARAMS_BUTTON].size;
892
893     SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, sizeof(nonclient_metrics), &nonclient_metrics,
894                           SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
895
896     for (i = 0; i < sizeof(metrics) / sizeof(metrics[0]); i++)
897         if (metrics[i].color_idx != -1)
898         {
899             colors_idx[cnt] = metrics[i].color_idx;
900             colors[cnt++] = metrics[i].color;
901         }
902     SetSysColors(cnt, colors_idx, colors);
903 }
904
905 static void on_sysparam_change(HWND hDlg)
906 {
907     int index = SendDlgItemMessageW(hDlg, IDC_SYSPARAM_COMBO, CB_GETCURSEL, 0, 0);
908
909     index = SendDlgItemMessageW(hDlg, IDC_SYSPARAM_COMBO, CB_GETITEMDATA, index, 0);
910
911     updating_ui = TRUE;
912
913     EnableWindow(GetDlgItem(hDlg, IDC_SYSPARAM_COLOR_TEXT), metrics[index].color_idx != -1);
914     EnableWindow(GetDlgItem(hDlg, IDC_SYSPARAM_COLOR), metrics[index].color_idx != -1);
915     InvalidateRect(GetDlgItem(hDlg, IDC_SYSPARAM_COLOR), NULL, TRUE);
916
917     EnableWindow(GetDlgItem(hDlg, IDC_SYSPARAM_SIZE_TEXT), metrics[index].sm_idx != -1);
918     EnableWindow(GetDlgItem(hDlg, IDC_SYSPARAM_SIZE), metrics[index].sm_idx != -1);
919     EnableWindow(GetDlgItem(hDlg, IDC_SYSPARAM_SIZE_UD), metrics[index].sm_idx != -1);
920     if (metrics[index].sm_idx != -1)
921         SendDlgItemMessageW(hDlg, IDC_SYSPARAM_SIZE_UD, UDM_SETPOS, 0, MAKELONG(metrics[index].size, 0));
922     else
923         set_text(hDlg, IDC_SYSPARAM_SIZE, "");
924
925     updating_ui = FALSE;
926 }
927
928 static void on_draw_item(HWND hDlg, WPARAM wParam, LPARAM lParam)
929 {
930     static HBRUSH black_brush = 0;
931     LPDRAWITEMSTRUCT draw_info = (LPDRAWITEMSTRUCT)lParam;
932
933     if (!black_brush) black_brush = CreateSolidBrush(0);
934
935     if (draw_info->CtlID == IDC_SYSPARAM_COLOR)
936     {
937         UINT state = DFCS_ADJUSTRECT | DFCS_BUTTONPUSH;
938
939         if (draw_info->itemState & ODS_DISABLED)
940             state |= DFCS_INACTIVE;
941         else
942             state |= draw_info->itemState & ODS_SELECTED ? DFCS_PUSHED : 0;
943
944         DrawFrameControl(draw_info->hDC, &draw_info->rcItem, DFC_BUTTON, state);
945
946         if (!(draw_info->itemState & ODS_DISABLED))
947         {
948             HBRUSH brush;
949             int index = SendDlgItemMessageW(hDlg, IDC_SYSPARAM_COMBO, CB_GETCURSEL, 0, 0);
950
951             index = SendDlgItemMessageW(hDlg, IDC_SYSPARAM_COMBO, CB_GETITEMDATA, index, 0);
952             brush = CreateSolidBrush(metrics[index].color);
953
954             InflateRect(&draw_info->rcItem, -1, -1);
955             FrameRect(draw_info->hDC, &draw_info->rcItem, black_brush);
956             InflateRect(&draw_info->rcItem, -1, -1);
957             FillRect(draw_info->hDC, &draw_info->rcItem, brush);
958             DeleteObject(brush);
959         }
960     }
961 }
962
963 INT_PTR CALLBACK
964 ThemeDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
965 {
966     switch (uMsg) {
967         case WM_INITDIALOG:
968             read_shell_folder_link_targets();
969             init_shell_folder_listview_headers(hDlg);
970             update_shell_folder_listview(hDlg);
971             read_sysparams(hDlg);
972             break;
973         
974         case WM_DESTROY:
975             free_theme_files();
976             break;
977
978         case WM_SHOWWINDOW:
979             set_window_title(hDlg);
980             break;
981             
982         case WM_COMMAND:
983             switch(HIWORD(wParam)) {
984                 case CBN_SELCHANGE: {
985                     if (updating_ui) break;
986                     switch (LOWORD(wParam))
987                     {
988                         case IDC_THEME_THEMECOMBO: on_theme_changed(hDlg); break;
989                         case IDC_THEME_COLORCOMBO: /* fall through */
990                         case IDC_THEME_SIZECOMBO: theme_dirty = TRUE; break;
991                         case IDC_SYSPARAM_COMBO: on_sysparam_change(hDlg); return FALSE;
992                     }
993                     SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
994                     break;
995                 }
996                 case EN_CHANGE: {
997                     if (updating_ui) break;
998                     switch (LOWORD(wParam))
999                     {
1000                         case IDC_EDIT_SFPATH: on_shell_folder_edit_changed(hDlg); break;
1001                         case IDC_SYSPARAM_SIZE:
1002                         {
1003                             char *text = get_text(hDlg, IDC_SYSPARAM_SIZE);
1004                             int index = SendDlgItemMessageW(hDlg, IDC_SYSPARAM_COMBO, CB_GETCURSEL, 0, 0);
1005
1006                             index = SendDlgItemMessageW(hDlg, IDC_SYSPARAM_COMBO, CB_GETITEMDATA, index, 0);
1007                             metrics[index].size = atoi(text);
1008                             HeapFree(GetProcessHeap(), 0, text);
1009
1010                             SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
1011                             break;
1012                         }
1013                     }
1014                     break;
1015                 }
1016                 case BN_CLICKED:
1017                     switch (LOWORD(wParam))
1018                     {
1019                         case IDC_THEME_INSTALL:
1020                             on_theme_install (hDlg);
1021                             break;
1022
1023                         case IDC_BROWSE_SFPATH:
1024                             if (browse_for_unix_folder(hDlg, psfiSelected->szLinkTarget)) {
1025                                 update_shell_folder_listview(hDlg);
1026                                 SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
1027                             }
1028                             break;
1029
1030                         case IDC_LINK_SFPATH:
1031                             if (IsDlgButtonChecked(hDlg, IDC_LINK_SFPATH)) {
1032                                 if (browse_for_unix_folder(hDlg, psfiSelected->szLinkTarget)) {
1033                                     update_shell_folder_listview(hDlg);
1034                                     SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
1035                                 } else {
1036                                     CheckDlgButton(hDlg, IDC_LINK_SFPATH, BST_UNCHECKED);
1037                                 }
1038                             } else {
1039                                 psfiSelected->szLinkTarget[0] = '\0';
1040                                 update_shell_folder_listview(hDlg);
1041                                 SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
1042                             }
1043                             break;    
1044
1045                         case IDC_SYSPARAM_COLOR:
1046                         {
1047                             static COLORREF user_colors[16];
1048                             CHOOSECOLORW c_color;
1049                             int index = SendDlgItemMessageW(hDlg, IDC_SYSPARAM_COMBO, CB_GETCURSEL, 0, 0);
1050
1051                             index = SendDlgItemMessageW(hDlg, IDC_SYSPARAM_COMBO, CB_GETITEMDATA, index, 0);
1052
1053                             memset(&c_color, 0, sizeof(c_color));
1054                             c_color.lStructSize = sizeof(c_color);
1055                             c_color.lpCustColors = user_colors;
1056                             c_color.rgbResult = metrics[index].color;
1057                             c_color.Flags = CC_ANYCOLOR | CC_RGBINIT;
1058                             c_color.hwndOwner = hDlg;
1059                             if (ChooseColorW(&c_color))
1060                             {
1061                                 metrics[index].color = c_color.rgbResult;
1062                                 save_sys_color(index, metrics[index].color);
1063                                 InvalidateRect(GetDlgItem(hDlg, IDC_SYSPARAM_COLOR), NULL, TRUE);
1064                                 SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
1065                             }
1066                             break;
1067                         }
1068                     }
1069                     break;
1070             }
1071             break;
1072         
1073         case WM_NOTIFY:
1074             switch (((LPNMHDR)lParam)->code) {
1075                 case PSN_KILLACTIVE: {
1076                     SetWindowLongPtr(hDlg, DWLP_MSGRESULT, FALSE);
1077                     break;
1078                 }
1079                 case PSN_APPLY: {
1080                     apply();
1081                     apply_theme(hDlg);
1082                     apply_shell_folder_changes();
1083                     apply_sysparams();
1084                     read_shell_folder_link_targets();
1085                     update_shell_folder_listview(hDlg);
1086                     SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
1087                     break;
1088                 }
1089                 case LVN_ITEMCHANGED: { 
1090                     if (wParam == IDC_LIST_SFPATHS)  
1091                         on_shell_folder_selection_changed(hDlg, (LPNMLISTVIEW)lParam);
1092                     break;
1093                 }
1094                 case PSN_SETACTIVE: {
1095                     init_dialog (hDlg);
1096                     break;
1097                 }
1098             }
1099             break;
1100
1101         case WM_DRAWITEM:
1102             on_draw_item(hDlg, wParam, lParam);
1103             break;
1104
1105         default:
1106             break;
1107     }
1108     return FALSE;
1109 }