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