reactos/dll/cpl/desk/background.c
2020-01-28 22:24:30 +09:00

1274 lines
41 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Display Control Panel
* FILE: dll/cpl/desk/background.c
* PURPOSE: Background property page
*
* PROGRAMMERS: Trevor McCort (lycan359@gmail.com)
* Alexey Minnekhanov (minlexx@rambler.ru)
*/
#include "desk.h"
#include <shellapi.h>
#include <shlwapi.h>
#define MAX_BACKGROUNDS 100
typedef enum
{
PLACEMENT_CENTER = 0,
PLACEMENT_STRETCH,
PLACEMENT_TILE,
PLACEMENT_FIT,
PLACEMENT_FILL
} PLACEMENT;
/* The tile placement is stored in different registry
* key, but due to a condition in win32k it needs to be
* zero when stored in the same key as others.
*/
typedef enum
{
PLACEMENT_VALUE_CENTER = 0,
PLACEMENT_VALUE_STRETCH = 2,
PLACEMENT_VALUE_TILE = 0,
PLACEMENT_VALUE_FIT = 6,
PLACEMENT_VALUE_FILL = 10
} PLACEMENT_VALUE;
/* The values in these macros are dependent on the
* layout of the monitor image and they must be adjusted
* if that image will be changed.
*/
#define MONITOR_LEFT 20
#define MONITOR_TOP 8
#define MONITOR_RIGHT 140
#define MONITOR_BOTTOM 92
#define MONITOR_WIDTH (MONITOR_RIGHT-MONITOR_LEFT)
#define MONITOR_HEIGHT (MONITOR_BOTTOM-MONITOR_TOP)
typedef struct
{
BOOL bWallpaper; /* Is this background a wallpaper */
TCHAR szFilename[MAX_PATH];
TCHAR szDisplayName[256];
} BackgroundItem;
typedef struct _BACKGROUND_DATA
{
BOOL bWallpaperChanged;
BOOL bClrBackgroundChanged;
BackgroundItem backgroundItems[MAX_BACKGROUNDS];
PDIBITMAP pWallpaperBitmap;
int placementSelection;
int backgroundSelection;
COLORREF custom_colors[16];
int listViewItemCount;
HBITMAP hBitmap;
int cxSource;
int cySource;
ULONG_PTR gdipToken;
} BACKGROUND_DATA, *PBACKGROUND_DATA;
GLOBAL_DATA g_GlobalData;
HRESULT
GdipGetEncoderClsid(PCWSTR MimeType, CLSID *pClsid)
{
UINT num;
UINT size;
UINT i;
ImageCodecInfo *codecInfo;
if (GdipGetImageEncodersSize(&num, &size) != Ok ||
size == 0)
{
return E_FAIL;
}
codecInfo = HeapAlloc(GetProcessHeap(), 0, size);
if (!codecInfo)
{
return E_OUTOFMEMORY;
}
if (GdipGetImageEncoders(num, size, codecInfo) != Ok)
{
HeapFree(GetProcessHeap(), 0, codecInfo);
return E_FAIL;
}
for (i = 0; i < num; i++)
{
if (!_wcsicmp(codecInfo[i].MimeType, MimeType))
{
*pClsid = codecInfo[i].Clsid;
HeapFree(GetProcessHeap(), 0, codecInfo);
return S_OK;
}
}
HeapFree(GetProcessHeap(), 0, codecInfo);
return E_FAIL;
}
LPWSTR
GdipGetSupportedFileExtensions(VOID)
{
ImageCodecInfo *codecInfo;
UINT num;
UINT size;
UINT i;
LPWSTR lpBuffer = NULL;
if (GdipGetImageDecodersSize(&num, &size) != Ok ||
size == 0)
{
return NULL;
}
codecInfo = HeapAlloc(GetProcessHeap(), 0, size);
if (!codecInfo)
{
return NULL;
}
if (GdipGetImageDecoders(num, size, codecInfo) != Ok)
{
HeapFree(GetProcessHeap(), 0, codecInfo);
return NULL;
}
size = 0;
for (i = 0; i < num; ++i)
{
size = size + (UINT)wcslen(codecInfo[i].FilenameExtension) + 1;
}
size = (size + 1) * sizeof(WCHAR);
lpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
if (!lpBuffer)
{
HeapFree(GetProcessHeap(), 0, codecInfo);
return NULL;
}
for (i = 0; i < num; ++i)
{
StringCbCatW(lpBuffer, size, codecInfo[i].FilenameExtension);
if (i < (num - 1))
{
StringCbCatW(lpBuffer, size, L";");
}
}
HeapFree(GetProcessHeap(), 0, codecInfo);
return lpBuffer;
}
static UINT
AddWallpapersFromDirectory(UINT uCounter, HWND hwndBackgroundList, BackgroundItem *backgroundItem, PBACKGROUND_DATA pData, LPCTSTR wallpaperFilename, LPCTSTR wallpaperDirectory)
{
WIN32_FIND_DATA fd;
HANDLE hFind;
TCHAR szSearchPath[MAX_PATH];
LPTSTR szFileTypes = NULL;
TCHAR separators[] = TEXT(";");
TCHAR *token;
HRESULT hr;
SHFILEINFO sfi;
UINT i = uCounter;
LV_ITEM listItem;
HIMAGELIST himl;
szFileTypes = GdipGetSupportedFileExtensions();
if (!szFileTypes)
{
return i;
}
himl = ListView_GetImageList(hwndBackgroundList, LVSIL_SMALL);
token = _tcstok(szFileTypes, separators);
while (token != NULL)
{
if (!PathCombine(szSearchPath, wallpaperDirectory, token))
{
HeapFree(GetProcessHeap(), 0, szFileTypes);
return i;
}
hFind = FindFirstFile(szSearchPath, &fd);
while (hFind != INVALID_HANDLE_VALUE)
{
TCHAR filename[MAX_PATH];
if (!PathCombine(filename, wallpaperDirectory, fd.cFileName))
{
FindClose(hFind);
HeapFree(GetProcessHeap(), 0, szFileTypes);
return i;
}
/* Don't add any hidden bitmaps. Also don't add current wallpaper once more. */
if (((fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0) && (_tcsicmp(wallpaperFilename, filename) != 0))
{
SHGetFileInfo(filename,
0,
&sfi,
sizeof(sfi),
SHGFI_ICON | SHGFI_SMALLICON | SHGFI_DISPLAYNAME);
sfi.iIcon = ImageList_AddIcon(himl, sfi.hIcon);
i++;
backgroundItem = &pData->backgroundItems[pData->listViewItemCount];
backgroundItem->bWallpaper = TRUE;
hr = StringCbCopy(backgroundItem->szDisplayName, sizeof(backgroundItem->szDisplayName), sfi.szDisplayName);
if (FAILED(hr))
{
FindClose(hFind);
HeapFree(GetProcessHeap(), 0, szFileTypes);
return i;
}
PathRemoveExtension(backgroundItem->szDisplayName);
hr = StringCbCopy(backgroundItem->szFilename, sizeof(backgroundItem->szFilename), filename);
if (FAILED(hr))
{
FindClose(hFind);
HeapFree(GetProcessHeap(), 0, szFileTypes);
return i;
}
ZeroMemory(&listItem, sizeof(LV_ITEM));
listItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
listItem.pszText = backgroundItem->szDisplayName;
listItem.state = 0;
listItem.iImage = sfi.iIcon;
listItem.iItem = pData->listViewItemCount;
listItem.lParam = pData->listViewItemCount;
(void)ListView_InsertItem(hwndBackgroundList, &listItem);
pData->listViewItemCount++;
}
if (!FindNextFile(hFind, &fd))
break;
}
token = _tcstok(NULL, separators);
FindClose(hFind);
}
HeapFree(GetProcessHeap(), 0, szFileTypes);
return i;
}
/* Add the images in the C:\ReactOS, the wallpaper directory and the current wallpaper if any */
static VOID
AddListViewItems(HWND hwndDlg, PBACKGROUND_DATA pData)
{
TCHAR szSearchPath[MAX_PATH];
LV_ITEM listItem;
LV_COLUMN dummy;
RECT clientRect;
HKEY regKey;
SHFILEINFO sfi;
HIMAGELIST himl;
TCHAR wallpaperFilename[MAX_PATH];
TCHAR originalWallpaper[MAX_PATH];
DWORD bufferSize = sizeof(wallpaperFilename);
TCHAR buffer[MAX_PATH];
DWORD varType = REG_SZ;
LONG result;
UINT i = 0;
BackgroundItem *backgroundItem = NULL;
HWND hwndBackgroundList;
HRESULT hr;
HICON hIcon;
INT cx, cy;
HINSTANCE hShell32;
hwndBackgroundList = GetDlgItem(hwndDlg, IDC_BACKGROUND_LIST);
GetClientRect(hwndBackgroundList, &clientRect);
cx = GetSystemMetrics(SM_CXSMICON);
cy = GetSystemMetrics(SM_CYSMICON);
himl = ImageList_Create(cx, cy, ILC_COLOR32 | ILC_MASK, 0, 0);
/* Load (None) icon */
#define IDI_SHELL_NO 200
hShell32 = GetModuleHandleW(L"shell32.dll");
hIcon = (HICON)LoadImageW(hShell32, MAKEINTRESOURCEW(IDI_SHELL_NO), IMAGE_ICON, cx, cy, 0);
#undef IDI_SHELL_NO
ListView_SetImageList(hwndBackgroundList, himl, LVSIL_SMALL);
/* Add a new column to the list */
ZeroMemory(&dummy, sizeof(LV_COLUMN));
dummy.mask = LVCF_SUBITEM | LVCF_WIDTH;
dummy.iSubItem = 0;
dummy.cx = (clientRect.right - clientRect.left) - GetSystemMetrics(SM_CXVSCROLL);
(void)ListView_InsertColumn(hwndBackgroundList, 0, &dummy);
/* Add the "None" item */
backgroundItem = &pData->backgroundItems[pData->listViewItemCount];
backgroundItem->bWallpaper = FALSE;
LoadString(hApplet,
IDS_NONE,
backgroundItem->szDisplayName,
sizeof(backgroundItem->szDisplayName) / sizeof(TCHAR));
ZeroMemory(&listItem, sizeof(LV_ITEM));
listItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
listItem.state = 0;
listItem.pszText = backgroundItem->szDisplayName;
listItem.iImage = ImageList_AddIcon(himl, hIcon);
listItem.iItem = pData->listViewItemCount;
listItem.lParam = pData->listViewItemCount;
hIcon = NULL;
(void)ListView_InsertItem(hwndBackgroundList, &listItem);
ListView_SetItemState(hwndBackgroundList,
pData->listViewItemCount,
LVIS_SELECTED,
LVIS_SELECTED);
pData->listViewItemCount++;
/* Add current wallpaper if any */
result = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), 0, KEY_QUERY_VALUE, &regKey);
if (result == ERROR_SUCCESS)
{
result = RegQueryValueEx(regKey, TEXT("Wallpaper"), 0, &varType, (LPBYTE)wallpaperFilename, &bufferSize);
if ((result == ERROR_SUCCESS) && (_tcslen(wallpaperFilename) > 0))
{
bufferSize = sizeof(originalWallpaper);
result = RegQueryValueEx(regKey, TEXT("OriginalWallpaper"), 0, &varType, (LPBYTE)originalWallpaper, &bufferSize);
/* If Wallpaper and OriginalWallpaper are the same, try to retrieve ConvertedWallpaper and use it instead of Wallpaper */
if ((result == ERROR_SUCCESS) && (_tcslen(originalWallpaper) > 0) && (_tcsicmp(wallpaperFilename, originalWallpaper) == 0))
{
bufferSize = sizeof(originalWallpaper);
result = RegQueryValueEx(regKey, TEXT("ConvertedWallpaper"), 0, &varType, (LPBYTE)originalWallpaper, &bufferSize);
if ((result == ERROR_SUCCESS) && (_tcslen(originalWallpaper) > 0))
{
hr = StringCbCopy(wallpaperFilename, sizeof(wallpaperFilename), originalWallpaper);
if (FAILED(hr))
{
RegCloseKey(regKey);
return;
}
}
}
/* Allow environment variables in file name */
if (ExpandEnvironmentStrings(wallpaperFilename, buffer, MAX_PATH))
{
hr = StringCbCopy(wallpaperFilename, sizeof(wallpaperFilename), buffer);
if (FAILED(hr))
{
RegCloseKey(regKey);
return;
}
}
SHGetFileInfoW(wallpaperFilename,
0,
&sfi,
sizeof(sfi),
SHGFI_ICON | SHGFI_SMALLICON |
SHGFI_DISPLAYNAME);
sfi.iIcon = ImageList_AddIcon(himl, sfi.hIcon);
i++;
backgroundItem = &pData->backgroundItems[pData->listViewItemCount];
backgroundItem->bWallpaper = TRUE;
hr = StringCbCopy(backgroundItem->szDisplayName, sizeof(backgroundItem->szDisplayName), sfi.szDisplayName);
if (FAILED(hr))
{
RegCloseKey(regKey);
return;
}
PathRemoveExtension(backgroundItem->szDisplayName);
hr = StringCbCopy(backgroundItem->szFilename, sizeof(backgroundItem->szFilename), wallpaperFilename);
if (FAILED(hr))
{
RegCloseKey(regKey);
return;
}
ZeroMemory(&listItem, sizeof(LV_ITEM));
listItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
listItem.state = 0;
listItem.pszText = backgroundItem->szDisplayName;
listItem.iImage = sfi.iIcon;
listItem.iItem = pData->listViewItemCount;
listItem.lParam = pData->listViewItemCount;
(void)ListView_InsertItem(hwndBackgroundList, &listItem);
ListView_SetItemState(hwndBackgroundList,
pData->listViewItemCount,
LVIS_SELECTED,
LVIS_SELECTED);
pData->listViewItemCount++;
}
RegCloseKey(regKey);
}
/* Add all the images in the C:\ReactOS directory. */
if (GetWindowsDirectory(szSearchPath, MAX_PATH))
{
i = AddWallpapersFromDirectory(i, hwndBackgroundList, backgroundItem, pData, wallpaperFilename, szSearchPath);
}
/* Add all the images in the wallpaper directory. */
if (SHRegGetPath(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"), TEXT("WallPaperDir"), szSearchPath, 0) == ERROR_SUCCESS)
{
i = AddWallpapersFromDirectory(i, hwndBackgroundList, backgroundItem, pData, wallpaperFilename, szSearchPath);
}
}
static VOID
InitBackgroundDialog(HWND hwndDlg, PBACKGROUND_DATA pData)
{
TCHAR szString[256];
HKEY regKey;
TCHAR szBuffer[3];
DWORD bufferSize = sizeof(szBuffer);
BITMAP bitmap;
AddListViewItems(hwndDlg, pData);
LoadString(hApplet, IDS_CENTER, szString, sizeof(szString) / sizeof(TCHAR));
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_CENTER, (LPARAM)szString);
LoadString(hApplet, IDS_STRETCH, szString, sizeof(szString) / sizeof(TCHAR));
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_STRETCH, (LPARAM)szString);
LoadString(hApplet, IDS_TILE, szString, sizeof(szString) / sizeof(TCHAR));
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_TILE, (LPARAM)szString);
LoadString(hApplet, IDS_FIT, szString, sizeof(szString) / sizeof(TCHAR));
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_FIT, (LPARAM)szString);
LoadString(hApplet, IDS_FILL, szString, sizeof(szString) / sizeof(TCHAR));
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_INSERTSTRING, PLACEMENT_FILL, (LPARAM)szString);
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_CENTER, 0);
pData->placementSelection = PLACEMENT_CENTER;
pData->hBitmap = (HBITMAP) LoadImage(hApplet, MAKEINTRESOURCE(IDC_MONITOR), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
if (pData->hBitmap != NULL)
{
GetObject(pData->hBitmap, sizeof(BITMAP), &bitmap);
pData->cxSource = bitmap.bmWidth;
pData->cySource = bitmap.bmHeight;
}
/* Load the default settings from the registry */
if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), 0, KEY_QUERY_VALUE, &regKey) != ERROR_SUCCESS)
{
return;
}
if (RegQueryValueEx(regKey, TEXT("WallpaperStyle"), 0, NULL, (LPBYTE)szBuffer, &bufferSize) == ERROR_SUCCESS)
{
if (_ttoi(szBuffer) == PLACEMENT_VALUE_CENTER)
{
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_CENTER, 0);
pData->placementSelection = PLACEMENT_CENTER;
}
if (_ttoi(szBuffer) == PLACEMENT_VALUE_STRETCH)
{
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_STRETCH, 0);
pData->placementSelection = PLACEMENT_STRETCH;
}
if (_ttoi(szBuffer) == PLACEMENT_VALUE_FIT)
{
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_FIT, 0);
pData->placementSelection = PLACEMENT_FIT;
}
if (_ttoi(szBuffer) == PLACEMENT_VALUE_FILL)
{
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_FILL, 0);
pData->placementSelection = PLACEMENT_FILL;
}
}
if (RegQueryValueEx(regKey, TEXT("TileWallpaper"), 0, NULL, (LPBYTE)szBuffer, &bufferSize) == ERROR_SUCCESS)
{
if (_ttoi(szBuffer) == 1)
{
SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_SETCURSEL, PLACEMENT_TILE, 0);
pData->placementSelection = PLACEMENT_TILE;
}
}
RegCloseKey(regKey);
}
static VOID
OnColorButton(HWND hwndDlg, PBACKGROUND_DATA pData)
{
/* Load custom colors from Registry */
HKEY hKey = NULL;
LONG res = ERROR_SUCCESS;
CHOOSECOLOR cc;
res = RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Appearance"), 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hKey, NULL);
/* Now the key is either created or opened existing, if res == ERROR_SUCCESS */
if (res == ERROR_SUCCESS)
{
/* Key opened */
DWORD dwType = REG_BINARY;
DWORD cbData = sizeof(pData->custom_colors);
res = RegQueryValueEx(hKey, TEXT("CustomColors"), NULL, &dwType,
(LPBYTE)pData->custom_colors, &cbData);
RegCloseKey(hKey);
hKey = NULL;
}
/* Launch ChooseColor() dialog */
cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = hwndDlg;
cc.hInstance = NULL;
cc.rgbResult = g_GlobalData.desktop_color;
cc.lpCustColors = pData->custom_colors;
cc.Flags = CC_ANYCOLOR | /* Causes the dialog box to display all available colors in the set of basic colors. */
CC_FULLOPEN | /* opens dialog in full size */
CC_RGBINIT ; /* init chosen color by rgbResult value */
cc.lCustData = 0;
cc.lpfnHook = NULL;
cc.lpTemplateName = NULL;
if (ChooseColor(&cc))
{
/* Save selected color to var */
g_GlobalData.desktop_color = cc.rgbResult;
pData->bClrBackgroundChanged = TRUE;
/* Apply button will be activated */
PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
/* Window will be updated :) */
InvalidateRect(GetDlgItem(hwndDlg, IDC_BACKGROUND_PREVIEW), NULL, TRUE);
/* Save custom colors to reg. To this moment key must be created already. See above */
res = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Appearance"), 0,
KEY_SET_VALUE, &hKey);
if (res == ERROR_SUCCESS)
{
/* Key opened */
RegSetValueEx(hKey, TEXT("CustomColors"), 0, REG_BINARY,
(LPBYTE)pData->custom_colors, sizeof(pData->custom_colors));
RegCloseKey(hKey);
hKey = NULL;
}
}
}
/*
* ListView_FindItem() Macro: Searches for a list-view item with the specified
* characteristics. Returns the index of the item if successful, or -1 otherwise
*/
static BOOL
CheckListViewFilenameExists(HWND hwndList, LPCTSTR tszFileName)
{
LVFINDINFO lvfi;
int retVal;
lvfi.flags = LVFI_STRING; /* Search item by EXACT string */
lvfi.psz = tszFileName; /* String to search */
/* Other items of this structure are not valid, besacuse flags are not set. */
retVal = ListView_FindItem(hwndList, -1, &lvfi);
if (retVal != -1)
return TRUE; /* item found! */
return FALSE; /* item not found. */
}
static VOID
OnBrowseButton(HWND hwndDlg, PBACKGROUND_DATA pData)
{
OPENFILENAME ofn;
TCHAR filename[MAX_PATH];
TCHAR fileTitle[256];
LPTSTR filter;
LPTSTR extensions;
BackgroundItem *backgroundItem = NULL;
SHFILEINFO sfi;
LV_ITEM listItem;
HWND hwndBackgroundList;
TCHAR *p;
HRESULT hr;
TCHAR filterdesc[MAX_PATH];
TCHAR *c;
size_t sizeRemain;
SIZE_T buffersize;
BOOL success;
HIMAGELIST himl;
hwndBackgroundList = GetDlgItem(hwndDlg, IDC_BACKGROUND_LIST);
himl = ListView_GetImageList(hwndBackgroundList, LVSIL_SMALL);
ZeroMemory(&ofn, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwndDlg;
ofn.lpstrFile = filename;
LoadString(hApplet, IDS_BACKGROUND_COMDLG_FILTER, filterdesc, sizeof(filterdesc) / sizeof(TCHAR));
extensions = GdipGetSupportedFileExtensions();
if (!extensions)
{
return;
}
buffersize = (_tcslen(extensions) * 2 + 6) * sizeof(TCHAR) + sizeof(filterdesc);
filter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffersize);
if (!filter)
{
HeapFree(GetProcessHeap(), 0, extensions);
return;
}
sizeRemain = buffersize;
c = filter;
if (FAILED(StringCbPrintfEx(c, sizeRemain, &c, &sizeRemain, 0, L"%ls (%ls)", filterdesc, extensions)))
{
HeapFree(GetProcessHeap(), 0, extensions);
HeapFree(GetProcessHeap(), 0, filter);
return;
}
c++;
sizeRemain -= sizeof(*c);
if (FAILED(StringCbPrintfEx(c, sizeRemain, &c, &sizeRemain, 0, L"%ls", extensions)))
{
HeapFree(GetProcessHeap(), 0, extensions);
HeapFree(GetProcessHeap(), 0, filter);
return;
}
HeapFree(GetProcessHeap(), 0, extensions);
/* Set lpstrFile[0] to '\0' so that GetOpenFileName does not
* use the contents of szFile to initialize itself */
ofn.lpstrFile[0] = TEXT('\0');
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = filter;
ofn.nFilterIndex = 0;
ofn.lpstrFileTitle = fileTitle;
ofn.nMaxFileTitle = 256;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
success = GetOpenFileName(&ofn);
HeapFree(GetProcessHeap(), 0, filter);
if (success)
{
/* Check if there is already a entry that holds this filename */
if (CheckListViewFilenameExists(hwndBackgroundList, ofn.lpstrFileTitle) != FALSE)
return;
if (pData->listViewItemCount > (MAX_BACKGROUNDS - 1))
return;
SHGetFileInfo(filename,
0,
&sfi,
sizeof(sfi),
SHGFI_ICON | SHGFI_SMALLICON | SHGFI_DISPLAYNAME);
sfi.iIcon = ImageList_AddIcon(himl, sfi.hIcon);
backgroundItem = &pData->backgroundItems[pData->listViewItemCount];
backgroundItem->bWallpaper = TRUE;
hr = StringCbCopy(backgroundItem->szDisplayName, sizeof(backgroundItem->szDisplayName), sfi.szDisplayName);
if (FAILED(hr))
return;
p = _tcsrchr(backgroundItem->szDisplayName, _T('.'));
if (p)
*p = (TCHAR)0;
hr = StringCbCopy(backgroundItem->szFilename, sizeof(backgroundItem->szFilename), filename);
if (FAILED(hr))
return;
ZeroMemory(&listItem, sizeof(LV_ITEM));
listItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
listItem.state = 0;
listItem.pszText = backgroundItem->szDisplayName;
listItem.iImage = sfi.iIcon;
listItem.iItem = pData->listViewItemCount;
listItem.lParam = pData->listViewItemCount;
(void)ListView_InsertItem(hwndBackgroundList, &listItem);
ListView_SetItemState(hwndBackgroundList,
pData->listViewItemCount,
LVIS_SELECTED,
LVIS_SELECTED);
SendMessage(hwndBackgroundList, WM_VSCROLL, SB_BOTTOM, 0);
pData->listViewItemCount++;
}
}
static VOID
ListViewItemChanged(HWND hwndDlg, PBACKGROUND_DATA pData, int itemIndex)
{
BackgroundItem *backgroundItem = NULL;
pData->backgroundSelection = itemIndex;
backgroundItem = &pData->backgroundItems[pData->backgroundSelection];
if (pData->pWallpaperBitmap != NULL)
{
DibFreeImage(pData->pWallpaperBitmap);
pData->pWallpaperBitmap = NULL;
}
if (backgroundItem->bWallpaper != FALSE)
{
pData->pWallpaperBitmap = DibLoadImage(backgroundItem->szFilename);
if (pData->pWallpaperBitmap == NULL)
return;
}
pData->bWallpaperChanged = TRUE;
InvalidateRect(GetDlgItem(hwndDlg, IDC_BACKGROUND_PREVIEW),
NULL, TRUE);
EnableWindow(GetDlgItem(hwndDlg, IDC_PLACEMENT_COMBO),
backgroundItem->bWallpaper);
PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
}
static VOID
DrawBackgroundPreview(LPDRAWITEMSTRUCT draw, PBACKGROUND_DATA pData)
{
float scaleX;
float scaleY;
int scaledWidth;
int scaledHeight;
int posX, desX;
int posY, desY;
int fitFillScaleNum, fitFillScaleDen;
int fitFillWidth, fitFillHeight;
HBRUSH hBrush;
int x;
int y;
HDC hDC;
HGDIOBJ hOldObj;
RECT rcItem = {
MONITOR_LEFT,
MONITOR_TOP,
MONITOR_RIGHT,
MONITOR_BOTTOM
};
hDC = CreateCompatibleDC(draw->hDC);
hOldObj = SelectObject(hDC, pData->hBitmap);
if (pData->backgroundItems[pData->backgroundSelection].bWallpaper == FALSE)
{
/* Update desktop background color image */
hBrush = CreateSolidBrush(g_GlobalData.desktop_color);
FillRect(hDC, &rcItem, hBrush);
DeleteObject(hBrush);
}
else
if (pData->pWallpaperBitmap != NULL)
{
scaleX = ((float)GetSystemMetrics(SM_CXSCREEN) - 1) / (float)MONITOR_WIDTH;
scaleY = ((float)GetSystemMetrics(SM_CYSCREEN) - 1) / (float)MONITOR_HEIGHT;
scaledWidth = (int)(pData->pWallpaperBitmap->width / scaleX);
scaledHeight = (int)(pData->pWallpaperBitmap->height / scaleY);
FillRect(hDC, &rcItem, GetSysColorBrush(COLOR_BACKGROUND));
SetStretchBltMode(hDC, COLORONCOLOR);
switch (pData->placementSelection)
{
case PLACEMENT_CENTER:
posX = (MONITOR_WIDTH - scaledWidth + 1) / 2;
posY = (MONITOR_HEIGHT - scaledHeight + 1) / 2;
desX = 0;
desY = 0;
if (posX < 0) { desX = -posX / 2; posX = 0; }
if (posY < 0) { desY = -posY / 2; posY = 0; }
if (scaledWidth > MONITOR_WIDTH)
scaledWidth = MONITOR_WIDTH;
if (scaledHeight > MONITOR_HEIGHT)
scaledHeight = MONITOR_HEIGHT;
StretchDIBits(hDC,
MONITOR_LEFT+posX,
MONITOR_TOP+posY,
scaledWidth,
scaledHeight,
desX,
desY,
pData->pWallpaperBitmap->width - (int)(desX * scaleX),
pData->pWallpaperBitmap->height - (int)(desY * scaleY),
pData->pWallpaperBitmap->bits,
pData->pWallpaperBitmap->info,
DIB_RGB_COLORS,
SRCCOPY);
break;
case PLACEMENT_STRETCH:
StretchDIBits(hDC,
MONITOR_LEFT,
MONITOR_TOP,
MONITOR_WIDTH,
MONITOR_HEIGHT,
0,
0,
pData->pWallpaperBitmap->width,
pData->pWallpaperBitmap->height,
pData->pWallpaperBitmap->bits,
pData->pWallpaperBitmap->info,
DIB_RGB_COLORS,
SRCCOPY);
break;
case PLACEMENT_TILE:
for (y = 0; y < MONITOR_HEIGHT; y += scaledHeight)
{
for (x = 0; x < MONITOR_WIDTH; x += scaledWidth)
{
if ((MONITOR_WIDTH-x) >= scaledWidth)
posX = scaledWidth;
else
posX = MONITOR_WIDTH-x;
if ((MONITOR_HEIGHT-y) >= scaledHeight)
posY = scaledHeight;
else
posY = MONITOR_HEIGHT-y;
StretchDIBits(hDC,
MONITOR_LEFT + x,
MONITOR_TOP + y,
posX,
posY,
0,
0,
pData->pWallpaperBitmap->width * posX / scaledWidth,
pData->pWallpaperBitmap->height * posY / scaledHeight,
pData->pWallpaperBitmap->bits,
pData->pWallpaperBitmap->info,
DIB_RGB_COLORS,
SRCCOPY);
}
}
break;
case PLACEMENT_FIT:
if ((MONITOR_WIDTH * scaledHeight) <= (MONITOR_HEIGHT * scaledWidth))
{
fitFillScaleNum = MONITOR_WIDTH;
fitFillScaleDen = scaledWidth;
}
else
{
fitFillScaleNum = MONITOR_HEIGHT;
fitFillScaleDen = scaledHeight;
}
fitFillWidth = MulDiv(scaledWidth, fitFillScaleNum, fitFillScaleDen);
fitFillHeight = MulDiv(scaledHeight, fitFillScaleNum, fitFillScaleDen);
posX = (MONITOR_WIDTH - fitFillWidth) / 2;
posY = (MONITOR_HEIGHT - fitFillHeight) / 2;
StretchDIBits(hDC,
MONITOR_LEFT + posX,
MONITOR_TOP + posY,
fitFillWidth,
fitFillHeight,
0,
0,
pData->pWallpaperBitmap->width,
pData->pWallpaperBitmap->height,
pData->pWallpaperBitmap->bits,
pData->pWallpaperBitmap->info,
DIB_RGB_COLORS,
SRCCOPY);
break;
case PLACEMENT_FILL:
if ((MONITOR_WIDTH * scaledHeight) > (MONITOR_HEIGHT * scaledWidth))
{
fitFillScaleNum = MONITOR_WIDTH;
fitFillScaleDen = scaledWidth;
}
else
{
fitFillScaleNum = MONITOR_HEIGHT;
fitFillScaleDen = scaledHeight;
}
fitFillWidth = MulDiv(scaledWidth, fitFillScaleNum, fitFillScaleDen);
fitFillHeight = MulDiv(scaledHeight, fitFillScaleNum, fitFillScaleDen);
desX = (((fitFillWidth - MONITOR_WIDTH) * pData->pWallpaperBitmap->width) / (2 * fitFillWidth));
desY = (((fitFillHeight - MONITOR_HEIGHT) * pData->pWallpaperBitmap->height) / (2 * fitFillHeight));
StretchDIBits(hDC,
MONITOR_LEFT,
MONITOR_TOP,
MONITOR_WIDTH,
MONITOR_HEIGHT,
desX,
desY,
(MONITOR_WIDTH * pData->pWallpaperBitmap->width) / fitFillWidth,
(MONITOR_HEIGHT * pData->pWallpaperBitmap->height) / fitFillHeight,
pData->pWallpaperBitmap->bits,
pData->pWallpaperBitmap->info,
DIB_RGB_COLORS,
SRCCOPY);
break;
}
}
GdiTransparentBlt(draw->hDC,
draw->rcItem.left, draw->rcItem.top,
draw->rcItem.right-draw->rcItem.left+1,
draw->rcItem.bottom-draw->rcItem.top+1,
hDC,
0, 0,
pData->cxSource, pData->cySource,
0xFF00FF);
SelectObject(hDC, hOldObj);
DeleteDC(hDC);
}
static VOID
SetWallpaper(PBACKGROUND_DATA pData)
{
HKEY regKey;
TCHAR szWallpaper[MAX_PATH];
GpImage *image;
CLSID encoderClsid;
GUID guidFormat;
size_t length = 0;
GpStatus status;
if (FAILED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, szWallpaper)))
{
return;
}
if (FAILED(StringCbCat(szWallpaper, sizeof(szWallpaper), TEXT("\\Wallpaper1.bmp"))))
{
return;
}
if (RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &regKey, NULL) != ERROR_SUCCESS)
{
return;
}
if (pData->placementSelection == PLACEMENT_TILE)
{
RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("1"), sizeof(TCHAR) * 2);
RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
}
if (pData->placementSelection == PLACEMENT_CENTER)
{
RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
}
if (pData->placementSelection == PLACEMENT_STRETCH)
{
RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("2"), sizeof(TCHAR) * 2);
}
if (pData->placementSelection == PLACEMENT_FIT)
{
RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("6"), sizeof(TCHAR) * 2);
}
if (pData->placementSelection == PLACEMENT_FILL)
{
RegSetValueEx(regKey, TEXT("TileWallpaper"), 0, REG_SZ, (LPBYTE)TEXT("0"), sizeof(TCHAR) * 2);
RegSetValueEx(regKey, TEXT("WallpaperStyle"), 0, REG_SZ, (LPBYTE)TEXT("10"), sizeof(TCHAR) * 3);
}
if (pData->backgroundItems[pData->backgroundSelection].bWallpaper != FALSE)
{
GdipLoadImageFromFile(pData->backgroundItems[pData->backgroundSelection].szFilename, &image);
if (!image)
{
RegCloseKey(regKey);
return;
}
GdipGetImageRawFormat(image, &guidFormat);
if (IsEqualGUID(&guidFormat, &ImageFormatBMP))
{
GdipDisposeImage(image);
RegCloseKey(regKey);
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, pData->backgroundItems[pData->backgroundSelection].szFilename, SPIF_UPDATEINIFILE);
return;
}
if (FAILED(GdipGetEncoderClsid(L"image/bmp", &encoderClsid)))
{
GdipDisposeImage(image);
RegCloseKey(regKey);
return;
}
status = GdipSaveImageToFile(image, szWallpaper, &encoderClsid, NULL);
GdipDisposeImage(image);
if (status != Ok)
{
RegCloseKey(regKey);
return;
}
if (SUCCEEDED(StringCchLength(pData->backgroundItems[pData->backgroundSelection].szFilename, MAX_PATH, &length)))
{
RegSetValueEx(regKey,
TEXT("ConvertedWallpaper"),
0,
REG_SZ,
(LPBYTE)pData->backgroundItems[pData->backgroundSelection].szFilename,
(DWORD)((length + 1) * sizeof(TCHAR)));
}
if (SUCCEEDED(StringCchLength(szWallpaper, MAX_PATH, &length)))
{
RegSetValueEx(regKey,
TEXT("OriginalWallpaper"),
0,
REG_SZ,
(LPBYTE)szWallpaper,
(DWORD)((length + 1) * sizeof(TCHAR)));
}
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, szWallpaper, SPIF_UPDATEINIFILE);
}
else
{
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (void*) TEXT(""), SPIF_UPDATEINIFILE);
}
RegCloseKey(regKey);
}
/* Change system color */
static VOID
SetDesktopBackColor(HWND hwndDlg, PBACKGROUND_DATA pData)
{
HKEY hKey;
INT iElement = COLOR_BACKGROUND;
TCHAR clText[16];
BYTE red, green, blue;
if (!SetSysColors(1, &iElement, &g_GlobalData.desktop_color))
{
/* FIXME: these error texts can need internationalization? */
MessageBox(hwndDlg, TEXT("SetSysColor() failed!"),
TEXT("Error!"), MB_ICONSTOP );
}
if (RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Colors"), 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL) != ERROR_SUCCESS)
{
return;
}
red = GetRValue(g_GlobalData.desktop_color);
green = GetGValue(g_GlobalData.desktop_color);
blue = GetBValue(g_GlobalData.desktop_color);
/* Format string to be set to registry */
StringCbPrintf(clText, sizeof(clText), TEXT("%d %d %d"), red, green, blue);
RegSetValueEx(hKey, TEXT("Background"), 0, REG_SZ, (LPBYTE)clText,
(wcslen(clText) + 1) * sizeof(TCHAR));
RegCloseKey(hKey);
}
INT_PTR CALLBACK
BackgroundPageProc(HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
PBACKGROUND_DATA pData;
struct GdiplusStartupInput gdipStartup;
pData = (PBACKGROUND_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
switch (uMsg)
{
case WM_INITDIALOG:
pData = (PBACKGROUND_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BACKGROUND_DATA));
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pData);
gdipStartup.GdiplusVersion = 1;
gdipStartup.DebugEventCallback = NULL;
gdipStartup.SuppressBackgroundThread = FALSE;
gdipStartup.SuppressExternalCodecs = FALSE;
GdiplusStartup(&pData->gdipToken, &gdipStartup, NULL);
InitBackgroundDialog(hwndDlg, pData);
break;
case WM_COMMAND:
{
DWORD controlId = LOWORD(wParam);
DWORD command = HIWORD(wParam);
switch (controlId)
{
case IDC_COLOR_BUTTON:
if (command == BN_CLICKED)
OnColorButton(hwndDlg, pData);
break;
case IDC_BROWSE_BUTTON:
if (command == BN_CLICKED)
OnBrowseButton(hwndDlg, pData);
break;
case IDC_PLACEMENT_COMBO:
if (command == CBN_SELCHANGE)
{
pData->placementSelection = (int)SendDlgItemMessage(hwndDlg, IDC_PLACEMENT_COMBO, CB_GETCURSEL, 0, 0);
InvalidateRect(GetDlgItem(hwndDlg, IDC_BACKGROUND_PREVIEW), NULL, TRUE);
PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
}
break;
}
} break;
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT drawItem;
drawItem = (LPDRAWITEMSTRUCT)lParam;
if (drawItem->CtlID == IDC_BACKGROUND_PREVIEW)
{
DrawBackgroundPreview(drawItem, pData);
}
}
break;
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch(lpnm->code)
{
case PSN_APPLY:
if (pData->bWallpaperChanged)
SetWallpaper(pData);
if (pData->bClrBackgroundChanged)
SetDesktopBackColor(hwndDlg, pData);
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)_T(""));
return TRUE;
case LVN_ITEMCHANGED:
{
LPNMLISTVIEW nm = (LPNMLISTVIEW)lParam;
if ((nm->uNewState & LVIS_SELECTED) == 0)
return FALSE;
ListViewItemChanged(hwndDlg, pData, nm->iItem);
}
break;
}
}
break;
case WM_DESTROY:
if (pData->pWallpaperBitmap != NULL)
DibFreeImage(pData->pWallpaperBitmap);
DeleteObject(pData->hBitmap);
GdiplusShutdown(pData->gdipToken);
HeapFree(GetProcessHeap(), 0, pData);
break;
}
return FALSE;
}