xref: /reactos/base/shell/progman/group.c (revision 1a102501)
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
InitUxTheme(VOID)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
EnableThemeDialogTexture(_In_ HWND hwnd,_In_ DWORD dwFlags)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
SetWindowTheme(_In_ HWND hwnd,_In_ LPCWSTR pszSubAppName,_In_ LPCWSTR pszSubIdList)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
GROUP_GroupWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)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 
GROUP_RegisterGroupWinClass(VOID)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, 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 
GROUP_NewGroup(GROUPFORMAT format,BOOL bIsCommonGroup)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*
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,BOOL bSuppressShowWindow)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 
GROUP_ModifyGroup(PROGGROUP * hGroup)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 
GROUP_DeleteGroup(PROGGROUP * hGroup)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 */
GROUP_ShowGroupWindow(PROGGROUP * hGroup)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 
GROUP_ActiveGroup(VOID)671 PROGGROUP* GROUP_ActiveGroup(VOID)
672 {
673     return Globals.hActiveGroup;
674 }
675