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