reactos/base/shell/progman/group.c
Oleg Dubinskiy 1a1025011f
[REACTOS] Fix LoadCursorW() incorrect usage cases (#7053)
Fix wrong MAKEINTRESOURCEW() macro usage cases in LoadCursorW() system-wide.
MSDN documentation for this function states we need to use MAKEINTRESOURCEW() macro only for internal application-defined cursors (loaded from the app instance specified by hInstance parameter), but not for system-defined cursors (those begin with IDC_* prefix), in case when hInstance is NULL.
So get rid from MAKEINTRESOURCEW() macro usage for all cases when hInstance parameter is NULL. And on the contrary, when hInstance is valid and points to the application instance, then use it for the resource identifier.
2024-07-01 18:05:46 +02:00

675 lines
23 KiB
C

/*
* Program Manager
*
* Copyright 1996 Ulrich Schmid
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* PROJECT: ReactOS Program Manager
* COPYRIGHT: GPL - See COPYING in the top level directory
* FILE: base/shell/progman/group.c
* PURPOSE: Program group files helper functions
* PROGRAMMERS: Ulrich Schmid
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
#include "progman.h"
/***********************************************************************
*
* UX Theming helpers, dropped from msconfig_new/comctl32ex/uxthemesupp.c
*
*/
static HMODULE hUxTheme = NULL;
typedef HRESULT (WINAPI* ETDTProc)(HWND, DWORD);
static ETDTProc fnEnableThemeDialogTexture = NULL;
typedef HRESULT (WINAPI* SWTProc)(HWND, LPCWSTR, LPCWSTR);
static SWTProc fnSetWindowTheme = NULL;
static BOOL
InitUxTheme(VOID)
{
if (hUxTheme) return TRUE;
hUxTheme = LoadLibraryW(L"uxtheme.dll");
if (hUxTheme == NULL) return FALSE;
fnEnableThemeDialogTexture =
(ETDTProc)GetProcAddress(hUxTheme, "EnableThemeDialogTexture");
fnSetWindowTheme =
(SWTProc)GetProcAddress(hUxTheme, "SetWindowTheme");
return TRUE;
}
#if 0
static VOID
CleanupUxTheme(VOID)
{
FreeLibrary(hUxTheme);
hUxTheme = NULL;
}
#endif
////////////////////////////////////////////////////////////////////////////////
// Taken from WinSpy++ 1.7
// http://www.catch22.net/software/winspy
// Copyright (c) 2002 by J Brown
//
HRESULT
WINAPI
EnableThemeDialogTexture(_In_ HWND hwnd,
_In_ DWORD dwFlags)
{
if (!InitUxTheme())
return HRESULT_FROM_WIN32(GetLastError());
if (!fnEnableThemeDialogTexture)
return HRESULT_FROM_WIN32(GetLastError());
return fnEnableThemeDialogTexture(hwnd, dwFlags);
}
HRESULT
WINAPI
SetWindowTheme(_In_ HWND hwnd,
_In_ LPCWSTR pszSubAppName,
_In_ LPCWSTR pszSubIdList)
{
if (!InitUxTheme())
return HRESULT_FROM_WIN32(GetLastError());
if (!fnSetWindowTheme)
return HRESULT_FROM_WIN32(GetLastError());
return fnSetWindowTheme(hwnd, pszSubAppName, pszSubIdList);
}
/***********************************************************************
*
* GROUP_GroupWndProc
*/
static
LRESULT
CALLBACK
GROUP_GroupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PROGGROUP* group;
INT iItem;
LVITEMW lvItem;
POINT pt;
group = (PROGGROUP*)GetWindowLongPtrW(hWnd, 0);
switch (uMsg)
{
case WM_NCCREATE:
{
LPCREATESTRUCTW pcs = (LPCREATESTRUCTW)lParam;
LPMDICREATESTRUCTW pMDIcs = (LPMDICREATESTRUCTW)pcs->lpCreateParams;
group = (PROGGROUP*)pMDIcs->lParam;
SetWindowLongPtrW(hWnd, 0, (LONG_PTR)group);
if (group->bIsCommonGroup)
{
DefMDIChildProcW(hWnd, WM_SETICON, ICON_BIG,
(LPARAM)CopyImage(Globals.hCommonGroupIcon,
IMAGE_ICON,
GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON),
LR_COPYFROMRESOURCE));
DefMDIChildProcW(hWnd, WM_SETICON, ICON_SMALL,
(LPARAM)CopyImage(Globals.hCommonGroupIcon,
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_COPYFROMRESOURCE));
}
else
{
DefMDIChildProcW(hWnd, WM_SETICON, ICON_BIG,
(LPARAM)CopyImage(Globals.hPersonalGroupIcon,
IMAGE_ICON,
GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON),
LR_COPYFROMRESOURCE));
DefMDIChildProcW(hWnd, WM_SETICON, ICON_SMALL,
(LPARAM)CopyImage(Globals.hPersonalGroupIcon,
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_COPYFROMRESOURCE));
}
break;
}
case WM_NCDESTROY:
SetWindowLongPtrW(hWnd, 0, 0);
break;
case WM_CREATE:
{
DWORD dwStyle;
RECT rect;
GetClientRect(hWnd, &rect);
group->hListView = CreateWindowW(WC_LISTVIEW,
NULL,
WS_CHILD | WS_VISIBLE | WS_OVERLAPPED,
0, 0,
rect.right - rect.left,
rect.bottom - rect.top,
hWnd,
NULL,
Globals.hInstance,
NULL);
dwStyle = (GetWindowLongPtrW(group->hListView, GWL_STYLE) | LVS_SHOWSELALWAYS) & ~LVS_AUTOARRANGE;
SetWindowLongPtrW(group->hListView, GWL_STYLE, dwStyle);
dwStyle = SendMessageA(group->hListView, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0) | LVS_EX_BORDERSELECT;
SendMessageA(group->hListView, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_SNAPTOGRID, dwStyle);
InitUxTheme();
SetWindowTheme(group->hListView, L"Explorer", NULL);
group->hListLarge = ImageList_Create(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), ILC_COLOR24 | ILC_MASK, 1, 1);
SendMessageA(group->hListView, LVM_SETIMAGELIST, 0, (LPARAM)group->hListLarge);
SendMessageA(group->hListView, LVM_SETICONSPACING, 0, MAKELPARAM(80, 64));
break;
}
case WM_DESTROY:
{
SendMessageA(group->hListView, LVM_SETIMAGELIST, 0, 0);
ImageList_Destroy(group->hListLarge);
DestroyWindow(group->hListView);
break;
}
case WM_SIZE:
{
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = LOWORD(lParam);
rect.bottom = HIWORD(lParam);
AdjustWindowRectEx(&rect, GetWindowLongPtrW(group->hListView, GWL_STYLE), FALSE, GetWindowLongPtrW(group->hListView, GWL_EXSTYLE));
MoveWindow(group->hListView, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
break;
}
case WM_CLOSE:
SendMessageW(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
break;
case WM_SYSCOMMAND:
if (wParam == SC_CLOSE) wParam = SC_MINIMIZE;
break;
case WM_CHILDACTIVATE:
case WM_NCLBUTTONDOWN:
Globals.hActiveGroup = (PROGGROUP*)GetWindowLongPtrW(hWnd, 0);
Globals.hActiveGroup->hActiveProgram = NULL;
break;
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case NM_CLICK:
{
iItem = ((LPNMITEMACTIVATE)lParam)->iItem;
if (iItem == -1)
{
group->hActiveProgram = NULL;
break;
}
lvItem.mask = LVIF_PARAM;
lvItem.iItem = iItem;
SendMessageW(group->hListView, LVM_GETITEMW, 0, (LPARAM)&lvItem);
group->hActiveProgram = (PROGRAM*)lvItem.lParam;
break;
}
case NM_DBLCLK:
{
iItem = ((LPNMITEMACTIVATE)lParam)->iItem;
if (iItem == -1)
break;
lvItem.mask = LVIF_PARAM;
lvItem.iItem = iItem;
SendMessageW(group->hListView, LVM_GETITEMW, 0, (LPARAM)&lvItem);
/* ... or use group->hActiveProgram */
PROGRAM_ExecuteProgram((PROGRAM*)lvItem.lParam);
break;
}
case LVN_BEGINDRAG:
{
POINT ptMin;
BOOL bFirst = TRUE;
for (iItem = SendMessageA(group->hListView, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
iItem != -1;
iItem = SendMessageA(group->hListView, LVM_GETNEXTITEM, iItem, LVNI_SELECTED))
{
if (bFirst)
{
group->hDragImageList = (HIMAGELIST)SendMessageA(group->hListView,
LVM_CREATEDRAGIMAGE,
iItem,
(LPARAM)&pt);
ptMin = pt;
bFirst = FALSE;
}
else
{
HIMAGELIST hOneImageList, hTempImageList;
hOneImageList = (HIMAGELIST)SendMessageA(group->hListView,
LVM_CREATEDRAGIMAGE,
iItem,
(LPARAM)&pt);
hTempImageList = ImageList_Merge(group->hDragImageList,
0,
hOneImageList,
0,
pt.x - ptMin.x,
pt.y - ptMin.y);
ImageList_Destroy(group->hDragImageList);
ImageList_Destroy(hOneImageList);
group->hDragImageList = hTempImageList;
ptMin.x = min(ptMin.x, pt.x);
ptMin.y = min(ptMin.y, pt.y);
}
}
// pt = ((LPNMLISTVIEW)lParam)->ptAction;
pt.x = ((LPNMLISTVIEW)lParam)->ptAction.x;
pt.y = ((LPNMLISTVIEW)lParam)->ptAction.y;
group->ptStart = pt;
pt.x -= ptMin.x;
pt.y -= ptMin.y;
ImageList_BeginDrag(group->hDragImageList, 0, pt.x, pt.y);
MapWindowPoints(group->hListView, Globals.hMDIWnd, &pt, 1);
ImageList_DragEnter(Globals.hMDIWnd, pt.x, pt.y);
group->bDragging = TRUE;
group->hOldCursor = GetCursor();
SetCapture(group->hWnd);
break;
}
}
break;
case WM_MOUSEMOVE:
if (group->bDragging)
{
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
MapWindowPoints(group->hWnd, Globals.hMDIWnd, &pt, 1);
ImageList_DragMove(pt.x, pt.y);
}
break;
case WM_LBUTTONUP:
if (group->bDragging)
{
// LVHITTESTINFO lvhti;
POINT ptHit;
group->bDragging = FALSE;
ImageList_DragLeave(Globals.hMDIWnd);
ImageList_EndDrag();
ImageList_Destroy(group->hDragImageList);
ReleaseCapture();
SetCursor(group->hOldCursor);
ptHit.x = GET_X_LPARAM(lParam);
ptHit.y = GET_Y_LPARAM(lParam);
MapWindowPoints(group->hWnd, group->hListView, &ptHit, 1);
for (iItem = SendMessageA(group->hListView, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
iItem != -1;
iItem = SendMessageA(group->hListView, LVM_GETNEXTITEM, iItem, LVNI_SELECTED))
{
SendMessageA(group->hListView, LVM_GETITEMPOSITION, iItem, (LPARAM)&pt);
pt.x += ptHit.x - group->ptStart.x;
pt.y += ptHit.y - group->ptStart.y;
SendMessageA(group->hListView, LVM_SETITEMPOSITION, iItem, MAKELPARAM(pt.x, pt.y));
}
}
break;
}
return DefMDIChildProcW(hWnd, uMsg, wParam, lParam);
}
/***********************************************************************
*
* GROUP_RegisterGroupWinClass
*/
ATOM GROUP_RegisterGroupWinClass(VOID)
{
WNDCLASSW wndClass;
wndClass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = GROUP_GroupWndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = sizeof(LONG_PTR);
wndClass.hInstance = Globals.hInstance;
wndClass.hIcon = LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(IDI_GROUP_ICON));
wndClass.hCursor = LoadCursorW(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = STRING_GROUP_WIN_CLASS_NAME;
return RegisterClassW(&wndClass);
}
/***********************************************************************
*
* GROUP_NewGroup
*/
VOID GROUP_NewGroup(GROUPFORMAT format, BOOL bIsCommonGroup)
{
HANDLE hFile;
WCHAR szGrpFile[MAX_PATHNAME_LEN] = L"";
WCHAR szTitle[MAX_PATHNAME_LEN] = L"";
// ZeroMemory(szTitle, sizeof(szTitle));
// ZeroMemory(szGrpFile, sizeof(szGrpFile));
if (!DIALOG_GroupAttributes(format, szTitle, szGrpFile, MAX_PATHNAME_LEN))
return;
/*
* Just check whether the group file does exist. If it does, close the handle, because GRPFILE_ReadGroupFile will
* reopen the file for loading. If it doesn't exist, we create a new one.
*/
hFile = CreateFileW(szGrpFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
/* File doesn't exist */
PROGGROUP* hGroup = GROUP_AddGroup(format, bIsCommonGroup, szTitle, szGrpFile,
DEF_GROUP_WIN_XPOS, DEF_GROUP_WIN_YPOS,
DEF_GROUP_WIN_XPOS + DEF_GROUP_WIN_WIDTH, DEF_GROUP_WIN_YPOS + DEF_GROUP_WIN_HEIGHT,
0, 0, SW_SHOWNORMAL, 0, 0, FALSE, FALSE);
if (hGroup)
GRPFILE_WriteGroupFile(hGroup);
}
else
{
/* File exist */
CloseHandle(hFile);
GRPFILE_ReadGroupFile(szGrpFile, bIsCommonGroup);
}
/* FIXME Update progman.ini */
}
/***********************************************************************
*
* GROUP_AddGroup
*/
PROGGROUP*
GROUP_AddGroup(GROUPFORMAT format, BOOL bIsCommonGroup, LPCWSTR lpszName, LPCWSTR lpszGrpFile,
INT left, INT top, INT right, INT bottom, INT xMin, INT yMin, INT nCmdShow,
WORD cxIcon, WORD cyIcon, BOOL bOverwriteFileOk,
/* FIXME shouldn't be necessary */
BOOL bSuppressShowWindow)
{
PROGGROUP* hGroup;
PROGGROUP* hPrior;
PROGGROUP** p;
LPWSTR hName;
LPWSTR hGrpFile;
LPCWSTR GroupFileName;
INT skip;
INT width;
INT height;
INT seqnum;
MDICREATESTRUCTW mcs;
WINDOWPLACEMENT WndPl;
WndPl.length = sizeof(WndPl);
// FIXME: Use system default position in case we don't place the window at a given (x,y) coordinate.
if (bIsCommonGroup)
{
if (swscanf(lpszGrpFile,
L"%d %d %d %d %d %d %d %n",
&WndPl.rcNormalPosition.left,
&WndPl.rcNormalPosition.top,
&WndPl.rcNormalPosition.right,
&WndPl.rcNormalPosition.bottom,
&WndPl.ptMinPosition.x,
&WndPl.ptMinPosition.y,
&WndPl.showCmd,
&skip) == 7)
{
WndPl.flags = WPF_SETMINPOSITION;
width = WndPl.rcNormalPosition.right - WndPl.rcNormalPosition.left;
height = WndPl.rcNormalPosition.bottom - WndPl.rcNormalPosition.top;
GroupFileName = &lpszGrpFile[skip];
}
else
{
#if 0 // FIXME!
WndPl.rcNormalPosition.top = CW_USEDEFAULT;
WndPl.rcNormalPosition.left = CW_USEDEFAULT;
WndPl.rcNormalPosition.right = 0;
WndPl.rcNormalPosition.bottom = 0;
width = CW_USEDEFAULT;
height = CW_USEDEFAULT;
WndPl.showCmd = SW_SHOWNORMAL;
GroupFileName = lpszGrpFile;
#else
WndPl.flags = WPF_SETMINPOSITION;
WndPl.ptMinPosition.x = xMin;
WndPl.ptMinPosition.y = yMin;
WndPl.rcNormalPosition.left = left;
WndPl.rcNormalPosition.top = top;
WndPl.rcNormalPosition.right = right;
WndPl.rcNormalPosition.bottom = bottom;
width = right - left;
height = bottom - top;
WndPl.showCmd = nCmdShow;
GroupFileName = lpszGrpFile;
#endif
}
}
else
{
WndPl.flags = WPF_SETMINPOSITION;
WndPl.ptMinPosition.x = xMin;
WndPl.ptMinPosition.y = yMin;
WndPl.rcNormalPosition.left = left;
WndPl.rcNormalPosition.top = top;
WndPl.rcNormalPosition.right = right;
WndPl.rcNormalPosition.bottom = bottom;
width = right - left;
height = bottom - top;
WndPl.showCmd = nCmdShow;
GroupFileName = lpszGrpFile;
}
hGroup = Alloc(HEAP_ZERO_MEMORY, sizeof(*hGroup));
hName = Alloc(HEAP_ZERO_MEMORY, (wcslen(lpszName) + 1) * sizeof(WCHAR));
hGrpFile = Alloc(HEAP_ZERO_MEMORY, (wcslen(GroupFileName) + 1) * sizeof(WCHAR));
if (!hGroup || !hName || !hGrpFile)
{
MAIN_MessageBoxIDS(IDS_OUT_OF_MEMORY, IDS_ERROR, MB_OK);
if (hGroup) Free(hGroup);
if (hName) Free(hName);
if (hGrpFile) Free(hGrpFile);
return NULL;
}
memcpy(hName , lpszName , (wcslen(lpszName) + 1) * sizeof(WCHAR));
memcpy(hGrpFile, GroupFileName, (wcslen(GroupFileName) + 1) * sizeof(WCHAR));
Globals.hActiveGroup = hGroup;
seqnum = 1;
hPrior = NULL;
for (p = &Globals.hGroups; *p; p = &hPrior->hNext)
{
hPrior = *p;
if (hPrior->seqnum >= seqnum)
seqnum = hPrior->seqnum + 1;
}
*p = hGroup;
hGroup->hPrior = hPrior;
hGroup->hNext = NULL;
hGroup->format = format;
hGroup->bIsCommonGroup = bIsCommonGroup;
hGroup->hName = hName;
hGroup->hGrpFile = hGrpFile;
hGroup->bOverwriteFileOk = bOverwriteFileOk;
hGroup->seqnum = seqnum;
hGroup->nCmdShow = nCmdShow;
#if 0
hGroup->x = x;
hGroup->y = y;
hGroup->width = width;
hGroup->height = height;
#endif
hGroup->iconx = cxIcon;
hGroup->icony = cyIcon;
hGroup->hPrograms = NULL;
hGroup->hActiveProgram = NULL;
hGroup->TagsSize = 0;
hGroup->Tags = NULL;
mcs.szClass = STRING_GROUP_WIN_CLASS_NAME;
mcs.szTitle = lpszName;
mcs.hOwner = NULL;
mcs.x = WndPl.rcNormalPosition.left;
mcs.y = WndPl.rcNormalPosition.top;
mcs.cx = width;
mcs.cy = height;
mcs.style = 0;
mcs.lParam = (LPARAM)hGroup;
hGroup->hWnd = (HWND)SendMessageW(Globals.hMDIWnd, WM_MDICREATE, 0, (LPARAM)&mcs);
SetWindowPlacement(hGroup->hWnd, &WndPl);
#if 1
if (!bSuppressShowWindow) /* FIXME shouldn't be necessary */
#endif
UpdateWindow(hGroup->hWnd);
return hGroup;
}
/***********************************************************************
*
* GROUP_ModifyGroup
*/
VOID GROUP_ModifyGroup(PROGGROUP* hGroup)
{
WCHAR Dest[MAX_PATHNAME_LEN]; // szName
WCHAR szGrpFile[MAX_PATHNAME_LEN]; // szFile
wcsncpy(Dest, hGroup->hName, ARRAYSIZE(Dest));
wcsncpy(szGrpFile, hGroup->hGrpFile, ARRAYSIZE(szGrpFile));
if (!DIALOG_GroupAttributes(hGroup->format, Dest, szGrpFile, MAX_PATHNAME_LEN))
return;
if (wcscmp(szGrpFile, hGroup->hGrpFile))
hGroup->bOverwriteFileOk = FALSE;
MAIN_ReplaceString(&hGroup->hName, Dest);
MAIN_ReplaceString(&hGroup->hGrpFile, szGrpFile);
GRPFILE_WriteGroupFile(hGroup);
/* FIXME Delete old GrpFile if GrpFile changed */
/* FIXME Update progman.ini */
SetWindowTextW(hGroup->hWnd, Dest);
}
/***********************************************************************
*
* GROUP_DeleteGroup
*/
VOID GROUP_DeleteGroup(PROGGROUP* hGroup)
{
if (Globals.hActiveGroup == hGroup)
Globals.hActiveGroup = NULL;
if (hGroup->hPrior)
hGroup->hPrior->hNext = hGroup->hNext;
else
Globals.hGroups = hGroup->hNext;
if (hGroup->hNext)
hGroup->hNext->hPrior = hGroup->hPrior;
while (hGroup->hPrograms)
PROGRAM_DeleteProgram(hGroup->hPrograms, FALSE);
/* FIXME Update progman.ini */
SendMessageW(Globals.hMDIWnd, WM_MDIDESTROY, (WPARAM)hGroup->hWnd, 0);
if (hGroup->Tags)
Free(hGroup->Tags);
Free(hGroup->hName);
Free(hGroup->hGrpFile);
Free(hGroup);
}
/***********************************************************************
*
* GROUP_ShowGroupWindow
*/
/* FIXME shouldn't be necessary */
VOID GROUP_ShowGroupWindow(PROGGROUP* hGroup)
{
ShowWindow(hGroup->hWnd, hGroup->nCmdShow);
UpdateWindow(hGroup->hWnd);
}
/***********************************************************************
*
* GROUP_ActiveGroup
*/
PROGGROUP* GROUP_ActiveGroup(VOID)
{
return Globals.hActiveGroup;
}