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