xref: /reactos/dll/cpl/desk/background.c (revision 9393fc32)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Display Control Panel
4  * FILE:            dll/cpl/desk/background.c
5  * PURPOSE:         Background property page
6  *
7  * PROGRAMMERS:     Trevor McCort (lycan359@gmail.com)
8  *                  Alexey Minnekhanov (minlexx@rambler.ru)
9  */
10 
11 #include "desk.h"
12 
13 #include <shellapi.h>
14 #include <shlwapi.h>
15 
16 #define MAX_BACKGROUNDS     100
17 
18 typedef enum
19 {
20     PLACEMENT_CENTER = 0,
21     PLACEMENT_STRETCH,
22     PLACEMENT_TILE,
23     PLACEMENT_FIT,
24     PLACEMENT_FILL
25 } PLACEMENT;
26 
27 /* The tile placement is stored in different registry
28  * key, but due to a condition in win32k it needs to be
29  * zero when stored in the same key as others.
30  */
31 typedef enum
32 {
33     PLACEMENT_VALUE_CENTER    = 0,
34     PLACEMENT_VALUE_STRETCH   = 2,
35     PLACEMENT_VALUE_TILE      = 0,
36     PLACEMENT_VALUE_FIT       = 6,
37     PLACEMENT_VALUE_FILL      = 10
38 } PLACEMENT_VALUE;
39 
40 /* The values in these macros are dependent on the
41  * layout of the monitor image and they must be adjusted
42  * if that image will be changed.
43  */
44 #define MONITOR_LEFT        20
45 #define MONITOR_TOP         8
46 #define MONITOR_RIGHT       140
47 #define MONITOR_BOTTOM      92
48 
49 #define MONITOR_WIDTH       (MONITOR_RIGHT-MONITOR_LEFT)
50 #define MONITOR_HEIGHT      (MONITOR_BOTTOM-MONITOR_TOP)
51 
52 typedef struct
53 {
54     BOOL bWallpaper; /* Is this background a wallpaper */
55 
56     TCHAR szFilename[MAX_PATH];
57     TCHAR szDisplayName[256];
58 
59 } BackgroundItem;
60 
61 typedef struct _BACKGROUND_DATA
62 {
63     BOOL bWallpaperChanged;
64     BOOL bClrBackgroundChanged;
65 
66     BackgroundItem backgroundItems[MAX_BACKGROUNDS];
67 
68     PDIBITMAP pWallpaperBitmap;
69 
70     int placementSelection;
71     int backgroundSelection;
72 
73     COLORREF custom_colors[16];
74 
75     int listViewItemCount;
76 
77     HBITMAP hBitmap;
78     int cxSource;
79     int cySource;
80 
81     ULONG_PTR gdipToken;
82 } BACKGROUND_DATA, *PBACKGROUND_DATA;
83 
84 GLOBAL_DATA g_GlobalData;
85 
86 
87 HRESULT
88 GdipGetEncoderClsid(PCWSTR MimeType, CLSID *pClsid)
89 {
90     UINT num;
91     UINT size;
92     UINT i;
93     ImageCodecInfo *codecInfo;
94 
95     if (GdipGetImageEncodersSize(&num, &size) != Ok ||
96         size == 0)
97     {
98         return E_FAIL;
99     }
100 
101     codecInfo = HeapAlloc(GetProcessHeap(), 0, size);
102     if (!codecInfo)
103     {
104         return E_OUTOFMEMORY;
105     }
106 
107     if (GdipGetImageEncoders(num, size, codecInfo) != Ok)
108     {
109         HeapFree(GetProcessHeap(), 0, codecInfo);
110         return E_FAIL;
111     }
112 
113     for (i = 0; i < num; i++)
114     {
115         if (!_wcsicmp(codecInfo[i].MimeType, MimeType))
116         {
117             *pClsid = codecInfo[i].Clsid;
118             HeapFree(GetProcessHeap(), 0, codecInfo);
119             return S_OK;
120         }
121     }
122 
123     HeapFree(GetProcessHeap(), 0, codecInfo);
124     return E_FAIL;
125 }
126 
127 
128 LPWSTR
129 GdipGetSupportedFileExtensions(VOID)
130 {
131     ImageCodecInfo *codecInfo;
132     UINT num;
133     UINT size;
134     UINT i;
135     LPWSTR lpBuffer = NULL;
136 
137     if (GdipGetImageDecodersSize(&num, &size) != Ok ||
138         size == 0)
139     {
140         return NULL;
141     }
142 
143     codecInfo = HeapAlloc(GetProcessHeap(), 0, size);
144     if (!codecInfo)
145     {
146         return NULL;
147     }
148 
149     if (GdipGetImageDecoders(num, size, codecInfo) != Ok)
150     {
151         HeapFree(GetProcessHeap(), 0, codecInfo);
152         return NULL;
153     }
154 
155     size = 0;
156     for (i = 0; i < num; ++i)
157     {
158         size = size + (UINT)wcslen(codecInfo[i].FilenameExtension) + 1;
159     }
160 
161     size = (size + 1) * sizeof(WCHAR);
162 
163     lpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
164     if (!lpBuffer)
165     {
166         HeapFree(GetProcessHeap(), 0, codecInfo);
167         return NULL;
168     }
169 
170     for (i = 0; i < num; ++i)
171     {
172         StringCbCatW(lpBuffer, size, codecInfo[i].FilenameExtension);
173         if (i < (num - 1))
174         {
175             StringCbCatW(lpBuffer, size, L";");
176         }
177     }
178 
179     HeapFree(GetProcessHeap(), 0, codecInfo);
180 
181     return lpBuffer;
182 }
183 
184 
185 static UINT
186 AddWallpapersFromDirectory(UINT uCounter, HWND hwndBackgroundList, BackgroundItem *backgroundItem, PBACKGROUND_DATA pData, LPCTSTR wallpaperFilename, LPCTSTR wallpaperDirectory)
187 {
188     WIN32_FIND_DATA fd;
189     HANDLE hFind;
190     TCHAR szSearchPath[MAX_PATH];
191     LPTSTR szFileTypes = NULL;
192     TCHAR separators[] = TEXT(";");
193     TCHAR *token;
194     HRESULT hr;
195     SHFILEINFO sfi;
196     UINT i = uCounter;
197     LV_ITEM listItem;
198     HIMAGELIST himl;
199 
200     szFileTypes = GdipGetSupportedFileExtensions();
201     if (!szFileTypes)
202     {
203         return i;
204     }
205 
206     himl = ListView_GetImageList(hwndBackgroundList, LVSIL_SMALL);
207 
208     token = _tcstok(szFileTypes, separators);
209     while (token != NULL)
210     {
211         if (!PathCombine(szSearchPath, wallpaperDirectory, token))
212         {
213             HeapFree(GetProcessHeap(), 0, szFileTypes);
214             return i;
215         }
216 
217         hFind = FindFirstFile(szSearchPath, &fd);
218         while (hFind != INVALID_HANDLE_VALUE)
219         {
220             TCHAR filename[MAX_PATH];
221 
222             if (!PathCombine(filename, wallpaperDirectory, fd.cFileName))
223             {
224                 FindClose(hFind);
225                 HeapFree(GetProcessHeap(), 0, szFileTypes);
226                 return i;
227             }
228 
229             /* Don't add any hidden bitmaps. Also don't add current wallpaper once more. */
230             if (((fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0) && (_tcsicmp(wallpaperFilename, filename) != 0))
231             {
232                 SHGetFileInfo(filename,
233                               0,
234                               &sfi,
235                               sizeof(sfi),
236                               SHGFI_ICON | SHGFI_SMALLICON | SHGFI_DISPLAYNAME);
237                 sfi.iIcon = ImageList_AddIcon(himl, sfi.hIcon);
238                 i++;
239 
240                 backgroundItem = &pData->backgroundItems[pData->listViewItemCount];
241 
242                 backgroundItem->bWallpaper = TRUE;
243 
244                 hr = StringCbCopy(backgroundItem->szDisplayName, sizeof(backgroundItem->szDisplayName), sfi.szDisplayName);
245                 if (FAILED(hr))
246                 {
247                     FindClose(hFind);
248                     HeapFree(GetProcessHeap(), 0, szFileTypes);
249                     return i;
250                 }
251 
252                 PathRemoveExtension(backgroundItem->szDisplayName);
253 
254                 hr = StringCbCopy(backgroundItem->szFilename, sizeof(backgroundItem->szFilename), filename);
255                 if (FAILED(hr))
256                 {
257                     FindClose(hFind);
258                     HeapFree(GetProcessHeap(), 0, szFileTypes);
259                     return i;
260                 }
261 
262                 ZeroMemory(&listItem, sizeof(LV_ITEM));
263                 listItem.mask       = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
264                 listItem.pszText    = backgroundItem->szDisplayName;
265                 listItem.state      = 0;
266                 listItem.iImage     = sfi.iIcon;
267                 listItem.iItem      = pData->listViewItemCount;
268                 listItem.lParam     = pData->listViewItemCount;
269 
270                 (void)ListView_InsertItem(hwndBackgroundList, &listItem);
271 
272                 pData->listViewItemCount++;
273             }
274 
275             if (!FindNextFile(hFind, &fd))
276                 break;
277         }
278 
279         token = _tcstok(NULL, separators);
280         FindClose(hFind);
281     }
282 
283     HeapFree(GetProcessHeap(), 0, szFileTypes);
284 
285     return i;
286 }
287 
288 
289 /* Add the images in the C:\ReactOS, the wallpaper directory and the current wallpaper if any */
290 static VOID
291 AddListViewItems(HWND hwndDlg, PBACKGROUND_DATA pData)
292 {
293     TCHAR szSearchPath[MAX_PATH];
294     LV_ITEM listItem;
295     LV_COLUMN dummy;
296     RECT clientRect;
297     HKEY regKey;
298     SHFILEINFO sfi;
299     HIMAGELIST himl;
300     TCHAR wallpaperFilename[MAX_PATH];
301     TCHAR originalWallpaper[MAX_PATH];
302     DWORD bufferSize = sizeof(wallpaperFilename);
303     TCHAR buffer[MAX_PATH];
304     DWORD varType = REG_SZ;
305     LONG result;
306     UINT i = 0;
307     BackgroundItem *backgroundItem = NULL;
308     HWND hwndBackgroundList;
309     HRESULT hr;
310     HICON hIcon;
311     INT cx, cy;
312     HINSTANCE hShell32;
313 
314     hwndBackgroundList = GetDlgItem(hwndDlg, IDC_BACKGROUND_LIST);
315 
316     GetClientRect(hwndBackgroundList, &clientRect);
317 
318     cx = GetSystemMetrics(SM_CXSMICON);
319     cy = GetSystemMetrics(SM_CYSMICON);
320     himl = ImageList_Create(cx, cy, ILC_COLOR32 | ILC_MASK, 0, 0);
321 
322     /* Load (None) icon */
323 #define IDI_SHELL_NO 200
324     hShell32 = GetModuleHandleW(L"shell32.dll");
325     hIcon = (HICON)LoadImageW(hShell32, MAKEINTRESOURCEW(IDI_SHELL_NO), IMAGE_ICON, cx, cy, 0);
326 #undef IDI_SHELL_NO
327 
328     ListView_SetImageList(hwndBackgroundList, himl, LVSIL_SMALL);
329 
330     /* Add a new column to the list */
331     ZeroMemory(&dummy, sizeof(LV_COLUMN));
332     dummy.mask      = LVCF_SUBITEM | LVCF_WIDTH;
333     dummy.iSubItem  = 0;
334     dummy.cx        = (clientRect.right - clientRect.left) - GetSystemMetrics(SM_CXVSCROLL);
335     (void)ListView_InsertColumn(hwndBackgroundList, 0, &dummy);
336 
337     /* Add the "None" item */
338     backgroundItem = &pData->backgroundItems[pData->listViewItemCount];
339     backgroundItem->bWallpaper = FALSE;
340     LoadString(hApplet,
341                IDS_NONE,
342                backgroundItem->szDisplayName,
343                sizeof(backgroundItem->szDisplayName) / sizeof(TCHAR));
344 
345     ZeroMemory(&listItem, sizeof(LV_ITEM));
346     listItem.mask       = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
347     listItem.state      = 0;
348     listItem.pszText    = backgroundItem->szDisplayName;
349     listItem.iImage     = ImageList_AddIcon(himl, hIcon);
350     listItem.iItem      = pData->listViewItemCount;
351     listItem.lParam     = pData->listViewItemCount;
352     hIcon = NULL;
353 
354     (void)ListView_InsertItem(hwndBackgroundList, &listItem);
355     ListView_SetItemState(hwndBackgroundList,
356                           pData->listViewItemCount,
357                           LVIS_SELECTED,
358                           LVIS_SELECTED);
359 
360     pData->listViewItemCount++;
361 
362     /* Add current wallpaper if any */
363     result = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), 0, KEY_QUERY_VALUE, &regKey);
364     if (result == ERROR_SUCCESS)
365     {
366         result = RegQueryValueEx(regKey, TEXT("Wallpaper"), 0, &varType, (LPBYTE)wallpaperFilename, &bufferSize);
367         if ((result == ERROR_SUCCESS) && (_tcslen(wallpaperFilename) > 0))
368         {
369             bufferSize = sizeof(originalWallpaper);
370             result = RegQueryValueEx(regKey, TEXT("OriginalWallpaper"), 0, &varType, (LPBYTE)originalWallpaper, &bufferSize);
371 
372             /* If Wallpaper and OriginalWallpaper are the same, try to retrieve ConvertedWallpaper and use it instead of Wallpaper */
373             if ((result == ERROR_SUCCESS) && (_tcslen(originalWallpaper) > 0) && (_tcsicmp(wallpaperFilename, originalWallpaper) == 0))
374             {
375                 bufferSize = sizeof(originalWallpaper);
376                 result = RegQueryValueEx(regKey, TEXT("ConvertedWallpaper"), 0, &varType, (LPBYTE)originalWallpaper, &bufferSize);
377 
378                 if ((result == ERROR_SUCCESS) && (_tcslen(originalWallpaper) > 0))
379                 {
380                     hr = StringCbCopy(wallpaperFilename, sizeof(wallpaperFilename), originalWallpaper);
381                     if (FAILED(hr))
382                     {
383                         RegCloseKey(regKey);
384                         return;
385                     }
386                 }
387             }
388 
389             /* Allow environment variables in file name */
390             if (ExpandEnvironmentStrings(wallpaperFilename, buffer, MAX_PATH))
391             {
392                 hr = StringCbCopy(wallpaperFilename, sizeof(wallpaperFilename), buffer);
393                 if (FAILED(hr))
394                 {
395                     RegCloseKey(regKey);
396                     return;
397                 }
398             }
399 
400             SHGetFileInfoW(wallpaperFilename,
401                            0,
402                            &sfi,
403                            sizeof(sfi),
404                            SHGFI_ICON | SHGFI_SMALLICON |
405                            SHGFI_DISPLAYNAME);
406             sfi.iIcon = ImageList_AddIcon(himl, sfi.hIcon);
407 
408             i++;
409 
410             backgroundItem = &pData->backgroundItems[pData->listViewItemCount];
411 
412             backgroundItem->bWallpaper = TRUE;
413 
414             hr = StringCbCopy(backgroundItem->szDisplayName, sizeof(backgroundItem->szDisplayName), sfi.szDisplayName);
415             if (FAILED(hr))
416             {
417                 RegCloseKey(regKey);
418                 return;
419             }
420 
421             PathRemoveExtension(backgroundItem->szDisplayName);
422 
423             hr = StringCbCopy(backgroundItem->szFilename, sizeof(backgroundItem->szFilename), wallpaperFilename);
424             if (FAILED(hr))
425             {
426                 RegCloseKey(regKey);
427                 return;
428             }
429 
430             ZeroMemory(&listItem, sizeof(LV_ITEM));
431             listItem.mask       = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
432             listItem.state      = 0;
433             listItem.pszText    = backgroundItem->szDisplayName;
434             listItem.iImage     = sfi.iIcon;
435             listItem.iItem      = pData->listViewItemCount;
436             listItem.lParam     = pData->listViewItemCount;
437 
438             (void)ListView_InsertItem(hwndBackgroundList, &listItem);
439             ListView_SetItemState(hwndBackgroundList,
440                                   pData->listViewItemCount,
441                                   LVIS_SELECTED,
442                                   LVIS_SELECTED);
443 
444             pData->listViewItemCount++;
445         }
446 
447         RegCloseKey(regKey);
448     }
449 
450     /* Add all the images in the C:\ReactOS directory. */
451     if (GetWindowsDirectory(szSearchPath, MAX_PATH))
452     {
453         i = AddWallpapersFromDirectory(i, hwndBackgroundList, backgroundItem, pData, wallpaperFilename, szSearchPath);
454     }
455 
456     /* Add all the images in the wallpaper directory. */
457     if (SHRegGetPath(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"), TEXT("WallPaperDir"), szSearchPath, 0) == ERROR_SUCCESS)
458     {
459         i = AddWallpapersFromDirectory(i, hwndBackgroundList, backgroundItem, pData, wallpaperFilename, szSearchPath);
460     }
461 }
462 
463 
464 static VOID
465 InitBackgroundDialog(HWND hwndDlg, PBACKGROUND_DATA pData)
466 {
467     TCHAR szString[256];
468     HKEY regKey;
469     TCHAR szBuffer[3];
470     DWORD bufferSize = sizeof(szBuffer);
471     BITMAP bitmap;
472 
473     AddListViewItems(hwndDlg, pData);
474 
475     LoadString(hApplet, IDS_CENTER, szString, sizeof(szString) / sizeof(TCHAR));
476     SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_CENTER, (LPARAM)szString);
477 
478     LoadString(hApplet, IDS_STRETCH, szString, sizeof(szString) / sizeof(TCHAR));
479     SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_STRETCH, (LPARAM)szString);
480 
481     LoadString(hApplet, IDS_TILE, szString, sizeof(szString) / sizeof(TCHAR));
482     SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_TILE, (LPARAM)szString);
483 
484     LoadString(hApplet, IDS_FIT, szString, sizeof(szString) / sizeof(TCHAR));
485     SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_FIT, (LPARAM)szString);
486 
487     LoadString(hApplet, IDS_FILL, szString, sizeof(szString) / sizeof(TCHAR));
488     SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_FILL, (LPARAM)szString);
489 
490     SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_CENTER, 0);
491     pData->placementSelection = PLACEMENT_CENTER;
492 
493     pData->hBitmap = (HBITMAP) LoadImage(hApplet, MAKEINTRESOURCE(IDC_MONITOR), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
494     if (pData->hBitmap != NULL)
495     {
496         GetObject(pData->hBitmap, sizeof(BITMAP), &bitmap);
497 
498         pData->cxSource = bitmap.bmWidth;
499         pData->cySource = bitmap.bmHeight;
500     }
501 
502     /* Load the default settings from the registry */
503     if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), 0, KEY_QUERY_VALUE, &regKey) != ERROR_SUCCESS)
504     {
505         return;
506     }
507 
508     if (RegQueryValueEx(regKey, TEXT("WallpaperStyle"), 0, NULL, (LPBYTE)szBuffer, &bufferSize) == ERROR_SUCCESS)
509     {
510         if (_ttoi(szBuffer) == PLACEMENT_VALUE_CENTER)
511         {
512             SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_CENTER, 0);
513             pData->placementSelection = PLACEMENT_CENTER;
514         }
515 
516         if (_ttoi(szBuffer) == PLACEMENT_VALUE_STRETCH)
517         {
518             SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_STRETCH, 0);
519             pData->placementSelection = PLACEMENT_STRETCH;
520         }
521 
522         if (_ttoi(szBuffer) == PLACEMENT_VALUE_FIT)
523         {
524             SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_FIT, 0);
525             pData->placementSelection = PLACEMENT_FIT;
526         }
527 
528         if (_ttoi(szBuffer) == PLACEMENT_VALUE_FILL)
529         {
530             SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_FILL, 0);
531             pData->placementSelection = PLACEMENT_FILL;
532         }
533     }
534 
535     if (RegQueryValueEx(regKey, TEXT("TileWallpaper"), 0, NULL, (LPBYTE)szBuffer, &bufferSize) == ERROR_SUCCESS)
536     {
537         if (_ttoi(szBuffer) == 1)
538         {
539             SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_TILE, 0);
540             pData->placementSelection = PLACEMENT_TILE;
541         }
542     }
543 
544     RegCloseKey(regKey);
545 }
546 
547 
548 static VOID
549 OnColorButton(HWND hwndDlg, PBACKGROUND_DATA pData)
550 {
551     /* Load custom colors from Registry */
552     HKEY hKey = NULL;
553     LONG res = ERROR_SUCCESS;
554     CHOOSECOLOR cc;
555 
556     res = RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Appearance"), 0, NULL,
557                          REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hKey, NULL);
558     /* Now the key is either created or opened existing, if res == ERROR_SUCCESS */
559     if (res == ERROR_SUCCESS)
560     {
561         /* Key opened */
562         DWORD dwType = REG_BINARY;
563         DWORD cbData = sizeof(pData->custom_colors);
564         res = RegQueryValueEx(hKey, TEXT("CustomColors"), NULL, &dwType,
565                               (LPBYTE)pData->custom_colors, &cbData);
566         RegCloseKey(hKey);
567         hKey = NULL;
568     }
569 
570     /* Launch ChooseColor() dialog */
571 
572     cc.lStructSize = sizeof(CHOOSECOLOR);
573     cc.hwndOwner = hwndDlg;
574     cc.hInstance = NULL;
575     cc.rgbResult = g_GlobalData.desktop_color;
576     cc.lpCustColors = pData->custom_colors;
577     cc.Flags = CC_ANYCOLOR | /* Causes the dialog box to display all available colors in the set of basic colors.  */
578                CC_FULLOPEN | /* opens dialog in full size */
579                CC_RGBINIT ;  /* init chosen color by rgbResult value */
580     cc.lCustData = 0;
581     cc.lpfnHook = NULL;
582     cc.lpTemplateName = NULL;
583     if (ChooseColor(&cc))
584     {
585         /* Save selected color to var */
586         g_GlobalData.desktop_color = cc.rgbResult;
587         pData->bClrBackgroundChanged = TRUE;
588 
589         /* Apply button will be activated */
590         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
591 
592         /* Window will be updated :) */
593         InvalidateRect(GetDlgItem(hwndDlg, IDC_BACKGROUND_PREVIEW), NULL, TRUE);
594 
595         /* Save custom colors to reg. To this moment key must be created already. See above */
596         res = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Appearance"), 0,
597                            KEY_SET_VALUE, &hKey);
598         if (res == ERROR_SUCCESS)
599         {
600             /* Key opened */
601             RegSetValueEx(hKey, TEXT("CustomColors"), 0, REG_BINARY,
602                           (LPBYTE)pData->custom_colors, sizeof(pData->custom_colors));
603             RegCloseKey(hKey);
604             hKey = NULL;
605         }
606     }
607 }
608 
609 
610 /*
611  * ListView_FindItem() Macro: Searches for a list-view item with the specified
612  * characteristics. Returns the index of the item if successful, or -1 otherwise
613  */
614 static BOOL
615 CheckListViewFilenameExists(HWND hwndList, LPCTSTR tszFileName)
616 {
617     LVFINDINFO lvfi;
618     int retVal;
619 
620     lvfi.flags = LVFI_STRING; /* Search item by EXACT string */
621     lvfi.psz   = tszFileName; /* String to search */
622 
623     /* Other items of this structure are not valid, besacuse flags are not set. */
624     retVal = ListView_FindItem(hwndList, -1, &lvfi);
625     if (retVal != -1)
626         return TRUE; /* item found! */
627 
628     return FALSE; /* item not found. */
629 }
630 
631 
632 static VOID
633 OnBrowseButton(HWND hwndDlg, PBACKGROUND_DATA pData)
634 {
635     OPENFILENAME ofn;
636     TCHAR filename[MAX_PATH];
637     TCHAR fileTitle[256];
638     TCHAR initialDir[MAX_PATH];
639     LPTSTR filter;
640     LPTSTR extensions;
641     BackgroundItem *backgroundItem = NULL;
642     SHFILEINFO sfi;
643     LV_ITEM listItem;
644     HWND hwndBackgroundList;
645     TCHAR *p;
646     HRESULT hr;
647     TCHAR filterdesc[MAX_PATH];
648     TCHAR *c;
649     size_t sizeRemain;
650     SIZE_T buffersize;
651     BOOL success;
652     HIMAGELIST himl;
653 
654     hwndBackgroundList = GetDlgItem(hwndDlg, IDC_BACKGROUND_LIST);
655     himl = ListView_GetImageList(hwndBackgroundList, LVSIL_SMALL);
656     SHGetFolderPathW(NULL, CSIDL_MYPICTURES, NULL, 0, initialDir);
657 
658     ZeroMemory(&ofn, sizeof(OPENFILENAME));
659 
660     ofn.lStructSize = sizeof(OPENFILENAME);
661     ofn.hwndOwner = hwndDlg;
662     ofn.lpstrFile = filename;
663 
664     LoadString(hApplet, IDS_BACKGROUND_COMDLG_FILTER, filterdesc, sizeof(filterdesc) / sizeof(TCHAR));
665 
666     extensions = GdipGetSupportedFileExtensions();
667     if (!extensions)
668     {
669         return;
670     }
671 
672     buffersize = (_tcslen(extensions) * 2 + 6) * sizeof(TCHAR) + sizeof(filterdesc);
673 
674     filter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffersize);
675     if (!filter)
676     {
677         HeapFree(GetProcessHeap(), 0, extensions);
678         return;
679     }
680 
681     sizeRemain = buffersize;
682     c = filter;
683 
684     if (FAILED(StringCbPrintfEx(c, sizeRemain, &c, &sizeRemain, 0, L"%ls (%ls)", filterdesc, extensions)))
685     {
686         HeapFree(GetProcessHeap(), 0, extensions);
687         HeapFree(GetProcessHeap(), 0, filter);
688         return;
689     }
690 
691     c++;
692     sizeRemain -= sizeof(*c);
693 
694     if (FAILED(StringCbPrintfEx(c, sizeRemain, &c, &sizeRemain, 0, L"%ls", extensions)))
695     {
696         HeapFree(GetProcessHeap(), 0, extensions);
697         HeapFree(GetProcessHeap(), 0, filter);
698         return;
699     }
700 
701     HeapFree(GetProcessHeap(), 0, extensions);
702 
703     /* Set lpstrFile[0] to '\0' so that GetOpenFileName does not
704      * use the contents of szFile to initialize itself */
705     ofn.lpstrFile[0] = TEXT('\0');
706     ofn.nMaxFile = MAX_PATH;
707     ofn.lpstrFilter = filter;
708     ofn.nFilterIndex = 0;
709     ofn.lpstrFileTitle = fileTitle;
710     ofn.nMaxFileTitle = 256;
711     ofn.lpstrInitialDir = initialDir;
712     ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER;
713 
714     success = GetOpenFileName(&ofn);
715     HeapFree(GetProcessHeap(), 0, filter);
716 
717     if (success)
718     {
719         /* Check if there is already a entry that holds this filename */
720         if (CheckListViewFilenameExists(hwndBackgroundList, ofn.lpstrFileTitle) != FALSE)
721             return;
722 
723         if (pData->listViewItemCount > (MAX_BACKGROUNDS - 1))
724             return;
725 
726         SHGetFileInfo(filename,
727                       0,
728                       &sfi,
729                       sizeof(sfi),
730                       SHGFI_ICON | SHGFI_SMALLICON | SHGFI_DISPLAYNAME);
731         sfi.iIcon = ImageList_AddIcon(himl, sfi.hIcon);
732 
733         backgroundItem = &pData->backgroundItems[pData->listViewItemCount];
734 
735         backgroundItem->bWallpaper = TRUE;
736 
737         hr = StringCbCopy(backgroundItem->szDisplayName, sizeof(backgroundItem->szDisplayName), sfi.szDisplayName);
738         if (FAILED(hr))
739             return;
740         p = _tcsrchr(backgroundItem->szDisplayName, _T('.'));
741         if (p)
742             *p = (TCHAR)0;
743         hr = StringCbCopy(backgroundItem->szFilename, sizeof(backgroundItem->szFilename), filename);
744         if (FAILED(hr))
745             return;
746 
747         ZeroMemory(&listItem, sizeof(LV_ITEM));
748         listItem.mask       = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
749         listItem.state      = 0;
750         listItem.pszText    = backgroundItem->szDisplayName;
751         listItem.iImage     = sfi.iIcon;
752         listItem.iItem      = pData->listViewItemCount;
753         listItem.lParam     = pData->listViewItemCount;
754 
755         (void)ListView_InsertItem(hwndBackgroundList, &listItem);
756         ListView_SetItemState(hwndBackgroundList,
757                               pData->listViewItemCount,
758                               LVIS_SELECTED,
759                               LVIS_SELECTED);
760         SendMessage(hwndBackgroundList, WM_VSCROLL, SB_BOTTOM, 0);
761 
762         pData->listViewItemCount++;
763     }
764 }
765 
766 
767 static VOID
768 ListViewItemChanged(HWND hwndDlg, PBACKGROUND_DATA pData, int itemIndex)
769 {
770     BackgroundItem *backgroundItem = NULL;
771 
772     pData->backgroundSelection = itemIndex;
773     backgroundItem = &pData->backgroundItems[pData->backgroundSelection];
774 
775     if (pData->pWallpaperBitmap != NULL)
776     {
777         DibFreeImage(pData->pWallpaperBitmap);
778         pData->pWallpaperBitmap = NULL;
779     }
780 
781     if (backgroundItem->bWallpaper != FALSE)
782     {
783         pData->pWallpaperBitmap = DibLoadImage(backgroundItem->szFilename);
784 
785         if (pData->pWallpaperBitmap == NULL)
786             return;
787     }
788 
789     pData->bWallpaperChanged = TRUE;
790 
791     InvalidateRect(GetDlgItem(hwndDlg, IDC_BACKGROUND_PREVIEW),
792                    NULL, TRUE);
793 
794     EnableWindow(GetDlgItem(hwndDlg, IDC_PLACEMENT_COMBO),
795                  backgroundItem->bWallpaper);
796 
797     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
798 }
799 
800 
801 static VOID
802 DrawBackgroundPreview(LPDRAWITEMSTRUCT draw, PBACKGROUND_DATA pData)
803 {
804     float scaleX;
805     float scaleY;
806     int scaledWidth;
807     int scaledHeight;
808     int posX, desX;
809     int posY, desY;
810     int fitFillScaleNum, fitFillScaleDen;
811     int fitFillWidth, fitFillHeight;
812     HBRUSH hBrush;
813     int x;
814     int y;
815     HDC hDC;
816     HGDIOBJ hOldObj;
817     RECT rcItem = {
818         MONITOR_LEFT,
819         MONITOR_TOP,
820         MONITOR_RIGHT,
821         MONITOR_BOTTOM
822     };
823 
824     hDC = CreateCompatibleDC(draw->hDC);
825     hOldObj = SelectObject(hDC, pData->hBitmap);
826 
827     if (pData->backgroundItems[pData->backgroundSelection].bWallpaper == FALSE)
828     {
829         /* Update desktop background color image */
830         hBrush = CreateSolidBrush(g_GlobalData.desktop_color);
831         FillRect(hDC, &rcItem, hBrush);
832         DeleteObject(hBrush);
833     }
834     else
835     if (pData->pWallpaperBitmap != NULL)
836     {
837         scaleX = ((float)GetSystemMetrics(SM_CXSCREEN) - 1) / (float)MONITOR_WIDTH;
838         scaleY = ((float)GetSystemMetrics(SM_CYSCREEN) - 1) / (float)MONITOR_HEIGHT;
839 
840         scaledWidth = (int)(pData->pWallpaperBitmap->width / scaleX);
841         scaledHeight = (int)(pData->pWallpaperBitmap->height / scaleY);
842 
843         FillRect(hDC, &rcItem, GetSysColorBrush(COLOR_BACKGROUND));
844 
845         SetStretchBltMode(hDC, COLORONCOLOR);
846 
847         switch (pData->placementSelection)
848         {
849             case PLACEMENT_CENTER:
850                 posX = (MONITOR_WIDTH - scaledWidth + 1) / 2;
851                 posY = (MONITOR_HEIGHT - scaledHeight + 1) / 2;
852                 desX = 0;
853                 desY = 0;
854 
855                 if (posX < 0) { desX = -posX / 2; posX = 0; }
856                 if (posY < 0) { desY = -posY / 2; posY = 0; }
857 
858                 if (scaledWidth > MONITOR_WIDTH)
859                     scaledWidth = MONITOR_WIDTH;
860 
861                 if (scaledHeight > MONITOR_HEIGHT)
862                     scaledHeight = MONITOR_HEIGHT;
863 
864                 StretchDIBits(hDC,
865                               MONITOR_LEFT+posX,
866                               MONITOR_TOP+posY,
867                               scaledWidth,
868                               scaledHeight,
869                               desX,
870                               desY,
871                               pData->pWallpaperBitmap->width - (int)(desX * scaleX),
872                               pData->pWallpaperBitmap->height - (int)(desY * scaleY),
873                               pData->pWallpaperBitmap->bits,
874                               pData->pWallpaperBitmap->info,
875                               DIB_RGB_COLORS,
876                               SRCCOPY);
877                 break;
878 
879             case PLACEMENT_STRETCH:
880                 StretchDIBits(hDC,
881                               MONITOR_LEFT,
882                               MONITOR_TOP,
883                               MONITOR_WIDTH,
884                               MONITOR_HEIGHT,
885                               0,
886                               0,
887                               pData->pWallpaperBitmap->width,
888                               pData->pWallpaperBitmap->height,
889                               pData->pWallpaperBitmap->bits,
890                               pData->pWallpaperBitmap->info,
891                               DIB_RGB_COLORS,
892                               SRCCOPY);
893                 break;
894 
895             case PLACEMENT_TILE:
896                 for (y = 0; y < MONITOR_HEIGHT; y += scaledHeight)
897                 {
898                     for (x = 0; x < MONITOR_WIDTH; x += scaledWidth)
899                     {
900                         if ((MONITOR_WIDTH-x) >= scaledWidth)
901                             posX = scaledWidth;
902                         else
903                             posX = MONITOR_WIDTH-x;
904 
905 
906                         if ((MONITOR_HEIGHT-y) >= scaledHeight)
907                             posY = scaledHeight;
908                         else
909                             posY = MONITOR_HEIGHT-y;
910 
911                         StretchDIBits(hDC,
912                                       MONITOR_LEFT + x,
913                                       MONITOR_TOP + y,
914                                       posX,
915                                       posY,
916                                       0,
917                                       0,
918                                       pData->pWallpaperBitmap->width * posX / scaledWidth,
919                                       pData->pWallpaperBitmap->height * posY / scaledHeight,
920                                       pData->pWallpaperBitmap->bits,
921                                       pData->pWallpaperBitmap->info,
922                                       DIB_RGB_COLORS,
923                                       SRCCOPY);
924                     }
925 
926                 }
927 
928                 break;
929 
930             case PLACEMENT_FIT:
931                 if ((MONITOR_WIDTH * scaledHeight) <= (MONITOR_HEIGHT * scaledWidth))
932                 {
933                     fitFillScaleNum = MONITOR_WIDTH;
934                     fitFillScaleDen = scaledWidth;
935                 }
936                 else
937                 {
938                     fitFillScaleNum = MONITOR_HEIGHT;
939                     fitFillScaleDen = scaledHeight;
940                 }
941 
942                 fitFillWidth = MulDiv(scaledWidth, fitFillScaleNum, fitFillScaleDen);
943                 fitFillHeight = MulDiv(scaledHeight, fitFillScaleNum, fitFillScaleDen);
944 
945                 posX = (MONITOR_WIDTH - fitFillWidth) / 2;
946                 posY = (MONITOR_HEIGHT - fitFillHeight) / 2;
947 
948                 StretchDIBits(hDC,
949                               MONITOR_LEFT + posX,
950                               MONITOR_TOP + posY,
951                               fitFillWidth,
952                               fitFillHeight,
953                               0,
954                               0,
955                               pData->pWallpaperBitmap->width,
956                               pData->pWallpaperBitmap->height,
957                               pData->pWallpaperBitmap->bits,
958                               pData->pWallpaperBitmap->info,
959                               DIB_RGB_COLORS,
960                               SRCCOPY);
961                 break;
962 
963             case PLACEMENT_FILL:
964                 if ((MONITOR_WIDTH * scaledHeight) > (MONITOR_HEIGHT * scaledWidth))
965                 {
966                     fitFillScaleNum = MONITOR_WIDTH;
967                     fitFillScaleDen = scaledWidth;
968                 }
969                 else
970                 {
971                     fitFillScaleNum = MONITOR_HEIGHT;
972                     fitFillScaleDen = scaledHeight;
973                 }
974 
975                 fitFillWidth = MulDiv(scaledWidth, fitFillScaleNum, fitFillScaleDen);
976                 fitFillHeight = MulDiv(scaledHeight, fitFillScaleNum, fitFillScaleDen);
977 
978                 desX = (((fitFillWidth - MONITOR_WIDTH) * pData->pWallpaperBitmap->width) / (2 * fitFillWidth));
979                 desY = (((fitFillHeight - MONITOR_HEIGHT) * pData->pWallpaperBitmap->height) / (2 * fitFillHeight));
980 
981                 StretchDIBits(hDC,
982                               MONITOR_LEFT,
983                               MONITOR_TOP,
984                               MONITOR_WIDTH,
985                               MONITOR_HEIGHT,
986                               desX,
987                               desY,
988                               (MONITOR_WIDTH * pData->pWallpaperBitmap->width) / fitFillWidth,
989                               (MONITOR_HEIGHT * pData->pWallpaperBitmap->height) / fitFillHeight,
990                               pData->pWallpaperBitmap->bits,
991                               pData->pWallpaperBitmap->info,
992                               DIB_RGB_COLORS,
993                               SRCCOPY);
994                 break;
995         }
996     }
997 
998     GdiTransparentBlt(draw->hDC,
999                       draw->rcItem.left, draw->rcItem.top,
1000                       draw->rcItem.right-draw->rcItem.left+1,
1001                       draw->rcItem.bottom-draw->rcItem.top+1,
1002                       hDC,
1003                       0, 0,
1004                       pData->cxSource, pData->cySource,
1005                       0xFF00FF);
1006 
1007     SelectObject(hDC, hOldObj);
1008     DeleteDC(hDC);
1009 }
1010 
1011 
1012 static VOID
1013 SetWallpaper(PBACKGROUND_DATA pData)
1014 {
1015     HKEY regKey;
1016     TCHAR szWallpaper[MAX_PATH];
1017     GpImage *image;
1018     CLSID  encoderClsid;
1019     GUID guidFormat;
1020     size_t length = 0;
1021     GpStatus status;
1022 
1023     if (FAILED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, szWallpaper)))
1024     {
1025         return;
1026     }
1027 
1028     if (FAILED(StringCbCat(szWallpaper, sizeof(szWallpaper), TEXT("\\Wallpaper1.bmp"))))
1029     {
1030         return;
1031     }
1032 
1033     if (RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), 0, NULL,
1034                        REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &regKey, NULL) != ERROR_SUCCESS)
1035     {
1036         return;
1037     }
1038 
1039     if (pData->placementSelection == PLACEMENT_TILE)
1040     {
1041         RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("1"), sizeof(TCHAR) * 2);
1042         RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
1043     }
1044 
1045     if (pData->placementSelection == PLACEMENT_CENTER)
1046     {
1047         RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
1048         RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
1049     }
1050 
1051     if (pData->placementSelection == PLACEMENT_STRETCH)
1052     {
1053         RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
1054         RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("2"), sizeof(TCHAR) * 2);
1055     }
1056 
1057     if (pData->placementSelection == PLACEMENT_FIT)
1058     {
1059         RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
1060         RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("6"), sizeof(TCHAR) * 2);
1061     }
1062 
1063     if (pData->placementSelection == PLACEMENT_FILL)
1064     {
1065         RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
1066         RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("10"), sizeof(TCHAR) * 3);
1067     }
1068 
1069     if (pData->backgroundItems[pData->backgroundSelection].bWallpaper != FALSE)
1070     {
1071         GdipLoadImageFromFile(pData->backgroundItems[pData->backgroundSelection].szFilename, &image);
1072         if (!image)
1073         {
1074             RegCloseKey(regKey);
1075             return;
1076         }
1077 
1078         GdipGetImageRawFormat(image, &guidFormat);
1079         if (IsEqualGUID(&guidFormat, &ImageFormatBMP))
1080         {
1081             GdipDisposeImage(image);
1082             RegCloseKey(regKey);
1083             SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, pData->backgroundItems[pData->backgroundSelection].szFilename, SPIF_UPDATEINIFILE);
1084             return;
1085         }
1086 
1087         if (FAILED(GdipGetEncoderClsid(L"image/bmp", &encoderClsid)))
1088         {
1089             GdipDisposeImage(image);
1090             RegCloseKey(regKey);
1091             return;
1092         }
1093 
1094         status = GdipSaveImageToFile(image, szWallpaper, &encoderClsid, NULL);
1095 
1096         GdipDisposeImage(image);
1097 
1098         if (status != Ok)
1099         {
1100             RegCloseKey(regKey);
1101             return;
1102         }
1103 
1104         if (SUCCEEDED(StringCchLength(pData->backgroundItems[pData->backgroundSelection].szFilename, MAX_PATH, &length)))
1105         {
1106             RegSetValueEx(regKey,
1107                           TEXT("ConvertedWallpaper"),
1108                           0,
1109                           REG_SZ,
1110                           (LPBYTE)pData->backgroundItems[pData->backgroundSelection].szFilename,
1111                           (DWORD)((length + 1) * sizeof(TCHAR)));
1112         }
1113 
1114         if (SUCCEEDED(StringCchLength(szWallpaper, MAX_PATH, &length)))
1115         {
1116             RegSetValueEx(regKey,
1117                           TEXT("OriginalWallpaper"),
1118                           0,
1119                           REG_SZ,
1120                           (LPBYTE)szWallpaper,
1121                           (DWORD)((length + 1) * sizeof(TCHAR)));
1122         }
1123 
1124         SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, szWallpaper, SPIF_UPDATEINIFILE);
1125     }
1126     else
1127     {
1128         SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (void*) TEXT(""), SPIF_UPDATEINIFILE);
1129     }
1130 
1131     RegCloseKey(regKey);
1132 }
1133 
1134 
1135 /* Change system color */
1136 static VOID
1137 SetDesktopBackColor(HWND hwndDlg, PBACKGROUND_DATA pData)
1138 {
1139     HKEY hKey;
1140     INT iElement = COLOR_BACKGROUND;
1141     TCHAR clText[16];
1142     BYTE red, green, blue;
1143 
1144     if (!SetSysColors(1, &iElement, &g_GlobalData.desktop_color))
1145     {
1146         /* FIXME: these error texts can need internationalization? */
1147         MessageBox(hwndDlg, TEXT("SetSysColor() failed!"),
1148             TEXT("Error!"), MB_ICONSTOP );
1149     }
1150 
1151     if (RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Colors"), 0, NULL,
1152                        REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL) != ERROR_SUCCESS)
1153     {
1154         return;
1155     }
1156 
1157     red   = GetRValue(g_GlobalData.desktop_color);
1158     green = GetGValue(g_GlobalData.desktop_color);
1159     blue  = GetBValue(g_GlobalData.desktop_color);
1160 
1161     /* Format string to be set to registry */
1162     StringCbPrintf(clText, sizeof(clText), TEXT("%d %d %d"), red, green, blue);
1163     RegSetValueEx(hKey, TEXT("Background"), 0, REG_SZ, (LPBYTE)clText,
1164                   (wcslen(clText) + 1) * sizeof(TCHAR));
1165 
1166     RegCloseKey(hKey);
1167 }
1168 
1169 INT_PTR CALLBACK
1170 BackgroundPageProc(HWND hwndDlg,
1171                    UINT uMsg,
1172                    WPARAM wParam,
1173                    LPARAM lParam)
1174 {
1175     PBACKGROUND_DATA pData;
1176     struct GdiplusStartupInput gdipStartup;
1177 
1178     pData = (PBACKGROUND_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
1179 
1180     switch (uMsg)
1181     {
1182         case WM_INITDIALOG:
1183             pData = (PBACKGROUND_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BACKGROUND_DATA));
1184             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pData);
1185             gdipStartup.GdiplusVersion = 1;
1186             gdipStartup.DebugEventCallback = NULL;
1187             gdipStartup.SuppressBackgroundThread = FALSE;
1188             gdipStartup.SuppressExternalCodecs = FALSE;
1189             GdiplusStartup(&pData->gdipToken, &gdipStartup, NULL);
1190             InitBackgroundDialog(hwndDlg, pData);
1191             break;
1192 
1193         case WM_COMMAND:
1194             {
1195                 DWORD controlId = LOWORD(wParam);
1196                 DWORD command   = HIWORD(wParam);
1197 
1198                 switch (controlId)
1199                 {
1200                     case IDC_COLOR_BUTTON:
1201                         if (command == BN_CLICKED)
1202                             OnColorButton(hwndDlg, pData);
1203                         break;
1204 
1205                     case IDC_BROWSE_BUTTON:
1206                         if (command == BN_CLICKED)
1207                             OnBrowseButton(hwndDlg, pData);
1208                         break;
1209 
1210                     case IDC_PLACEMENT_COMBO:
1211                         if (command == CBN_SELCHANGE)
1212                         {
1213                             pData->placementSelection = (int)SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_GETCURSEL, 0, 0);
1214 
1215                             InvalidateRect(GetDlgItem(hwndDlg, IDC_BACKGROUND_PREVIEW), NULL, TRUE);
1216 
1217                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1218                         }
1219                         break;
1220                 }
1221             } break;
1222 
1223         case WM_DRAWITEM:
1224             {
1225                 LPDRAWITEMSTRUCT drawItem;
1226                 drawItem = (LPDRAWITEMSTRUCT)lParam;
1227 
1228                 if (drawItem->CtlID == IDC_BACKGROUND_PREVIEW)
1229                 {
1230                     DrawBackgroundPreview(drawItem, pData);
1231                 }
1232 
1233             }
1234             break;
1235 
1236         case WM_NOTIFY:
1237             {
1238                 LPNMHDR lpnm = (LPNMHDR)lParam;
1239 
1240                 switch(lpnm->code)
1241                 {
1242                     case PSN_APPLY:
1243                         if (pData->bWallpaperChanged)
1244                             SetWallpaper(pData);
1245                         if (pData->bClrBackgroundChanged)
1246                             SetDesktopBackColor(hwndDlg, pData);
1247                         SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)_T(""));
1248                         return TRUE;
1249 
1250                     case LVN_ITEMCHANGED:
1251                         {
1252                             LPNMLISTVIEW nm = (LPNMLISTVIEW)lParam;
1253 
1254                             if ((nm->uNewState & LVIS_SELECTED) == 0)
1255                                 return FALSE;
1256 
1257                             ListViewItemChanged(hwndDlg, pData, nm->iItem);
1258                         }
1259                         break;
1260                 }
1261             }
1262             break;
1263 
1264         case WM_DESTROY:
1265             if (pData->pWallpaperBitmap != NULL)
1266                 DibFreeImage(pData->pWallpaperBitmap);
1267 
1268             DeleteObject(pData->hBitmap);
1269             GdiplusShutdown(pData->gdipToken);
1270             HeapFree(GetProcessHeap(), 0, pData);
1271             break;
1272     }
1273 
1274     return FALSE;
1275 }
1276