[DCOMLAUNCH] Add a DcomLaunch service stub
[reactos.git] / base / shell / progman / group.c
1 /*
2 * Program Manager
3 *
4 * Copyright 1996 Ulrich Schmid
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 /*
22 * PROJECT: ReactOS Program Manager
23 * COPYRIGHT: GPL - See COPYING in the top level directory
24 * FILE: base/shell/progman/group.c
25 * PURPOSE: Program group files helper functions
26 * PROGRAMMERS: Ulrich Schmid
27 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
28 */
29
30 #include "progman.h"
31
32 /***********************************************************************
33 *
34 * UX Theming helpers, dropped from msconfig_new/comctl32ex/uxthemesupp.c
35 *
36 */
37
38 static HMODULE hUxTheme = NULL;
39
40 typedef HRESULT (WINAPI* ETDTProc)(HWND, DWORD);
41 static ETDTProc fnEnableThemeDialogTexture = NULL;
42
43 typedef HRESULT (WINAPI* SWTProc)(HWND, LPCWSTR, LPCWSTR);
44 static SWTProc fnSetWindowTheme = NULL;
45
46
47 static BOOL
48 InitUxTheme(VOID)
49 {
50 if (hUxTheme) return TRUE;
51
52 hUxTheme = LoadLibraryW(L"uxtheme.dll");
53 if (hUxTheme == NULL) return FALSE;
54
55 fnEnableThemeDialogTexture =
56 (ETDTProc)GetProcAddress(hUxTheme, "EnableThemeDialogTexture");
57 fnSetWindowTheme =
58 (SWTProc)GetProcAddress(hUxTheme, "SetWindowTheme");
59
60 return TRUE;
61 }
62
63 #if 0
64 static VOID
65 CleanupUxTheme(VOID)
66 {
67 FreeLibrary(hUxTheme);
68 hUxTheme = NULL;
69 }
70 #endif
71
72
73 ////////////////////////////////////////////////////////////////////////////////
74 // Taken from WinSpy++ 1.7
75 // http://www.catch22.net/software/winspy
76 // Copyright (c) 2002 by J Brown
77 //
78
79 HRESULT
80 WINAPI
81 EnableThemeDialogTexture(_In_ HWND hwnd,
82 _In_ DWORD dwFlags)
83 {
84 if (!InitUxTheme())
85 return HRESULT_FROM_WIN32(GetLastError());
86
87 if (!fnEnableThemeDialogTexture)
88 return HRESULT_FROM_WIN32(GetLastError());
89
90 return fnEnableThemeDialogTexture(hwnd, dwFlags);
91 }
92
93 HRESULT
94 WINAPI
95 SetWindowTheme(_In_ HWND hwnd,
96 _In_ LPCWSTR pszSubAppName,
97 _In_ LPCWSTR pszSubIdList)
98 {
99 if (!InitUxTheme())
100 return HRESULT_FROM_WIN32(GetLastError());
101
102 if (!fnSetWindowTheme)
103 return HRESULT_FROM_WIN32(GetLastError());
104
105 return fnSetWindowTheme(hwnd, pszSubAppName, pszSubIdList);
106 }
107
108
109 /***********************************************************************
110 *
111 * GROUP_GroupWndProc
112 */
113
114 static
115 LRESULT
116 CALLBACK
117 GROUP_GroupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
118 {
119 PROGGROUP* group;
120 INT iItem;
121 LVITEMW lvItem;
122 POINT pt;
123
124 group = (PROGGROUP*)GetWindowLongPtrW(hWnd, 0);
125
126 switch (uMsg)
127 {
128 case WM_NCCREATE:
129 {
130 LPCREATESTRUCTW pcs = (LPCREATESTRUCTW)lParam;
131 LPMDICREATESTRUCTW pMDIcs = (LPMDICREATESTRUCTW)pcs->lpCreateParams;
132 group = (PROGGROUP*)pMDIcs->lParam;
133 SetWindowLongPtrW(hWnd, 0, (LONG_PTR)group);
134
135 if (group->bIsCommonGroup)
136 {
137 DefMDIChildProcW(hWnd, WM_SETICON, ICON_BIG,
138 (LPARAM)CopyImage(Globals.hCommonGroupIcon,
139 IMAGE_ICON,
140 GetSystemMetrics(SM_CXICON),
141 GetSystemMetrics(SM_CYICON),
142 LR_COPYFROMRESOURCE));
143 DefMDIChildProcW(hWnd, WM_SETICON, ICON_SMALL,
144 (LPARAM)CopyImage(Globals.hCommonGroupIcon,
145 IMAGE_ICON,
146 GetSystemMetrics(SM_CXSMICON),
147 GetSystemMetrics(SM_CYSMICON),
148 LR_COPYFROMRESOURCE));
149 }
150 else
151 {
152 DefMDIChildProcW(hWnd, WM_SETICON, ICON_BIG,
153 (LPARAM)CopyImage(Globals.hPersonalGroupIcon,
154 IMAGE_ICON,
155 GetSystemMetrics(SM_CXICON),
156 GetSystemMetrics(SM_CYICON),
157 LR_COPYFROMRESOURCE));
158 DefMDIChildProcW(hWnd, WM_SETICON, ICON_SMALL,
159 (LPARAM)CopyImage(Globals.hPersonalGroupIcon,
160 IMAGE_ICON,
161 GetSystemMetrics(SM_CXSMICON),
162 GetSystemMetrics(SM_CYSMICON),
163 LR_COPYFROMRESOURCE));
164 }
165 break;
166 }
167
168 case WM_NCDESTROY:
169 SetWindowLongPtrW(hWnd, 0, 0);
170 break;
171
172 case WM_CREATE:
173 {
174 DWORD dwStyle;
175 RECT rect;
176 GetClientRect(hWnd, &rect);
177 group->hListView = CreateWindowW(WC_LISTVIEW,
178 NULL,
179 WS_CHILD | WS_VISIBLE | WS_OVERLAPPED,
180 0, 0,
181 rect.right - rect.left,
182 rect.bottom - rect.top,
183 hWnd,
184 NULL,
185 Globals.hInstance,
186 NULL);
187 dwStyle = (GetWindowLongPtrW(group->hListView, GWL_STYLE) | LVS_SHOWSELALWAYS) & ~LVS_AUTOARRANGE;
188 SetWindowLongPtrW(group->hListView, GWL_STYLE, dwStyle);
189 dwStyle = SendMessageA(group->hListView, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0) | LVS_EX_BORDERSELECT;
190 SendMessageA(group->hListView, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_SNAPTOGRID, dwStyle);
191 InitUxTheme();
192 SetWindowTheme(group->hListView, L"Explorer", NULL);
193 group->hListLarge = ImageList_Create(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), ILC_COLOR24 | ILC_MASK, 1, 1);
194 SendMessageA(group->hListView, LVM_SETIMAGELIST, 0, (LPARAM)group->hListLarge);
195 SendMessageA(group->hListView, LVM_SETICONSPACING, 0, MAKELPARAM(80, 64));
196 break;
197 }
198
199 case WM_DESTROY:
200 {
201 SendMessageA(group->hListView, LVM_SETIMAGELIST, 0, 0);
202 ImageList_Destroy(group->hListLarge);
203 DestroyWindow(group->hListView);
204 break;
205 }
206
207 case WM_SIZE:
208 {
209 RECT rect;
210 rect.left = 0;
211 rect.top = 0;
212 rect.right = LOWORD(lParam);
213 rect.bottom = HIWORD(lParam);
214 AdjustWindowRectEx(&rect, GetWindowLongPtrW(group->hListView, GWL_STYLE), FALSE, GetWindowLongPtrW(group->hListView, GWL_EXSTYLE));
215 MoveWindow(group->hListView, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
216 break;
217 }
218
219 case WM_CLOSE:
220 SendMessageW(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
221 break;
222
223 case WM_SYSCOMMAND:
224 if (wParam == SC_CLOSE) wParam = SC_MINIMIZE;
225 break;
226
227 case WM_CHILDACTIVATE:
228 case WM_NCLBUTTONDOWN:
229 Globals.hActiveGroup = (PROGGROUP*)GetWindowLongPtrW(hWnd, 0);
230 Globals.hActiveGroup->hActiveProgram = NULL;
231 break;
232
233 case WM_NOTIFY:
234 switch (((LPNMHDR)lParam)->code)
235 {
236 case NM_CLICK:
237 {
238 iItem = ((LPNMITEMACTIVATE)lParam)->iItem;
239 if (iItem == -1)
240 {
241 group->hActiveProgram = NULL;
242 break;
243 }
244
245 lvItem.mask = LVIF_PARAM;
246 lvItem.iItem = iItem;
247 SendMessageW(group->hListView, LVM_GETITEMW, 0, (LPARAM)&lvItem);
248 group->hActiveProgram = (PROGRAM*)lvItem.lParam;
249 break;
250 }
251
252 case NM_DBLCLK:
253 {
254 iItem = ((LPNMITEMACTIVATE)lParam)->iItem;
255 if (iItem == -1)
256 break;
257
258 lvItem.mask = LVIF_PARAM;
259 lvItem.iItem = iItem;
260 SendMessageW(group->hListView, LVM_GETITEMW, 0, (LPARAM)&lvItem);
261 /* ... or use group->hActiveProgram */
262 PROGRAM_ExecuteProgram((PROGRAM*)lvItem.lParam);
263 break;
264 }
265
266 case LVN_BEGINDRAG:
267 {
268 POINT ptMin;
269
270 BOOL bFirst = TRUE;
271 for (iItem = SendMessageA(group->hListView, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
272 iItem != -1;
273 iItem = SendMessageA(group->hListView, LVM_GETNEXTITEM, iItem, LVNI_SELECTED))
274 {
275 if (bFirst)
276 {
277 group->hDragImageList = (HIMAGELIST)SendMessageA(group->hListView,
278 LVM_CREATEDRAGIMAGE,
279 iItem,
280 (LPARAM)&pt);
281 ptMin = pt;
282 bFirst = FALSE;
283 }
284 else
285 {
286 HIMAGELIST hOneImageList, hTempImageList;
287
288 hOneImageList = (HIMAGELIST)SendMessageA(group->hListView,
289 LVM_CREATEDRAGIMAGE,
290 iItem,
291 (LPARAM)&pt);
292 hTempImageList = ImageList_Merge(group->hDragImageList,
293 0,
294 hOneImageList,
295 0,
296 pt.x - ptMin.x,
297 pt.y - ptMin.y);
298 ImageList_Destroy(group->hDragImageList);
299 ImageList_Destroy(hOneImageList);
300 group->hDragImageList = hTempImageList;
301 ptMin.x = min(ptMin.x, pt.x);
302 ptMin.y = min(ptMin.y, pt.y);
303 }
304 }
305 // pt = ((LPNMLISTVIEW)lParam)->ptAction;
306 pt.x = ((LPNMLISTVIEW)lParam)->ptAction.x;
307 pt.y = ((LPNMLISTVIEW)lParam)->ptAction.y;
308 group->ptStart = pt;
309 pt.x -= ptMin.x;
310 pt.y -= ptMin.y;
311 ImageList_BeginDrag(group->hDragImageList, 0, pt.x, pt.y);
312 MapWindowPoints(group->hListView, Globals.hMDIWnd, &pt, 1);
313 ImageList_DragEnter(Globals.hMDIWnd, pt.x, pt.y);
314 group->bDragging = TRUE;
315 group->hOldCursor = GetCursor();
316 SetCapture(group->hWnd);
317
318 break;
319 }
320 }
321 break;
322
323 case WM_MOUSEMOVE:
324 if (group->bDragging)
325 {
326 pt.x = GET_X_LPARAM(lParam);
327 pt.y = GET_Y_LPARAM(lParam);
328 MapWindowPoints(group->hWnd, Globals.hMDIWnd, &pt, 1);
329 ImageList_DragMove(pt.x, pt.y);
330 }
331 break;
332
333 case WM_LBUTTONUP:
334 if (group->bDragging)
335 {
336 // LVHITTESTINFO lvhti;
337 POINT ptHit;
338
339 group->bDragging = FALSE;
340 ImageList_DragLeave(Globals.hMDIWnd);
341 ImageList_EndDrag();
342 ImageList_Destroy(group->hDragImageList);
343 ReleaseCapture();
344 SetCursor(group->hOldCursor);
345 ptHit.x = GET_X_LPARAM(lParam);
346 ptHit.y = GET_Y_LPARAM(lParam);
347 MapWindowPoints(group->hWnd, group->hListView, &ptHit, 1);
348 for (iItem = SendMessageA(group->hListView, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
349 iItem != -1;
350 iItem = SendMessageA(group->hListView, LVM_GETNEXTITEM, iItem, LVNI_SELECTED))
351 {
352 SendMessageA(group->hListView, LVM_GETITEMPOSITION, iItem, (LPARAM)&pt);
353 pt.x += ptHit.x - group->ptStart.x;
354 pt.y += ptHit.y - group->ptStart.y;
355 SendMessageA(group->hListView, LVM_SETITEMPOSITION, iItem, MAKELPARAM(pt.x, pt.y));
356 }
357 }
358 break;
359 }
360
361 return DefMDIChildProcW(hWnd, uMsg, wParam, lParam);
362 }
363
364 /***********************************************************************
365 *
366 * GROUP_RegisterGroupWinClass
367 */
368
369 ATOM GROUP_RegisterGroupWinClass(VOID)
370 {
371 WNDCLASSW wndClass;
372
373 wndClass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
374 wndClass.lpfnWndProc = GROUP_GroupWndProc;
375 wndClass.cbClsExtra = 0;
376 wndClass.cbWndExtra = sizeof(LONG_PTR);
377 wndClass.hInstance = Globals.hInstance;
378 wndClass.hIcon = LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(IDI_GROUP_ICON));
379 wndClass.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
380 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
381 wndClass.lpszMenuName = NULL;
382 wndClass.lpszClassName = STRING_GROUP_WIN_CLASS_NAME;
383
384 return RegisterClassW(&wndClass);
385 }
386
387 /***********************************************************************
388 *
389 * GROUP_NewGroup
390 */
391
392 VOID GROUP_NewGroup(GROUPFORMAT format, BOOL bIsCommonGroup)
393 {
394 HANDLE hFile;
395 WCHAR szGrpFile[MAX_PATHNAME_LEN] = L"";
396 WCHAR szTitle[MAX_PATHNAME_LEN] = L"";
397
398 // ZeroMemory(szTitle, sizeof(szTitle));
399 // ZeroMemory(szGrpFile, sizeof(szGrpFile));
400
401 if (!DIALOG_GroupAttributes(format, szTitle, szGrpFile, MAX_PATHNAME_LEN))
402 return;
403
404 /*
405 * Just check whether the group file does exist. If it does, close the handle, because GRPFILE_ReadGroupFile will
406 * reopen the file for loading. If it doesn't exist, we create a new one.
407 */
408 hFile = CreateFileW(szGrpFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
409 if (hFile == INVALID_HANDLE_VALUE)
410 {
411 /* File doesn't exist */
412 PROGGROUP* hGroup = GROUP_AddGroup(format, bIsCommonGroup, szTitle, szGrpFile,
413 DEF_GROUP_WIN_XPOS, DEF_GROUP_WIN_YPOS,
414 DEF_GROUP_WIN_XPOS + DEF_GROUP_WIN_WIDTH, DEF_GROUP_WIN_YPOS + DEF_GROUP_WIN_HEIGHT,
415 0, 0, SW_SHOWNORMAL, 0, 0, FALSE, FALSE);
416 if (hGroup)
417 GRPFILE_WriteGroupFile(hGroup);
418 }
419 else
420 {
421 /* File exist */
422 CloseHandle(hFile);
423 GRPFILE_ReadGroupFile(szGrpFile, bIsCommonGroup);
424 }
425
426 /* FIXME Update progman.ini */
427 }
428
429 /***********************************************************************
430 *
431 * GROUP_AddGroup
432 */
433
434 PROGGROUP*
435 GROUP_AddGroup(GROUPFORMAT format, BOOL bIsCommonGroup, LPCWSTR lpszName, LPCWSTR lpszGrpFile,
436 INT left, INT top, INT right, INT bottom, INT xMin, INT yMin, INT nCmdShow,
437 WORD cxIcon, WORD cyIcon, BOOL bOverwriteFileOk,
438 /* FIXME shouldn't be necessary */
439 BOOL bSuppressShowWindow)
440 {
441 PROGGROUP* hGroup;
442 PROGGROUP* hPrior;
443 PROGGROUP** p;
444 LPWSTR hName;
445 LPWSTR hGrpFile;
446 LPCWSTR GroupFileName;
447 INT skip;
448 INT width;
449 INT height;
450 INT seqnum;
451 MDICREATESTRUCTW mcs;
452 WINDOWPLACEMENT WndPl;
453
454 WndPl.length = sizeof(WndPl);
455
456 // FIXME: Use system default position in case we don't place the window at a given (x,y) coordinate.
457
458 if (bIsCommonGroup)
459 {
460 if (swscanf(lpszGrpFile,
461 L"%d %d %d %d %d %d %d %n",
462 &WndPl.rcNormalPosition.left,
463 &WndPl.rcNormalPosition.top,
464 &WndPl.rcNormalPosition.right,
465 &WndPl.rcNormalPosition.bottom,
466 &WndPl.ptMinPosition.x,
467 &WndPl.ptMinPosition.y,
468 &WndPl.showCmd,
469 &skip) == 7)
470 {
471 WndPl.flags = WPF_SETMINPOSITION;
472 width = WndPl.rcNormalPosition.right - WndPl.rcNormalPosition.left;
473 height = WndPl.rcNormalPosition.bottom - WndPl.rcNormalPosition.top;
474 GroupFileName = &lpszGrpFile[skip];
475 }
476 else
477 {
478 #if 0 // FIXME!
479 WndPl.rcNormalPosition.top = CW_USEDEFAULT;
480 WndPl.rcNormalPosition.left = CW_USEDEFAULT;
481 WndPl.rcNormalPosition.right = 0;
482 WndPl.rcNormalPosition.bottom = 0;
483 width = CW_USEDEFAULT;
484 height = CW_USEDEFAULT;
485 WndPl.showCmd = SW_SHOWNORMAL;
486 GroupFileName = lpszGrpFile;
487 #else
488 WndPl.flags = WPF_SETMINPOSITION;
489 WndPl.ptMinPosition.x = xMin;
490 WndPl.ptMinPosition.y = yMin;
491 WndPl.rcNormalPosition.left = left;
492 WndPl.rcNormalPosition.top = top;
493 WndPl.rcNormalPosition.right = right;
494 WndPl.rcNormalPosition.bottom = bottom;
495 width = right - left;
496 height = bottom - top;
497 WndPl.showCmd = nCmdShow;
498 GroupFileName = lpszGrpFile;
499 #endif
500 }
501 }
502 else
503 {
504 WndPl.flags = WPF_SETMINPOSITION;
505 WndPl.ptMinPosition.x = xMin;
506 WndPl.ptMinPosition.y = yMin;
507 WndPl.rcNormalPosition.left = left;
508 WndPl.rcNormalPosition.top = top;
509 WndPl.rcNormalPosition.right = right;
510 WndPl.rcNormalPosition.bottom = bottom;
511 width = right - left;
512 height = bottom - top;
513 WndPl.showCmd = nCmdShow;
514 GroupFileName = lpszGrpFile;
515 }
516
517 hGroup = Alloc(HEAP_ZERO_MEMORY, sizeof(*hGroup));
518 hName = Alloc(HEAP_ZERO_MEMORY, (wcslen(lpszName) + 1) * sizeof(WCHAR));
519 hGrpFile = Alloc(HEAP_ZERO_MEMORY, (wcslen(GroupFileName) + 1) * sizeof(WCHAR));
520 if (!hGroup || !hName || !hGrpFile)
521 {
522 MAIN_MessageBoxIDS(IDS_OUT_OF_MEMORY, IDS_ERROR, MB_OK);
523 if (hGroup) Free(hGroup);
524 if (hName) Free(hName);
525 if (hGrpFile) Free(hGrpFile);
526 return NULL;
527 }
528 memcpy(hName , lpszName , (wcslen(lpszName) + 1) * sizeof(WCHAR));
529 memcpy(hGrpFile, GroupFileName, (wcslen(GroupFileName) + 1) * sizeof(WCHAR));
530
531 Globals.hActiveGroup = hGroup;
532
533 seqnum = 1;
534 hPrior = NULL;
535 for (p = &Globals.hGroups; *p; p = &hPrior->hNext)
536 {
537 hPrior = *p;
538 if (hPrior->seqnum >= seqnum)
539 seqnum = hPrior->seqnum + 1;
540 }
541 *p = hGroup;
542
543 hGroup->hPrior = hPrior;
544 hGroup->hNext = NULL;
545 hGroup->format = format;
546 hGroup->bIsCommonGroup = bIsCommonGroup;
547 hGroup->hName = hName;
548 hGroup->hGrpFile = hGrpFile;
549 hGroup->bOverwriteFileOk = bOverwriteFileOk;
550 hGroup->seqnum = seqnum;
551 hGroup->nCmdShow = nCmdShow;
552 #if 0
553 hGroup->x = x;
554 hGroup->y = y;
555 hGroup->width = width;
556 hGroup->height = height;
557 #endif
558 hGroup->iconx = cxIcon;
559 hGroup->icony = cyIcon;
560 hGroup->hPrograms = NULL;
561 hGroup->hActiveProgram = NULL;
562 hGroup->TagsSize = 0;
563 hGroup->Tags = NULL;
564
565 mcs.szClass = STRING_GROUP_WIN_CLASS_NAME;
566 mcs.szTitle = lpszName;
567 mcs.hOwner = NULL;
568 mcs.x = WndPl.rcNormalPosition.left;
569 mcs.y = WndPl.rcNormalPosition.top;
570 mcs.cx = width;
571 mcs.cy = height;
572 mcs.style = 0;
573 mcs.lParam = (LPARAM)hGroup;
574
575 hGroup->hWnd = (HWND)SendMessageW(Globals.hMDIWnd, WM_MDICREATE, 0, (LPARAM)&mcs);
576
577 SetWindowPlacement(hGroup->hWnd, &WndPl);
578
579 #if 1
580 if (!bSuppressShowWindow) /* FIXME shouldn't be necessary */
581 #endif
582 UpdateWindow(hGroup->hWnd);
583
584 return hGroup;
585 }
586
587
588
589
590
591 /***********************************************************************
592 *
593 * GROUP_ModifyGroup
594 */
595
596 VOID GROUP_ModifyGroup(PROGGROUP* hGroup)
597 {
598 WCHAR Dest[MAX_PATHNAME_LEN]; // szName
599 WCHAR szGrpFile[MAX_PATHNAME_LEN]; // szFile
600
601 wcsncpy(Dest, hGroup->hName, ARRAYSIZE(Dest));
602 wcsncpy(szGrpFile, hGroup->hGrpFile, ARRAYSIZE(szGrpFile));
603
604 if (!DIALOG_GroupAttributes(hGroup->format, Dest, szGrpFile, MAX_PATHNAME_LEN))
605 return;
606
607 if (wcscmp(szGrpFile, hGroup->hGrpFile))
608 hGroup->bOverwriteFileOk = FALSE;
609
610 MAIN_ReplaceString(&hGroup->hName, Dest);
611 MAIN_ReplaceString(&hGroup->hGrpFile, szGrpFile);
612
613 GRPFILE_WriteGroupFile(hGroup);
614
615 /* FIXME Delete old GrpFile if GrpFile changed */
616
617 /* FIXME Update progman.ini */
618
619 SetWindowTextW(hGroup->hWnd, Dest);
620 }
621
622 /***********************************************************************
623 *
624 * GROUP_DeleteGroup
625 */
626
627 VOID GROUP_DeleteGroup(PROGGROUP* hGroup)
628 {
629 if (Globals.hActiveGroup == hGroup)
630 Globals.hActiveGroup = NULL;
631
632 if (hGroup->hPrior)
633 hGroup->hPrior->hNext = hGroup->hNext;
634 else
635 Globals.hGroups = hGroup->hNext;
636
637 if (hGroup->hNext)
638 hGroup->hNext->hPrior = hGroup->hPrior;
639
640 while (hGroup->hPrograms)
641 PROGRAM_DeleteProgram(hGroup->hPrograms, FALSE);
642
643 /* FIXME Update progman.ini */
644
645 SendMessageW(Globals.hMDIWnd, WM_MDIDESTROY, (WPARAM)hGroup->hWnd, 0);
646
647 if (hGroup->Tags)
648 Free(hGroup->Tags);
649 Free(hGroup->hName);
650 Free(hGroup->hGrpFile);
651 Free(hGroup);
652 }
653
654 /***********************************************************************
655 *
656 * GROUP_ShowGroupWindow
657 */
658
659 /* FIXME shouldn't be necessary */
660 VOID GROUP_ShowGroupWindow(PROGGROUP* hGroup)
661 {
662 ShowWindow(hGroup->hWnd, hGroup->nCmdShow);
663 UpdateWindow(hGroup->hWnd);
664 }
665
666 /***********************************************************************
667 *
668 * GROUP_ActiveGroup
669 */
670
671 PROGGROUP* GROUP_ActiveGroup(VOID)
672 {
673 return Globals.hActiveGroup;
674 }