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