--- /dev/null
+/*
+ * 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, MAKEINTRESOURCEW(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;
+}