4 * Copyright 1996 Ulrich Schmid
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.
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.
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
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)
32 /***********************************************************************
34 * UX Theming helpers, dropped from msconfig_new/comctl32ex/uxthemesupp.c
38 static HMODULE hUxTheme
= NULL
;
40 typedef HRESULT (WINAPI
* ETDTProc
)(HWND
, DWORD
);
41 static ETDTProc fnEnableThemeDialogTexture
= NULL
;
43 typedef HRESULT (WINAPI
* SWTProc
)(HWND
, LPCWSTR
, LPCWSTR
);
44 static SWTProc fnSetWindowTheme
= NULL
;
50 if (hUxTheme
) return TRUE
;
52 hUxTheme
= LoadLibraryW(L
"uxtheme.dll");
53 if (hUxTheme
== NULL
) return FALSE
;
55 fnEnableThemeDialogTexture
=
56 (ETDTProc
)GetProcAddress(hUxTheme
, "EnableThemeDialogTexture");
58 (SWTProc
)GetProcAddress(hUxTheme
, "SetWindowTheme");
67 FreeLibrary(hUxTheme
);
73 ////////////////////////////////////////////////////////////////////////////////
74 // Taken from WinSpy++ 1.7
75 // http://www.catch22.net/software/winspy
76 // Copyright (c) 2002 by J Brown
81 EnableThemeDialogTexture(_In_ HWND hwnd
,
85 return HRESULT_FROM_WIN32(GetLastError());
87 if (!fnEnableThemeDialogTexture
)
88 return HRESULT_FROM_WIN32(GetLastError());
90 return fnEnableThemeDialogTexture(hwnd
, dwFlags
);
95 SetWindowTheme(_In_ HWND hwnd
,
96 _In_ LPCWSTR pszSubAppName
,
97 _In_ LPCWSTR pszSubIdList
)
100 return HRESULT_FROM_WIN32(GetLastError());
102 if (!fnSetWindowTheme
)
103 return HRESULT_FROM_WIN32(GetLastError());
105 return fnSetWindowTheme(hwnd
, pszSubAppName
, pszSubIdList
);
109 /***********************************************************************
117 GROUP_GroupWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
124 group
= (PROGGROUP
*)GetWindowLongPtrW(hWnd
, 0);
130 LPCREATESTRUCTW pcs
= (LPCREATESTRUCTW
)lParam
;
131 LPMDICREATESTRUCTW pMDIcs
= (LPMDICREATESTRUCTW
)pcs
->lpCreateParams
;
132 group
= (PROGGROUP
*)pMDIcs
->lParam
;
133 SetWindowLongPtrW(hWnd
, 0, (LONG_PTR
)group
);
135 if (group
->bIsCommonGroup
)
137 DefMDIChildProcW(hWnd
, WM_SETICON
, ICON_BIG
,
138 (LPARAM
)CopyImage(Globals
.hCommonGroupIcon
,
140 GetSystemMetrics(SM_CXICON
),
141 GetSystemMetrics(SM_CYICON
),
142 LR_COPYFROMRESOURCE
));
143 DefMDIChildProcW(hWnd
, WM_SETICON
, ICON_SMALL
,
144 (LPARAM
)CopyImage(Globals
.hCommonGroupIcon
,
146 GetSystemMetrics(SM_CXSMICON
),
147 GetSystemMetrics(SM_CYSMICON
),
148 LR_COPYFROMRESOURCE
));
152 DefMDIChildProcW(hWnd
, WM_SETICON
, ICON_BIG
,
153 (LPARAM
)CopyImage(Globals
.hPersonalGroupIcon
,
155 GetSystemMetrics(SM_CXICON
),
156 GetSystemMetrics(SM_CYICON
),
157 LR_COPYFROMRESOURCE
));
158 DefMDIChildProcW(hWnd
, WM_SETICON
, ICON_SMALL
,
159 (LPARAM
)CopyImage(Globals
.hPersonalGroupIcon
,
161 GetSystemMetrics(SM_CXSMICON
),
162 GetSystemMetrics(SM_CYSMICON
),
163 LR_COPYFROMRESOURCE
));
169 SetWindowLongPtrW(hWnd
, 0, 0);
176 GetClientRect(hWnd
, &rect
);
177 group
->hListView
= CreateWindowW(WC_LISTVIEW
,
179 WS_CHILD
| WS_VISIBLE
| WS_OVERLAPPED
,
181 rect
.right
- rect
.left
,
182 rect
.bottom
- rect
.top
,
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
);
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));
201 SendMessageA(group
->hListView
, LVM_SETIMAGELIST
, 0, 0);
202 ImageList_Destroy(group
->hListLarge
);
203 DestroyWindow(group
->hListView
);
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
);
220 SendMessageW(hWnd
, WM_SYSCOMMAND
, SC_MINIMIZE
, 0);
224 if (wParam
== SC_CLOSE
) wParam
= SC_MINIMIZE
;
227 case WM_CHILDACTIVATE
:
228 case WM_NCLBUTTONDOWN
:
229 Globals
.hActiveGroup
= (PROGGROUP
*)GetWindowLongPtrW(hWnd
, 0);
230 Globals
.hActiveGroup
->hActiveProgram
= NULL
;
234 switch (((LPNMHDR
)lParam
)->code
)
238 iItem
= ((LPNMITEMACTIVATE
)lParam
)->iItem
;
241 group
->hActiveProgram
= NULL
;
245 lvItem
.mask
= LVIF_PARAM
;
246 lvItem
.iItem
= iItem
;
247 SendMessageW(group
->hListView
, LVM_GETITEMW
, 0, (LPARAM
)&lvItem
);
248 group
->hActiveProgram
= (PROGRAM
*)lvItem
.lParam
;
254 iItem
= ((LPNMITEMACTIVATE
)lParam
)->iItem
;
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
);
271 for (iItem
= SendMessageA(group
->hListView
, LVM_GETNEXTITEM
, -1, LVNI_SELECTED
);
273 iItem
= SendMessageA(group
->hListView
, LVM_GETNEXTITEM
, iItem
, LVNI_SELECTED
))
277 group
->hDragImageList
= (HIMAGELIST
)SendMessageA(group
->hListView
,
286 HIMAGELIST hOneImageList
, hTempImageList
;
288 hOneImageList
= (HIMAGELIST
)SendMessageA(group
->hListView
,
292 hTempImageList
= ImageList_Merge(group
->hDragImageList
,
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
);
305 // pt = ((LPNMLISTVIEW)lParam)->ptAction;
306 pt
.x
= ((LPNMLISTVIEW
)lParam
)->ptAction
.x
;
307 pt
.y
= ((LPNMLISTVIEW
)lParam
)->ptAction
.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
);
324 if (group
->bDragging
)
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
);
334 if (group
->bDragging
)
336 // LVHITTESTINFO lvhti;
339 group
->bDragging
= FALSE
;
340 ImageList_DragLeave(Globals
.hMDIWnd
);
342 ImageList_Destroy(group
->hDragImageList
);
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
);
350 iItem
= SendMessageA(group
->hListView
, LVM_GETNEXTITEM
, iItem
, LVNI_SELECTED
))
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
));
361 return DefMDIChildProcW(hWnd
, uMsg
, wParam
, lParam
);
364 /***********************************************************************
366 * GROUP_RegisterGroupWinClass
369 ATOM
GROUP_RegisterGroupWinClass(VOID
)
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
;
384 return RegisterClassW(&wndClass
);
387 /***********************************************************************
392 VOID
GROUP_NewGroup(GROUPFORMAT format
, BOOL bIsCommonGroup
)
395 WCHAR szGrpFile
[MAX_PATHNAME_LEN
] = L
"";
396 WCHAR szTitle
[MAX_PATHNAME_LEN
] = L
"";
398 // ZeroMemory(szTitle, sizeof(szTitle));
399 // ZeroMemory(szGrpFile, sizeof(szGrpFile));
401 if (!DIALOG_GroupAttributes(format
, szTitle
, szGrpFile
, MAX_PATHNAME_LEN
))
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.
408 hFile
= CreateFileW(szGrpFile
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
409 if (hFile
== INVALID_HANDLE_VALUE
)
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
);
417 GRPFILE_WriteGroupFile(hGroup
);
423 GRPFILE_ReadGroupFile(szGrpFile
, bIsCommonGroup
);
426 /* FIXME Update progman.ini */
429 /***********************************************************************
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
)
446 LPCWSTR GroupFileName
;
451 MDICREATESTRUCTW mcs
;
452 WINDOWPLACEMENT WndPl
;
454 WndPl
.length
= sizeof(WndPl
);
456 // FIXME: Use system default position in case we don't place the window at a given (x,y) coordinate.
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
,
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
];
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
;
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
;
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
;
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
)
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
);
528 memcpy(hName
, lpszName
, (wcslen(lpszName
) + 1) * sizeof(WCHAR
));
529 memcpy(hGrpFile
, GroupFileName
, (wcslen(GroupFileName
) + 1) * sizeof(WCHAR
));
531 Globals
.hActiveGroup
= hGroup
;
535 for (p
= &Globals
.hGroups
; *p
; p
= &hPrior
->hNext
)
538 if (hPrior
->seqnum
>= seqnum
)
539 seqnum
= hPrior
->seqnum
+ 1;
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
;
555 hGroup
->width
= width
;
556 hGroup
->height
= height
;
558 hGroup
->iconx
= cxIcon
;
559 hGroup
->icony
= cyIcon
;
560 hGroup
->hPrograms
= NULL
;
561 hGroup
->hActiveProgram
= NULL
;
562 hGroup
->TagsSize
= 0;
565 mcs
.szClass
= STRING_GROUP_WIN_CLASS_NAME
;
566 mcs
.szTitle
= lpszName
;
568 mcs
.x
= WndPl
.rcNormalPosition
.left
;
569 mcs
.y
= WndPl
.rcNormalPosition
.top
;
573 mcs
.lParam
= (LPARAM
)hGroup
;
575 hGroup
->hWnd
= (HWND
)SendMessageW(Globals
.hMDIWnd
, WM_MDICREATE
, 0, (LPARAM
)&mcs
);
577 SetWindowPlacement(hGroup
->hWnd
, &WndPl
);
580 if (!bSuppressShowWindow
) /* FIXME shouldn't be necessary */
582 UpdateWindow(hGroup
->hWnd
);
591 /***********************************************************************
596 VOID
GROUP_ModifyGroup(PROGGROUP
* hGroup
)
598 WCHAR Dest
[MAX_PATHNAME_LEN
]; // szName
599 WCHAR szGrpFile
[MAX_PATHNAME_LEN
]; // szFile
601 wcsncpy(Dest
, hGroup
->hName
, ARRAYSIZE(Dest
));
602 wcsncpy(szGrpFile
, hGroup
->hGrpFile
, ARRAYSIZE(szGrpFile
));
604 if (!DIALOG_GroupAttributes(hGroup
->format
, Dest
, szGrpFile
, MAX_PATHNAME_LEN
))
607 if (wcscmp(szGrpFile
, hGroup
->hGrpFile
))
608 hGroup
->bOverwriteFileOk
= FALSE
;
610 MAIN_ReplaceString(&hGroup
->hName
, Dest
);
611 MAIN_ReplaceString(&hGroup
->hGrpFile
, szGrpFile
);
613 GRPFILE_WriteGroupFile(hGroup
);
615 /* FIXME Delete old GrpFile if GrpFile changed */
617 /* FIXME Update progman.ini */
619 SetWindowTextW(hGroup
->hWnd
, Dest
);
622 /***********************************************************************
627 VOID
GROUP_DeleteGroup(PROGGROUP
* hGroup
)
629 if (Globals
.hActiveGroup
== hGroup
)
630 Globals
.hActiveGroup
= NULL
;
633 hGroup
->hPrior
->hNext
= hGroup
->hNext
;
635 Globals
.hGroups
= hGroup
->hNext
;
638 hGroup
->hNext
->hPrior
= hGroup
->hPrior
;
640 while (hGroup
->hPrograms
)
641 PROGRAM_DeleteProgram(hGroup
->hPrograms
, FALSE
);
643 /* FIXME Update progman.ini */
645 SendMessageW(Globals
.hMDIWnd
, WM_MDIDESTROY
, (WPARAM
)hGroup
->hWnd
, 0);
650 Free(hGroup
->hGrpFile
);
654 /***********************************************************************
656 * GROUP_ShowGroupWindow
659 /* FIXME shouldn't be necessary */
660 VOID
GROUP_ShowGroupWindow(PROGGROUP
* hGroup
)
662 ShowWindow(hGroup
->hWnd
, hGroup
->nCmdShow
);
663 UpdateWindow(hGroup
->hWnd
);
666 /***********************************************************************
671 PROGGROUP
* GROUP_ActiveGroup(VOID
)
673 return Globals
.hActiveGroup
;