3300 lines
85 KiB
C
3300 lines
85 KiB
C
|
|
|
|
/* PMGSEG.C - */
|
|
|
|
/* Program Manager Group Handling Routines */
|
|
|
|
|
|
|
|
#include "progman.h"
|
|
#include "dde.h"
|
|
#include "convgrp.h"
|
|
|
|
#define WORD_MIN -32767
|
|
#define WORD_MAX 32767
|
|
|
|
#ifndef ORGCODE
|
|
#include "fcntl.h"
|
|
#include "io.h"
|
|
#include "stdio.h"
|
|
#include <tchar.h>
|
|
#define S_IREAD 0000400 /* read permission, owner */
|
|
#define S_IWRITE 0000200 /* write permission, owner */
|
|
#endif
|
|
|
|
BOOL fFirstLoad = FALSE;
|
|
extern BOOL bHandleProgramGroupsEvent;
|
|
|
|
#if 0
|
|
// DOS apps are no longer set to fullscreen by default in progman
|
|
// 5-3-93 johannec (bug 8343)
|
|
#ifdef i386
|
|
BOOL IsDOSApplication(LPTSTR lpPath);
|
|
BOOL SetDOSApplicationToFullScreen(LPTSTR lpTitle);
|
|
#endif
|
|
#endif
|
|
|
|
void NEAR PASCAL RemoveItemFromList(PGROUP pGroup, PITEM pItem)
|
|
// Removes a PITEM from the list.
|
|
{
|
|
PITEM* ppItem;
|
|
|
|
/* Cause it to be repainted later. */
|
|
InvalidateIcon(pGroup, pItem);
|
|
|
|
if (pItem == pGroup->pItems) {
|
|
/*
|
|
* first one in list, must invalidate next one so it paints an active
|
|
* title bar.
|
|
*/
|
|
InvalidateIcon(pGroup, pItem->pNext);
|
|
}
|
|
|
|
/* Remove it from the list. */
|
|
for (ppItem = &pGroup->pItems; *ppItem != pItem;
|
|
ppItem = &((*ppItem)->pNext));
|
|
|
|
*ppItem = pItem->pNext;
|
|
|
|
/* Lastly free up the memory. */
|
|
LocalFree((HANDLE)pItem);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void NEAR PASCAL CheckBeforeReAlloc(HANDLE h)
|
|
{
|
|
TCHAR buf[100];
|
|
|
|
if ((BYTE)GlobalFlags(h)) {
|
|
wsprintf(buf, TEXT("LockCount before realloc %d\r\n"), (BYTE)GlobalFlags(h));
|
|
OutputDebugString(buf);
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
#else
|
|
#define CheckBeforeReAlloc(h)
|
|
#endif
|
|
|
|
#ifdef PARANOID
|
|
|
|
|
|
void PASCAL CheckRange(
|
|
LPGROUPDEF lpgd,
|
|
LPTSTR lp1,
|
|
WORD* lpw1,
|
|
WORD cb1,
|
|
LPTSTR lp2,
|
|
WORD w2,
|
|
WORD cb2,
|
|
LPTSTR lpThing)
|
|
{
|
|
WORD w1 = *lpw1;
|
|
WORD e1, e2;
|
|
|
|
if (!w1 || (w1 == w2)) {
|
|
return;
|
|
}
|
|
|
|
if (!cb1) {
|
|
cb1 = (WORD)lstrlen((LPTSTR)PTR(lpgd, *lpw1));
|
|
}
|
|
|
|
e1 = w1 + cb1;
|
|
e2 = w2 + cb2;
|
|
|
|
if ((w1 < e2) && (w2 < e1)) {
|
|
KdPrint(("ERROR: %s overlaps %s in %s!!!!\r\n", lp2, lp1, lpThing));
|
|
}
|
|
}
|
|
|
|
|
|
void PASCAL CheckPointer(
|
|
LPGROUPDEF lpgd,
|
|
LPTSTR lp,
|
|
WORD* lpw,
|
|
WORD cb,
|
|
WORD limit)
|
|
{
|
|
LPITEMDEF lpid;
|
|
int i;
|
|
|
|
if (lpw == NULL || !*lpw) {
|
|
KdPrint(("Warning: %s is NULL\r\n", lp));
|
|
DebugBreak();
|
|
}
|
|
|
|
if (!cb) {
|
|
cb = lstrlen((LPTSTR)PTR(lpgd, *lpw));
|
|
}
|
|
|
|
if (*lpw + cb > limit) {
|
|
KdPrint(("ERROR: %s runs off end of group\r\n", lp));
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
void PASCAL VerifyGroup(
|
|
LPGROUPDEF lpgd)
|
|
{
|
|
int i;
|
|
LPITEMDEF lpid;
|
|
DWORD limit = lpgd->cbGroup;
|
|
|
|
KdPrint(("\r\nChecking Group %s\r\n", (LPTSTR)PTR(lpgd, lpgd->pName)));
|
|
CheckPointer(lpgd, TEXT("Group Name"), &lpgd->pName, 0, limit);
|
|
|
|
for (i = 0; i < (int)lpgd->cItems; i++) {
|
|
if (!lpgd->rgiItems[i]) {
|
|
continue;
|
|
}
|
|
|
|
lpid = ITEM(lpgd, i);
|
|
KdPrint(("Checking item %d at %4.4X (%s):\r\n", i, lpgd->rgiItems[i],
|
|
(LPTSTR)PTR(lpgd, lpid->pName)));
|
|
CheckPointer(lpgd, TEXT("Itemdef"), lpgd->rgiItems + i, sizeof(ITEMDEF), limit);
|
|
CheckPointer(lpgd, TEXT("Item name"), &lpid->pName, 0, limit);
|
|
CheckPointer(lpgd, TEXT("item command"), &lpid->pCommand, 0, limit);
|
|
CheckPointer(lpgd, TEXT("item icon path"), &lpid->pIconPath, 0, limit);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
BOOL FAR PASCAL IsGroupReadOnly(LPTSTR szGroupKey, BOOL bCommonGroup)
|
|
{
|
|
HKEY hkey;
|
|
HKEY hkeyGroups;
|
|
|
|
if (bCommonGroup)
|
|
hkeyGroups = hkeyCommonGroups;
|
|
else if (bUseANSIGroups)
|
|
hkeyGroups = hkeyAnsiProgramGroups;
|
|
else
|
|
hkeyGroups = hkeyProgramGroups;
|
|
|
|
if (!hkeyGroups)
|
|
return(FALSE);
|
|
|
|
if (!RegOpenKeyEx(hkeyGroups, szGroupKey, 0, DELETE | KEY_READ | KEY_WRITE, &hkey)) {
|
|
RegCloseKey(hkey);
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOL FAR PASCAL GroupCheck(PGROUP pGroup)
|
|
{
|
|
if (!fExiting && IsGroupReadOnly(pGroup->lpKey, pGroup->fCommon)) {
|
|
pGroup->fRO = TRUE;
|
|
return FALSE;
|
|
}
|
|
pGroup->fRO = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
INT MyDwordAlign(INT wStrLen)
|
|
{
|
|
return ((wStrLen + 3) & ~3);
|
|
}
|
|
|
|
|
|
DWORD PASCAL SizeofGroup(LPGROUPDEF lpgd)
|
|
{
|
|
LPPMTAG lptag;
|
|
DWORD cbSeg;
|
|
DWORD cb;
|
|
|
|
cbSeg = (DWORD)GlobalSize(lpgd);
|
|
|
|
lptag = (LPPMTAG)((LPSTR)lpgd + lpgd->cbGroup);
|
|
|
|
if ((DWORD)((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)) + 4) <= cbSeg
|
|
&& lptag->wID == ID_MAGIC
|
|
&& lptag->wItem == (int)0xFFFF
|
|
&& lptag->cb == (WORD)(MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)) + 4)
|
|
&& *(PLONG)lptag->rgb == PMTAG_MAGIC) {
|
|
while ((cb = (DWORD)((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)))) <= cbSeg) {
|
|
if (lptag->wID == ID_LASTTAG)
|
|
return cb;
|
|
(LPSTR)lptag += lptag->cb;
|
|
}
|
|
}
|
|
return lpgd->cbGroup;
|
|
}
|
|
|
|
|
|
|
|
/* Given the handle to the group's window, lock the group segment and return
|
|
* a pointer thereto. Reloads the group segment if it is not in memory.
|
|
*/
|
|
LPGROUPDEF FAR PASCAL LockGroup(HWND hwndGroup)
|
|
{
|
|
PGROUP pGroup;
|
|
LPGROUPDEF lpgd;
|
|
WORD status;
|
|
LPTSTR lpszKey;
|
|
HKEY hKey = NULL;
|
|
LONG err;
|
|
DWORD cbMaxValueLen = 0;
|
|
FILETIME ft;
|
|
TCHAR szClass[64];
|
|
DWORD dummy = 64;
|
|
DWORD cbSecDesc;
|
|
HKEY hkeyGroups;
|
|
BOOL bCommonGroup;
|
|
|
|
wLockError = 0; // No errors.
|
|
|
|
/* Find the handle and try to lock it. */
|
|
pGroup = (PGROUP)GetWindowLong(hwndGroup, GWLP_PGROUP);
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
|
|
/* If we got a non-NULL selector, return the pointer. */
|
|
if (pGroup->fLoaded)
|
|
return(lpgd);
|
|
|
|
if (lpgd) {
|
|
GlobalUnlock(pGroup->hGroup);
|
|
}
|
|
|
|
NukeIconBitmap(pGroup); // invalidate the bitmap
|
|
|
|
/* The group has been discarded, must reread the file... */
|
|
lpszKey = pGroup->lpKey;
|
|
|
|
pGroup->fRO = FALSE;
|
|
|
|
bCommonGroup = pGroup->fCommon;
|
|
if (bCommonGroup)
|
|
hkeyGroups = hkeyCommonGroups;
|
|
else if (bUseANSIGroups)
|
|
hkeyGroups = hkeyAnsiProgramGroups;
|
|
else
|
|
hkeyGroups = hkeyProgramGroups;
|
|
|
|
if (!hkeyGroups)
|
|
goto RegError;
|
|
|
|
/* Try to open the group key. */
|
|
if (err = RegOpenKeyEx(hkeyGroups, lpszKey, 0,
|
|
DELETE | KEY_READ | KEY_WRITE,
|
|
&hKey)) {
|
|
/* Try read-only access */
|
|
if (err = RegOpenKeyEx(hkeyGroups, lpszKey, 0,
|
|
KEY_READ, &hKey) || !hKey) {
|
|
status = IDS_NOGRPFILE;
|
|
goto LGError1;
|
|
}
|
|
if (!bUseANSIGroups) {
|
|
pGroup->fRO = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!(err = RegQueryInfoKey(hKey,
|
|
szClass,
|
|
&dummy, // cbClass
|
|
NULL, // Title index
|
|
&dummy, // cbSubKeys
|
|
&dummy, // cb Max subkey length
|
|
&dummy, // max class len
|
|
&dummy, // values count
|
|
&dummy, // max value name length
|
|
&cbMaxValueLen,
|
|
&cbSecDesc, // cb Security Descriptor
|
|
&ft))) {
|
|
if (!pGroup->ftLastWriteTime.dwLowDateTime &&
|
|
!pGroup->ftLastWriteTime.dwHighDateTime)
|
|
pGroup->ftLastWriteTime = ft;
|
|
else if (pGroup->ftLastWriteTime.dwLowDateTime != ft.dwLowDateTime ||
|
|
pGroup->ftLastWriteTime.dwHighDateTime != ft.dwHighDateTime) {
|
|
wLockError = LOCK_FILECHANGED;
|
|
status = IDS_GRPHASCHANGED;
|
|
if (!fExiting) // Don't reload changed groups on exit.
|
|
PostMessage(hwndProgman, WM_RELOADGROUP, (WPARAM)pGroup, 0L);
|
|
goto LGError2;
|
|
}
|
|
}
|
|
|
|
/* Find the size of the file by seeking to the end. */
|
|
if (cbMaxValueLen < sizeof(GROUPDEF)) {
|
|
status = IDS_BADFILE;
|
|
goto LGError2;
|
|
}
|
|
|
|
/* Allocate some memory for the thing. */
|
|
CheckBeforeReAlloc(pGroup->hGroup);
|
|
if (!GlobalReAlloc(pGroup->hGroup, (DWORD)cbMaxValueLen, GMEM_MOVEABLE)) {
|
|
wLockError = LOCK_LOWMEM;
|
|
status = IDS_LOWMEM;
|
|
lpszKey = NULL;
|
|
goto LGError2;
|
|
}
|
|
|
|
pGroup->fLoaded = TRUE;
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
|
|
/* Read the whole group data into memory. */
|
|
status = IDS_BADFILE;
|
|
if (err = RegQueryValueEx(hKey, NULL, 0, 0, (LPBYTE)lpgd, &cbMaxValueLen)) {
|
|
goto LGError3;
|
|
}
|
|
|
|
// If we start out from the ANSI groups, we need the security description
|
|
// to copy the entire information to the UNICODE groups
|
|
|
|
if (bUseANSIGroups) {
|
|
pGroup->pSecDesc = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, cbSecDesc);
|
|
RegGetKeySecurity(hKey, DACL_SECURITY_INFORMATION, pGroup->pSecDesc, &cbSecDesc);
|
|
} else {
|
|
pGroup->pSecDesc = NULL;
|
|
}
|
|
|
|
|
|
// If we loaded an old format ANSI group, then convert it to the
|
|
// UNICODE format and save it back in the registry.
|
|
|
|
if (lpgd->dwMagic == GROUP_MAGIC) {
|
|
HANDLE hUNIGroup;
|
|
|
|
if (cbMaxValueLen = ConvertToUnicodeGroup((LPGROUPDEF_A)lpgd, &hUNIGroup)) {
|
|
UnlockGroup(hwndGroup);
|
|
/* Free the ANSI group. */
|
|
GlobalFree(pGroup->hGroup);
|
|
pGroup->hGroup = hUNIGroup;
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
} else {
|
|
goto LGError3;
|
|
}
|
|
}
|
|
|
|
if (lpgd->dwMagic != GROUP_UNICODE)
|
|
goto LGError3;
|
|
|
|
if (lpgd->cbGroup > cbMaxValueLen)
|
|
goto LGError3;
|
|
|
|
/* Now return the pointer. */
|
|
RegCloseKey(hKey);
|
|
|
|
return(lpgd);
|
|
|
|
LGError3:
|
|
GlobalUnlock(pGroup->hGroup);
|
|
GlobalDiscard(pGroup->hGroup);
|
|
pGroup->fLoaded = FALSE;
|
|
|
|
LGError2:
|
|
RegCloseKey(hKey);
|
|
|
|
LGError1:
|
|
if (status != IDS_LOWMEM && status != IDS_GRPHASCHANGED && status != IDS_NOGRPFILE) {
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, status, pGroup->lpKey,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
if (status == IDS_BADFILE) {
|
|
|
|
// stop handling of Program Groups key changes.
|
|
|
|
bHandleProgramGroupsEvent = FALSE;
|
|
|
|
RegDeleteKey(hkeyGroups, lpszKey);
|
|
|
|
|
|
// reset handling of Program Groups key changes.
|
|
|
|
ResetProgramGroupsEvent(bCommonGroup);
|
|
bHandleProgramGroupsEvent = TRUE;
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Special case the group not being found so we can delete it's entry...
|
|
*/
|
|
if (status == IDS_NOGRPFILE) {
|
|
/*
|
|
* If no restrictions then we can fixup progman.ini...
|
|
*/
|
|
if (!fNoSave && dwEditLevel < 1) {
|
|
TCHAR szGroup[10];
|
|
|
|
if (MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_NOGRPFILE2, lpszKey, MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON1 | MB_SYSTEMMODAL) == IDNO) {
|
|
wsprintf(szGroup, TEXT("Group%d"), pGroup->wIndex);
|
|
|
|
// stop handling of Program Groups key changes.
|
|
|
|
bHandleProgramGroupsEvent = FALSE;
|
|
RegDeleteKey(hkeyProgramGroups, lpszKey);
|
|
|
|
|
|
// reset handling of Program Groups key changes.
|
|
|
|
ResetProgramGroupsEvent(bCommonGroup);
|
|
bHandleProgramGroupsEvent = TRUE;
|
|
RegDeleteValue(hkeyPMGroups, szGroup);
|
|
|
|
if (!fFirstLoad)
|
|
PostMessage(hwndProgman, WM_UNLOADGROUP, (WPARAM)hwndGroup, 0L);
|
|
}
|
|
} else {
|
|
RegError:
|
|
/*
|
|
* Restrictions mean that the user can only OK this error...
|
|
*/
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_NOGRPFILE, lpszKey,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
}
|
|
}
|
|
|
|
ShowWindow(hwndGroup, SW_SHOWMINNOACTIVE);
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
void FAR PASCAL UnlockGroup(register HWND hwndGroup)
|
|
{
|
|
GlobalUnlock(((PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP))->hGroup);
|
|
}
|
|
|
|
|
|
LPITEMDEF FAR PASCAL LockItem(PGROUP pGroup, PITEM pItem)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
|
|
lpgd = LockGroup(pGroup->hwnd);
|
|
|
|
if (!lpgd)
|
|
return((LPITEMDEF)NULL);
|
|
|
|
return ITEM(lpgd, pItem->iItem);
|
|
}
|
|
|
|
|
|
/*
|
|
* Sets or unsets the discardable flag for the given group file. If setting
|
|
* to non-discard, forces the group to be in memory.
|
|
*/
|
|
HANDLE PASCAL KeepGroupAround(HWND hwndGroup, BOOL fKeep)
|
|
{
|
|
PGROUP pGroup;
|
|
|
|
UNREFERENCED_PARAMETER(fKeep);
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
return pGroup->hGroup;
|
|
|
|
#ifdef ORGCODE
|
|
PGROUP pGroup;
|
|
WORD flag;
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
|
|
if (fKeep) {
|
|
if (LockGroup(hwndGroup)) {
|
|
UnlockGroup(hwndGroup); // it is still in memory
|
|
} else {
|
|
return NULL; // failure
|
|
}
|
|
|
|
flag = GMEM_MODIFY | GMEM_MOVEABLE; // make non discardable
|
|
} else {
|
|
flag = GMEM_MODIFY | GMEM_MOVEABLE | GMEM_DISCARDABLE; // discardable
|
|
}
|
|
|
|
return GlobalReAlloc(pGroup->hGroup, 0, flag);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/* SaveGroup() - */
|
|
|
|
|
|
|
|
/*
|
|
* Writes out a group file. It must already be in memory or the operation
|
|
* is meaningless.
|
|
*/
|
|
BOOL APIENTRY SaveGroup(
|
|
HWND hwndGroup, BOOL bDiscard
|
|
)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
HKEY hKey;
|
|
PGROUP pGroup;
|
|
WORD status = 0;
|
|
DWORD cb;
|
|
LONG err;
|
|
HKEY hkeyGroups;
|
|
BOOL bCommonGroup;
|
|
DWORD dwDisposition;
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
|
|
bCommonGroup = pGroup->fCommon;
|
|
|
|
if (!bUseANSIGroups && IsGroupReadOnly(pGroup->lpKey, bCommonGroup)) {
|
|
// Don't produce an error message for RO groups.
|
|
return FALSE;
|
|
}
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
if (!lpgd) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (bCommonGroup)
|
|
hkeyGroups = hkeyCommonGroups;
|
|
else
|
|
hkeyGroups = hkeyProgramGroups;
|
|
|
|
if (!hkeyGroups) {
|
|
goto Exit1;
|
|
}
|
|
|
|
// it may already exist
|
|
|
|
if (err = RegCreateKeyEx(hkeyGroups, pGroup->lpKey, 0, 0, 0, DELETE | KEY_READ | KEY_WRITE | WRITE_DAC, pSecurityAttributes, &hKey, &dwDisposition)) {
|
|
//if (err = RegOpenKeyEx(hkeyGroups, pGroup->lpKey, 0,
|
|
// KEY_SET_VALUE, &hKey)) {
|
|
/*
|
|
* We can't open output group key.
|
|
*/
|
|
if (err = RegOpenKeyEx(hkeyGroups, pGroup->lpKey, 0, KEY_READ, &hKey)) {
|
|
status = IDS_NOGRPFILE;
|
|
} else {
|
|
// status = IDS_GRPISRO;
|
|
RegCloseKey(hKey);
|
|
}
|
|
goto Exit1;
|
|
} else {
|
|
if (dwDisposition == REG_CREATED_NEW_KEY && bUseANSIGroups) {
|
|
RegSetKeySecurity(hKey, DACL_SECURITY_INFORMATION, pGroup->pSecDesc);
|
|
LocalFree(pGroup->pSecDesc);
|
|
pGroup->pSecDesc = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// stop handling Program Groups key changes for a SAveGroup.
|
|
|
|
bHandleProgramGroupsEvent = FALSE;
|
|
|
|
cb = SizeofGroup(lpgd);
|
|
if (err = RegSetValueEx(hKey, NULL, 0, REG_BINARY, (LPBYTE)lpgd, cb)) {
|
|
status = IDS_CANTWRITEGRP;
|
|
}
|
|
|
|
RegFlushKey(hKey);
|
|
RegCloseKey(hKey);
|
|
|
|
pGroup->ftLastWriteTime.dwLowDateTime = 0; // update file time stamp if we need to reload
|
|
pGroup->ftLastWriteTime.dwHighDateTime = 0;
|
|
|
|
Exit1:
|
|
GlobalUnlock(pGroup->hGroup);
|
|
|
|
if (status && !fExiting) {
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, status, pGroup->lpKey,
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
|
|
/*
|
|
* Force the group to be reset.
|
|
*/
|
|
if (bDiscard) {
|
|
GlobalDiscard(pGroup->hGroup);
|
|
pGroup->fLoaded = FALSE;
|
|
InvalidateRect(pGroup->hwnd, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
// reset handling of Program Groups key changes.
|
|
|
|
ResetProgramGroupsEvent(bCommonGroup);
|
|
bHandleProgramGroupsEvent = TRUE;
|
|
return (status == 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* AdjustPointers() - */
|
|
|
|
|
|
|
|
/*
|
|
* Adjusts pointers in the segment after a section is moved up or down.
|
|
*/
|
|
void PASCAL AdjustPointers(LPGROUPDEF lpgd, DWORD iFirst, DWORD di)
|
|
{
|
|
WORD i;
|
|
LPITEMDEF lpid;
|
|
|
|
if (lpgd->pName >= iFirst) {
|
|
lpgd->pName += di;
|
|
}
|
|
|
|
for (i = 0; i < lpgd->cItems; i++) {
|
|
if (!lpgd->rgiItems[i]) {
|
|
continue;
|
|
}
|
|
|
|
if (lpgd->rgiItems[i] >= iFirst) {
|
|
lpgd->rgiItems[i] += di;
|
|
}
|
|
|
|
lpid = ITEM(lpgd, i);
|
|
|
|
if (lpid->pIconRes >= iFirst)
|
|
lpid->pIconRes += di;
|
|
if (lpid->pName >= iFirst)
|
|
lpid->pName += di;
|
|
if (lpid->pCommand >= iFirst)
|
|
lpid->pCommand += di;
|
|
if (lpid->pIconPath >= iFirst)
|
|
lpid->pIconPath += di;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* FindFreeItemIndex() - */
|
|
|
|
/* Returns the index of a free slot in the item offset array. If necessary,*/
|
|
/* moves stuff around. */
|
|
|
|
|
|
|
|
WORD PASCAL FindFreeItemIndex(HWND hwndGroup)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
PGROUP pGroup;
|
|
WORD i;
|
|
LPTSTR lp1;
|
|
LPTSTR lp2;
|
|
DWORD cb;
|
|
|
|
lpgd = LockGroup(hwndGroup);
|
|
if (!lpgd) {
|
|
return(0xFFFF);
|
|
}
|
|
|
|
for (i = 0; i < lpgd->cItems; i++) {
|
|
if (!lpgd->rgiItems[i]) {
|
|
UnlockGroup(hwndGroup);
|
|
return(i);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Didn't find an empty slot... make some new ones.
|
|
*/
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
|
|
// Current groups+tags size.
|
|
cb = SizeofGroup(lpgd);
|
|
|
|
// Increase space reserved item info.
|
|
lpgd->cbGroup += NSLOTS * sizeof(DWORD);
|
|
|
|
// Increase size of whole group.
|
|
cb += NSLOTS * sizeof(DWORD);
|
|
|
|
UnlockGroup(hwndGroup);
|
|
|
|
CheckBeforeReAlloc(pGroup->hGroup);
|
|
if (!GlobalReAlloc(pGroup->hGroup, cb, GMEM_MOVEABLE)) {
|
|
return 0xFFFF;
|
|
}
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
|
|
/*
|
|
* Copy tags junk (which starts at the end of the rgiItems array)
|
|
* up a bit to make room for the bigger array..
|
|
*/
|
|
lp1 = (LPTSTR) & (lpgd->rgiItems[lpgd->cItems]);
|
|
lp2 = (LPTSTR) & (lpgd->rgiItems[lpgd->cItems + NSLOTS]);
|
|
|
|
/*
|
|
* Copy everything down in the segment.
|
|
*/
|
|
RtlMoveMemory(lp2, lp1, (WORD)(cb - (DWORD)((LPSTR)lp2 - (LPSTR)lpgd)));
|
|
|
|
/*
|
|
* Zero out the new offsets.
|
|
*/
|
|
for (i = (WORD)lpgd->cItems; i < (WORD)(lpgd->cItems + NSLOTS); i++) {
|
|
lpgd->rgiItems[i] = 0;
|
|
}
|
|
|
|
i = lpgd->cItems;
|
|
|
|
/* Record that we now have more slots */
|
|
lpgd->cItems += NSLOTS;
|
|
|
|
/*
|
|
* Fix up all the offsets in the segment. Since the rgiItems array is
|
|
* part of the group header, all the pointers will change.
|
|
*/
|
|
AdjustPointers(lpgd, (WORD)1, NSLOTS * sizeof(DWORD));
|
|
|
|
GlobalUnlock(pGroup->hGroup);
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* DeleteThing() - */
|
|
|
|
|
|
/* Removes a part of the group segment. Updates everything in the segment */
|
|
/* but does not realloc. */
|
|
|
|
|
|
|
|
void NEAR PASCAL DeleteThing(LPGROUPDEF lpgd, LPDWORD lpiThing, WORD cbThing)
|
|
{
|
|
DWORD dwThingOffset;
|
|
LPTSTR lp1;
|
|
LPTSTR lp2;
|
|
INT cb;
|
|
WORD cbThingSize;
|
|
|
|
if (cbThing == 0xFFFF) {
|
|
return;
|
|
}
|
|
|
|
dwThingOffset = *lpiThing;
|
|
|
|
if (!dwThingOffset)
|
|
return;
|
|
|
|
*lpiThing = 0;
|
|
|
|
lp1 = (LPTSTR)PTR(lpgd, dwThingOffset);
|
|
|
|
/* If its a string we're removing, the caller can pass 0 as the length
|
|
* and have it calculated!!!
|
|
*/
|
|
if (!cbThing) {
|
|
cbThing = (WORD)sizeof(TCHAR) * (1 + lstrlen(lp1));
|
|
}
|
|
|
|
cbThingSize = (WORD)MyDwordAlign((int)cbThing);
|
|
|
|
lp2 = (LPTSTR)((LPBYTE)lp1 + cbThingSize);
|
|
|
|
cb = (int)SizeofGroup(lpgd);
|
|
|
|
RtlMoveMemory(lp1, lp2, (cb - (DWORD)((LPSTR)lp2 - (LPSTR)lpgd)));
|
|
|
|
lpgd->cbGroup -= cbThingSize;
|
|
|
|
AdjustPointers(lpgd, dwThingOffset, -cbThingSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* AddThing() - */
|
|
|
|
/* in: */
|
|
/* hGroup group handle, must not be discardable */
|
|
/* lpStuff pointer to data or NULL to init data to zero */
|
|
/* cbStuff count of item (may be 0) if lpStuff is a string */
|
|
|
|
/* Adds an object to the group segment and returns its offset. Will */
|
|
/* reallocate the segment if necessary. */
|
|
|
|
/* Handle passed in must not be discardable */
|
|
|
|
/* returns: */
|
|
/* 0 failure */
|
|
/* > 0 offset to thing in the segment */
|
|
|
|
|
|
|
|
DWORD PASCAL AddThing(HANDLE hGroup, LPTSTR lpStuff, DWORD cbStuff)
|
|
{
|
|
DWORD cb;
|
|
LPGROUPDEF lpgd;
|
|
DWORD offset;
|
|
LPTSTR lpT;
|
|
DWORD cbStuffSize;
|
|
DWORD cbGroupSize;
|
|
DWORD myOffset;
|
|
|
|
if (cbStuff == 0xFFFFFFFF) {
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
if (!cbStuff) {
|
|
cbStuff = sizeof(TCHAR) * (DWORD)(1 + lstrlen(lpStuff));
|
|
}
|
|
|
|
cbStuffSize = MyDwordAlign((int)cbStuff);
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(hGroup);
|
|
cb = SizeofGroup(lpgd);
|
|
cbGroupSize = MyDwordAlign((int)cb);
|
|
|
|
offset = lpgd->cbGroup;
|
|
myOffset = (DWORD)MyDwordAlign((int)offset);
|
|
|
|
GlobalUnlock(hGroup);
|
|
|
|
CheckBeforeReAlloc(hGroup);
|
|
if (!GlobalReAlloc(hGroup, (DWORD)(cbGroupSize + cbStuffSize), GMEM_MOVEABLE))
|
|
return 0;
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(hGroup);
|
|
|
|
/*
|
|
* Slide the tags up
|
|
*/
|
|
RtlMoveMemory((LPSTR)lpgd + myOffset + cbStuffSize, (LPSTR)lpgd + myOffset,
|
|
(cbGroupSize - myOffset));
|
|
lpgd->cbGroup += cbStuffSize;
|
|
|
|
lpT = (LPTSTR)((LPSTR)lpgd + myOffset);
|
|
if (lpStuff) {
|
|
RtlMoveMemory(lpT, lpStuff, cbStuff);
|
|
|
|
} else {
|
|
/*
|
|
* Zero it
|
|
*/
|
|
while (cbStuffSize--) {
|
|
*((LPSTR)lpT)++ = 0;
|
|
}
|
|
}
|
|
|
|
|
|
GlobalUnlock(hGroup);
|
|
|
|
return myOffset;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FindTag() - */
|
|
|
|
|
|
|
|
LPPMTAG NEAR PASCAL FindTag(LPGROUPDEF lpgd, int item, WORD id)
|
|
{
|
|
LPPMTAG lptag;
|
|
int cbSeg;
|
|
int cb;
|
|
|
|
cbSeg = (DWORD)GlobalSize(lpgd);
|
|
|
|
lptag = (LPPMTAG)((LPSTR)lpgd + lpgd->cbGroup);
|
|
|
|
if ((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)) + 4 <= cbSeg
|
|
&& lptag->wID == ID_MAGIC
|
|
&& lptag->wItem == (int)0xFFFF
|
|
&& lptag->cb == (WORD)(MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)) + 4)
|
|
&& *(LONG FAR*)lptag->rgb == PMTAG_MAGIC) {
|
|
|
|
while ((cb = (int)((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)))) <= cbSeg) {
|
|
if ((item == lptag->wItem)
|
|
&& (id == 0 || id == lptag->wID)) {
|
|
return lptag;
|
|
}
|
|
|
|
if (lptag->wID == ID_LASTTAG)
|
|
return NULL;
|
|
|
|
(LPSTR)lptag += lptag->cb;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/* CopyTag() - */
|
|
|
|
|
|
|
|
INT FAR PASCAL CopyTag(LPGROUPDEF lpgd, int item, WORD id, LPTSTR lpbuf, int cb)
|
|
{
|
|
LPTSTR lpt;
|
|
LPPMTAG lptag;
|
|
WORD cbT;
|
|
|
|
lptag = FindTag(lpgd, item, id);
|
|
|
|
if (lptag == NULL)
|
|
return 0;
|
|
|
|
if (cb > (int)lptag->cb)
|
|
cb = lptag->cb;
|
|
|
|
cbT = (WORD)cb;
|
|
|
|
lpt = (LPTSTR)lptag->rgb;
|
|
|
|
while (*lpt && cbT) {
|
|
*lpbuf++ = *lpt++;
|
|
cbT--;
|
|
}
|
|
|
|
if (!(*lpt) && cbT) {
|
|
*lpbuf = TEXT('\0');
|
|
}
|
|
|
|
return cb;
|
|
}
|
|
|
|
|
|
|
|
/* DeleteTag() - */
|
|
|
|
/* in: */
|
|
/* hGroup group handle, can be discardable (alwayws shrink object) */
|
|
|
|
|
|
|
|
VOID FAR PASCAL DeleteTag(HANDLE hGroup, int item, WORD id)
|
|
{
|
|
LPPMTAG lptag;
|
|
LPTSTR lp1, lp2;
|
|
LPTSTR lpend;
|
|
LPGROUPDEF lpgd;
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(hGroup);
|
|
|
|
lptag = FindTag(lpgd, item, id);
|
|
|
|
if (lptag == NULL) {
|
|
GlobalUnlock(hGroup);
|
|
return;
|
|
}
|
|
|
|
lp1 = (LPTSTR)lptag;
|
|
|
|
lp2 = (LPTSTR)((LPSTR)lptag + lptag->cb);
|
|
|
|
lpend = (LPTSTR)((LPSTR)lpgd + SizeofGroup(lpgd));
|
|
|
|
while (lp2 < lpend) {
|
|
*lp1++ = *lp2++;
|
|
}
|
|
|
|
/* always reallocing smaller
|
|
*/
|
|
GlobalUnlock(hGroup);
|
|
CheckBeforeReAlloc(hGroup);
|
|
GlobalReAlloc(hGroup, (DWORD)((LPSTR)lp1 - (LPSTR)lpgd), 0);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* AddTag() - */
|
|
|
|
/* in: */
|
|
/* h group handle, must not be discardable! */
|
|
|
|
/* returns: */
|
|
/* 0 failure */
|
|
/* 1 success */
|
|
|
|
INT PASCAL AddTag(HANDLE h, int item, WORD id, LPTSTR lpbuf, int cb)
|
|
{
|
|
LPPMTAG lptag;
|
|
WORD fAddFirst;
|
|
LPGROUPDEF lpgd;
|
|
int cbNew;
|
|
int cbMyLen;
|
|
LPGROUPDEF lpgdOld;
|
|
|
|
|
|
if (!cb && lpbuf) {
|
|
cb = sizeof(TCHAR) * (lstrlen(lpbuf) + 1);
|
|
}
|
|
cbMyLen = MyDwordAlign(cb);
|
|
|
|
if (!lpbuf) {
|
|
cb = 0;
|
|
cbMyLen = 0;
|
|
}
|
|
|
|
/*
|
|
* Remove the old version of the tag, if any.
|
|
*/
|
|
DeleteTag(h, item, id);
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(h);
|
|
|
|
lptag = FindTag(lpgd, (int)0xFFFF, (WORD)ID_LASTTAG);
|
|
|
|
if (!lptag) {
|
|
/*
|
|
* In this case, there are no tags at all, and we have to add
|
|
* the first tag, the interesting tag, and the last tag
|
|
*/
|
|
cbNew = 3 * (MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb))) + 4 + cbMyLen;
|
|
fAddFirst = TRUE;
|
|
lptag = (LPPMTAG)((LPSTR)lpgd + lpgd->cbGroup);
|
|
|
|
} else {
|
|
/*
|
|
* In this case, only the interesting tag needs to be added
|
|
* but we count in the last because the delta is from lptag
|
|
*/
|
|
cbNew = 2 * (MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb))) + cbMyLen;
|
|
fAddFirst = FALSE;
|
|
}
|
|
|
|
/*
|
|
* check for 64K limit
|
|
*/
|
|
if ((DWORD_PTR)lptag + cbNew < (DWORD_PTR)lptag) {
|
|
return 0;
|
|
}
|
|
|
|
cbNew += (int)((PCHAR)lptag - (PCHAR)lpgd);
|
|
lpgdOld = lpgd;
|
|
GlobalUnlock(h);
|
|
CheckBeforeReAlloc(h);
|
|
if (!GlobalReAlloc(h, (DWORD)cbNew, GMEM_MOVEABLE)) {
|
|
return 0;
|
|
}
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(h);
|
|
lptag = (LPPMTAG)((LPSTR)lpgd + ((LPSTR)lptag - (LPSTR)lpgdOld));
|
|
if (fAddFirst) {
|
|
/*
|
|
* Add the first tag
|
|
*/
|
|
lptag->wID = ID_MAGIC;
|
|
lptag->wItem = (int)0xFFFF;
|
|
*(LONG FAR*)lptag->rgb = PMTAG_MAGIC;
|
|
lptag->cb = (WORD)(MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)) + 4);
|
|
(LPSTR)lptag += lptag->cb;
|
|
}
|
|
|
|
/*
|
|
* Add the tag
|
|
*/
|
|
lptag->wID = id;
|
|
lptag->wItem = item;
|
|
lptag->cb = (WORD)(MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)) + cbMyLen);
|
|
if (lpbuf) {
|
|
RtlMoveMemory(lptag->rgb, lpbuf, (WORD)cb);
|
|
}
|
|
(LPSTR)lptag += lptag->cb;
|
|
|
|
/*
|
|
* Add the end tag
|
|
*/
|
|
lptag->wID = ID_LASTTAG;
|
|
lptag->wItem = (int)0xFFFF;
|
|
lptag->cb = 0;
|
|
|
|
GlobalUnlock(h);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* NukeIconBitmap -- Deletes the icon bitmap if one exists for the group */
|
|
|
|
|
|
|
|
void PASCAL NukeIconBitmap(PGROUP pGroup)
|
|
{
|
|
if (pGroup->hbm) {
|
|
DeleteObject(pGroup->hbm);
|
|
pGroup->hbm = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* GroupFlag() - */
|
|
|
|
|
|
|
|
WORD PASCAL GroupFlag(PGROUP pGroup, PITEM pItem, WORD wFlag)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
LPPMTAG lptag;
|
|
WORD wT = 0;
|
|
int wItem;
|
|
|
|
if (pItem) {
|
|
wItem = pItem->iItem;
|
|
} else {
|
|
wItem = (int)0xFFFF;
|
|
}
|
|
|
|
lpgd = LockGroup(pGroup->hwnd);
|
|
if (!lpgd)
|
|
return 0;
|
|
|
|
lptag = FindTag(lpgd, wItem, wFlag);
|
|
|
|
if (!lptag)
|
|
wT = 0;
|
|
else if (lptag->cb == (WORD)(MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb))))
|
|
wT = 1;
|
|
else if (lptag->cb == (WORD)(MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)) + 1))
|
|
wT = lptag->rgb[0];
|
|
else if (lptag->cb == (WORD)(MyDwordAlign(sizeof(PMTAG)) - MyDwordAlign(sizeof(lptag->rgb)) + 4))
|
|
wT = *(LPWORD)lptag->rgb;
|
|
UnlockGroup(pGroup->hwnd);
|
|
|
|
return wT;
|
|
}
|
|
|
|
|
|
|
|
/* GetGroupTag() - */
|
|
|
|
|
|
|
|
WORD PASCAL GetGroupTag(
|
|
PGROUP pGroup,
|
|
PITEM pItem,
|
|
WORD id,
|
|
LPTSTR lpT,
|
|
WORD cb)
|
|
{
|
|
WORD wT;
|
|
LPGROUPDEF lpgd;
|
|
int wItem;
|
|
|
|
if (pItem)
|
|
wItem = pItem->iItem;
|
|
else
|
|
wItem = (int)0xFFFF;
|
|
|
|
lpgd = LockGroup(pGroup->hwnd);
|
|
if (!lpgd)
|
|
return 0;
|
|
|
|
wT = (WORD)CopyTag(lpgd, wItem, id, lpT, cb);
|
|
|
|
UnlockGroup(pGroup->hwnd);
|
|
return wT;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ChangeTagID() - */
|
|
|
|
|
|
|
|
void PASCAL ChangeTagID(
|
|
LPGROUPDEF lpgd,
|
|
int iOld,
|
|
int iNew)
|
|
{
|
|
LPPMTAG lptag;
|
|
|
|
while (lptag = FindTag(lpgd, iOld, 0)) {
|
|
lptag->wItem = iNew;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* LoadItem() - */
|
|
|
|
/* Creates an item window (iconic) within a group window. Assumes that the */
|
|
/* group segment is up to date. */
|
|
|
|
|
|
|
|
PITEM PASCAL LoadItem(HWND hwndGroup, WORD iItem, BOOL bActivate)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
LPITEMDEF lpid;
|
|
PGROUP pGroup;
|
|
PITEM pItem;
|
|
PITEM* ppItem;
|
|
|
|
lpgd = LockGroup(hwndGroup);
|
|
if (!lpgd)
|
|
return NULL;
|
|
|
|
pItem = (PITEM)LocalAlloc(LPTR, sizeof(ITEM));
|
|
if (!pItem) {
|
|
UnlockGroup(hwndGroup);
|
|
return NULL;
|
|
}
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
|
|
NukeIconBitmap(pGroup);
|
|
|
|
if (bActivate) {
|
|
InvalidateIcon(pGroup, pGroup->pItems);
|
|
pItem->pNext = pGroup->pItems;
|
|
pGroup->pItems = pItem;
|
|
} else {
|
|
ppItem = &pGroup->pItems;
|
|
while (*ppItem) {
|
|
ppItem = &((*ppItem)->pNext);
|
|
}
|
|
pItem->pNext = NULL;
|
|
*ppItem = pItem;
|
|
}
|
|
|
|
lpid = ITEM(lpgd, iItem);
|
|
|
|
pItem->iItem = iItem;
|
|
pItem->dwDDEId = 0;
|
|
SetRectEmpty(&pItem->rcTitle);
|
|
SetRectEmpty(&pItem->rcIcon);
|
|
|
|
ComputeIconPosition(pGroup, lpid->pt, &pItem->rcIcon, &pItem->rcTitle,
|
|
(LPTSTR)PTR(lpgd, lpid->pName));
|
|
|
|
UnlockGroup(hwndGroup);
|
|
|
|
InvalidateIcon(pGroup, pItem);
|
|
|
|
return pItem;
|
|
}
|
|
|
|
|
|
PITEM FindItemName(LPGROUPDEF lpgd, register PITEM pItem, LPTSTR lpTitle)
|
|
{
|
|
LPITEMDEF lpid;
|
|
|
|
while (pItem) {
|
|
lpid = ITEM(lpgd, pItem->iItem);
|
|
|
|
if (!lstrcmp(lpTitle, (LPTSTR)PTR(lpgd, lpid->pName)))
|
|
return pItem;
|
|
|
|
pItem = pItem->pNext;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* CreateNewItem() - */
|
|
|
|
|
|
|
|
/*
|
|
* Creates a new item in the file, and adds a window for it.
|
|
*/
|
|
PITEM PASCAL CreateNewItem(
|
|
HWND hwndGroup,
|
|
LPTSTR lpTitle,
|
|
LPTSTR lpCommand,
|
|
LPTSTR lpIconPath,
|
|
LPTSTR lpDefDir,
|
|
WORD wHotKey,
|
|
BOOL fMinimize,
|
|
WORD wIconId,
|
|
WORD wIconIndex,
|
|
HICON hIcon,
|
|
LPPOINT lppt,
|
|
DWORD dwFlags)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
LPITEMDEF lpid;
|
|
WORD id;
|
|
DWORD offset;
|
|
PGROUP pGroup;
|
|
LPTSTR lpIconRes;
|
|
WORD cbIconRes;
|
|
//DWORD dwVer;
|
|
WORD wVer;
|
|
WORD idError = IDS_LOWMEM;
|
|
PITEM pItem;
|
|
DWORD cb;
|
|
TCHAR szCommand[3 * MAX_PATH];
|
|
TCHAR szExeDir[MAXITEMPATHLEN + 1];
|
|
TCHAR szIconExe[MAX_PATH];
|
|
TCHAR szTemp[MAXITEMPATHLEN + 1];
|
|
LPTSTR lp1, lp2, lp3;
|
|
HANDLE hIconRes;
|
|
HANDLE hModule;
|
|
BOOL fWin32App = FALSE;
|
|
BOOL fUseDefaultIcon = FALSE;
|
|
BOOL bNoIconPath = TRUE;
|
|
TCHAR cSeparator;
|
|
|
|
/*
|
|
* Before we do anything, whack the command line and exedir
|
|
*/
|
|
lp1 = lpCommand;
|
|
if (*lpCommand == TEXT('"') && wcschr(lpCommand + 1, TEXT('"'))) {
|
|
cSeparator = TEXT('"');
|
|
//lp1++;
|
|
} else {
|
|
cSeparator = TEXT(' ');
|
|
}
|
|
for (lp2 = lp3 = szExeDir; *lp1 && *lp1 != cSeparator; lp1 = CharNext(lp1)) {
|
|
*lp2++ = *lp1;
|
|
|
|
/*
|
|
* We know we're looking at the first byte
|
|
*/
|
|
if ((*lp1 == TEXT(':')) || (*lp1 == TEXT('\\'))) {
|
|
lp3 = lp2;
|
|
}
|
|
}
|
|
|
|
*lp2 = 0;
|
|
|
|
/*
|
|
* If the default dir pointer is NULL then we use the directory
|
|
* component of the command line. Otherwise we do the normal
|
|
* path whacking stuff to get everything into 3.0 format.
|
|
*/
|
|
if (lpDefDir) {
|
|
LPTSTR lpT;
|
|
|
|
lpT = lpDefDir;
|
|
// We have a valid pointer.
|
|
lstrcpy(szCommand, lpDefDir);
|
|
#if 0
|
|
/* spaces are allowed in LFN.
|
|
*/
|
|
RemoveLeadingSpaces(szCommand);
|
|
#endif
|
|
|
|
// If a default dir was supplied then go ahead and whack it
|
|
// into 3.0 format otherwise leave it blank.
|
|
if (*lpDefDir) {
|
|
LPTSTR lpNextChar;
|
|
|
|
// locate the character before the NULL
|
|
while (*(lpNextChar = CharNext(lpDefDir)))
|
|
lpDefDir = lpNextChar;
|
|
|
|
// If there is no '\' seperator, add one.
|
|
if (lpDefDir[0] != TEXT('\\')) {
|
|
lstrcat(szCommand, TEXT("\\"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now add the filename itself. this puts the command in the
|
|
* 3.0 format: defdir\exename
|
|
*/
|
|
lstrcat(szCommand, lp3);
|
|
|
|
/*
|
|
* Append the arguments
|
|
*/
|
|
lstrcat(szCommand, lp1);
|
|
lpDefDir = lpT;
|
|
|
|
} else {
|
|
/*
|
|
* Use the same command line (note def dir is assigned exe dir
|
|
*/
|
|
lstrcpy(szCommand, lpCommand);
|
|
}
|
|
|
|
/*
|
|
* Now truncate exedir so that it does not include the command filename
|
|
*/
|
|
*lp3 = 0;
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
|
|
if (!GroupCheck(pGroup)) {
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_GROUPRO,
|
|
(LPTSTR)PTR(lpgd, lpgd->pName),
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
GlobalUnlock(pGroup->hGroup);
|
|
return NULL;
|
|
}
|
|
GlobalUnlock(pGroup->hGroup);
|
|
|
|
if (*lpIconPath) {
|
|
bNoIconPath = FALSE;
|
|
}
|
|
|
|
lstrcpy(szIconExe, lpIconPath);
|
|
if (bNoIconPath && !(dwFlags & CI_NO_ASSOCIATION)) {
|
|
lstrcpy(szIconExe, lpCommand);
|
|
}
|
|
DoEnvironmentSubst(szIconExe, (WORD)CharSizeOf(szIconExe));
|
|
StripArgs(szIconExe);
|
|
if (!bNoIconPath) {
|
|
TagExtension(szIconExe, sizeof(szIconExe));
|
|
}
|
|
|
|
if (*szIconExe == TEXT('"') && *(szIconExe + lstrlen(szIconExe) - 1) == TEXT('"')) {
|
|
SheRemoveQuotes(szIconExe);
|
|
}
|
|
|
|
if (bNoIconPath) {
|
|
|
|
// if it's a relative path, extractassociatedicon and LoadLibrary don't
|
|
// handle that so find the executable first
|
|
|
|
SetCurrentDirectory(szOriginalDirectory);
|
|
FindExecutable(szIconExe, lpDefDir, szTemp);
|
|
if (*szTemp) {
|
|
lstrcpy(szIconExe, szTemp);
|
|
TagExtension(szIconExe, sizeof(szIconExe));
|
|
if (*szIconExe == TEXT('"') && *(szIconExe + lstrlen(szIconExe) - 1) == TEXT('"')) {
|
|
SheRemoveQuotes(szIconExe);
|
|
}
|
|
} else {
|
|
*szIconExe = 0; // Use a dummy value so no icons will be found
|
|
// and progman's item icon will be used instead
|
|
// This is to make moricons.dll item icon be the
|
|
// right one. -johannec 6/4/93
|
|
}
|
|
|
|
// reset the current directory to progman's working directory i.e. Windows directory
|
|
|
|
SetCurrentDirectory(szWindowsDirectory);
|
|
|
|
wIconId = 0;
|
|
wIconIndex = 0;
|
|
}
|
|
|
|
|
|
NoIcon:
|
|
|
|
if (!wIconId) {
|
|
TCHAR szOldIconExe[MAX_PATH];
|
|
|
|
lstrcpy(szOldIconExe, szIconExe);
|
|
hIcon = ExtractAssociatedIconEx(hAppInstance, szIconExe, &wIconIndex, &wIconId);
|
|
if (lstrcmp(szOldIconExe, szIconExe)) {
|
|
/* using default icon from Progman.exe */
|
|
fUseDefaultIcon = TRUE;
|
|
}
|
|
if (hIcon)
|
|
DestroyIcon(hIcon);
|
|
}
|
|
|
|
lpIconRes = NULL;
|
|
hIconRes = NULL;
|
|
if (hModule = LoadLibrary(szIconExe)) {
|
|
fWin32App = TRUE;
|
|
hIconRes = FindResource(hModule, (LPTSTR)MAKEINTRESOURCE(wIconId), (LPTSTR)MAKEINTRESOURCE(RT_ICON));
|
|
if (hIconRes) {
|
|
//dwVer = 0x00030000; // resource version is windows 3.x
|
|
wVer = 3; // resource version is windows 3.x
|
|
cbIconRes = (WORD)SizeofResource(hModule, hIconRes);
|
|
hIconRes = LoadResource(hModule, hIconRes);
|
|
lpIconRes = LockResource(hIconRes);
|
|
}
|
|
if (fUseDefaultIcon) {
|
|
wIconId = 0;
|
|
}
|
|
} else { // Win 3.1 app
|
|
|
|
if (wVer = ExtractIconResInfo(hAppInstance, szIconExe, wIconIndex, &cbIconRes, &hIconRes)) {
|
|
lpIconRes = GlobalLock(hIconRes);
|
|
}
|
|
}
|
|
|
|
if (!lpIconRes) {
|
|
wIconId = 0;
|
|
wIconIndex = 0;
|
|
|
|
// ToddB: I see no harm in always setting the current directory to the WinDir
|
|
// before jumping back to NoIcon. Seems to be required to fix a Japanese bug.
|
|
// The WinDir is the default directory of Progman anyhow, I really don't see
|
|
// where it's possible for us to not already be in this directory.
|
|
SetCurrentDirectory(szWindowsDirectory);
|
|
|
|
goto NoIcon;
|
|
}
|
|
|
|
if (!KeepGroupAround(hwndGroup, TRUE)) {
|
|
goto FreeIcon;
|
|
}
|
|
|
|
id = FindFreeItemIndex(hwndGroup);
|
|
if (id == 0xFFFF) {
|
|
goto FreeIcon;
|
|
}
|
|
|
|
if (id >= CITEMSMAX) { // check group size limit
|
|
idError = IDS_TOOMANYITEMS;
|
|
goto FreeIcon;
|
|
}
|
|
|
|
offset = AddThing(pGroup->hGroup, (TCHAR)0, (WORD)sizeof(ITEMDEF));
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
|
|
if (!offset) {
|
|
goto QuitThis;
|
|
}
|
|
|
|
lpgd->rgiItems[id] = offset;
|
|
lpid = ITEM(lpgd, id);
|
|
|
|
GlobalUnlock(pGroup->hGroup);
|
|
offset = AddThing(pGroup->hGroup, lpTitle, (WORD)0);
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
lpid = ITEM(lpgd, id);
|
|
if (!offset) {
|
|
goto PuntCreation;
|
|
}
|
|
|
|
lpid->pName = offset;
|
|
|
|
GlobalUnlock(pGroup->hGroup);
|
|
offset = AddThing(pGroup->hGroup, szCommand, (WORD)0);
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
lpid = ITEM(lpgd, id);
|
|
if (!offset) {
|
|
goto PuntCreation;
|
|
}
|
|
lpid->pCommand = offset;
|
|
|
|
GlobalUnlock(pGroup->hGroup);
|
|
CheckEscapes(szIconExe, CharSizeOf(szIconExe));
|
|
offset = AddThing(pGroup->hGroup, szIconExe, (WORD)0);
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
lpid = ITEM(lpgd, id);
|
|
if (!offset)
|
|
goto PuntCreation;
|
|
lpid->pIconPath = offset;
|
|
|
|
GlobalUnlock(pGroup->hGroup);
|
|
offset = AddThing(pGroup->hGroup, lpIconRes, cbIconRes);
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
lpid = ITEM(lpgd, id);
|
|
if (!offset)
|
|
goto PuntCreation;
|
|
lpid->pIconRes = offset;
|
|
|
|
if (lppt) {
|
|
lpid->pt = *lppt;
|
|
} else {
|
|
lpid->pt.x = lpid->pt.y = -1;
|
|
}
|
|
|
|
lpid->iIcon = wIconId;
|
|
lpid->wIconIndex = wIconIndex;
|
|
//lpid->dwIconVer = dwVer;
|
|
lpid->wIconVer = wVer;
|
|
lpid->cbIconRes = cbIconRes;
|
|
|
|
if (cbIconRes != 0xFFFF)
|
|
if (fWin32App) {
|
|
UnlockResource(hIconRes);
|
|
FreeResource(hIconRes);
|
|
FreeLibrary(hModule);
|
|
} else {
|
|
GlobalUnlock(hIconRes);
|
|
GlobalFree(hIconRes);
|
|
}
|
|
GlobalUnlock(pGroup->hGroup);
|
|
|
|
if (wHotKey) {
|
|
AddTag(pGroup->hGroup, (int)id, (WORD)ID_HOTKEY, (LPTSTR)&wHotKey, sizeof(wHotKey));
|
|
}
|
|
if (fMinimize) {
|
|
AddTag(pGroup->hGroup, (int)id, (WORD)ID_MINIMIZE, NULL, 0);
|
|
}
|
|
if (dwFlags & CI_SEPARATE_VDM) {
|
|
AddTag(pGroup->hGroup, (int)id, (WORD)ID_NEWVDM, NULL, 0);
|
|
}
|
|
if (*szExeDir) {
|
|
AddTag(pGroup->hGroup, (int)id, (WORD)ID_APPLICATIONDIR, szExeDir, 0);
|
|
}
|
|
|
|
pItem = LoadItem(hwndGroup, id, dwFlags & CI_ACTIVATE);
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
lpid = ITEM(lpgd, id);
|
|
|
|
if (!pItem) {
|
|
goto PuntCreation;
|
|
}
|
|
|
|
lpid->pt.x = pItem->rcIcon.left;
|
|
lpid->pt.y = pItem->rcIcon.top;
|
|
|
|
GlobalUnlock(pGroup->hGroup);
|
|
|
|
#if 0
|
|
// DOS apps are no longer set to fullscreen by default in progman
|
|
// 5-3-93 johannec (bug 8343)
|
|
#ifdef i386
|
|
|
|
// If this is a new DOS application, set the default to full screen.
|
|
// This is only done for x86, since mips doesn't have full screen.
|
|
|
|
lstrcpy(szCommand, lpCommand);
|
|
DoEnvironmentSubst(szCommand, (WORD)lstrlen(szCommand));
|
|
StripArgs(szCommand);
|
|
TagExtension(szCommand, sizeof(szCommand));
|
|
*szTemp = 0;
|
|
FindExecutable(szCommand, lpDefDir, szTemp);
|
|
|
|
if ((dwFlags & CI_SET_DOS_FULLSCRN) && *szTemp && IsDOSApplication(szTemp)) {
|
|
SetDOSApplicationToFullScreen(lpTitle);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
KeepGroupAround(hwndGroup, FALSE);
|
|
|
|
// We need to save the current group to disk now
|
|
// in case a setup program is doing DDE with us,
|
|
// and they reboot the system when finished.
|
|
|
|
if (!SaveGroup(hwndGroup, FALSE)) {
|
|
idError = 0;
|
|
DeleteItem(pGroup, pItem);
|
|
goto FreeIcon;
|
|
}
|
|
|
|
return pItem;
|
|
|
|
PuntCreation:
|
|
/*
|
|
* Note, must set lpid after each because it may move
|
|
*/
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pName, 0);
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pCommand, 0);
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pIconPath, 0);
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pIconRes, lpid->cbIconRes);
|
|
DeleteThing(lpgd, (LPDWORD)&lpgd->rgiItems[id], sizeof(ITEMDEF));
|
|
|
|
QuitThis:
|
|
cb = SizeofGroup(lpgd);
|
|
UnlockGroup(pGroup->hwnd);
|
|
|
|
CheckBeforeReAlloc(pGroup->hGroup);
|
|
GlobalReAlloc(pGroup->hGroup, cb, GMEM_MOVEABLE);
|
|
|
|
KeepGroupAround(hwndGroup, FALSE);
|
|
|
|
FreeIcon:
|
|
if (cbIconRes != 0xFFFF)
|
|
if (fWin32App) {
|
|
UnlockResource(hIconRes);
|
|
FreeResource(hIconRes);
|
|
FreeLibrary(hModule);
|
|
} else {
|
|
GlobalUnlock(hIconRes);
|
|
GlobalFree(hIconRes);
|
|
}
|
|
|
|
if (idError != 0)
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, idError, NULL,
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
// Force re-read of group.
|
|
//GlobalDiscard(pGroup->hGroup);
|
|
//pGroup->fLoaded = FALSE ;
|
|
//LockGroup(pGroup->hwnd);
|
|
//UnlockGroup(pGroup->hwnd);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/* DeleteItem() - */
|
|
|
|
|
|
|
|
VOID FAR PASCAL DeleteItem(PGROUP pGroup, PITEM pItem)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
LPITEMDEF lpid;
|
|
DWORD cb;
|
|
LPPMTAG lptag;
|
|
|
|
lpgd = LockGroup(pGroup->hwnd);
|
|
if (!lpgd)
|
|
return;
|
|
|
|
if (!GroupCheck(pGroup)) {
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_GROUPRO,
|
|
(LPTSTR)PTR(lpgd, lpgd->pName),
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
InvalidateIcon(pGroup, pItem);
|
|
return;
|
|
}
|
|
|
|
NukeIconBitmap(pGroup);
|
|
|
|
lpid = ITEM(lpgd, pItem->iItem);
|
|
|
|
if ((lpgd->cbGroup != (DWORD)MyDwordAlign((int)lpgd->cbGroup)) ||
|
|
(lpid->pName != (DWORD)MyDwordAlign((int)lpid->pName)) ||
|
|
(lpid->pCommand != (DWORD)MyDwordAlign((int)lpid->pCommand)) ||
|
|
(lpid->pIconPath != (DWORD)MyDwordAlign((int)lpid->pIconPath)) ||
|
|
(lpgd->rgiItems[pItem->iItem] != (DWORD)MyDwordAlign((int)lpgd->rgiItems[pItem->iItem]))) {
|
|
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_BADFILE,
|
|
(LPTSTR)PTR(lpgd, lpgd->pName),
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
return;
|
|
}
|
|
|
|
|
|
/* note, must set lpid after each because it may move
|
|
*/
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pName, 0);
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pCommand, 0);
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pIconPath, 0);
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pIconRes, lpid->cbIconRes);
|
|
DeleteThing(lpgd, (LPDWORD)&lpgd->rgiItems[pItem->iItem], sizeof(ITEMDEF));
|
|
|
|
while (lptag = FindTag(lpgd, pItem->iItem, 0)) {
|
|
/* delete all tags associated with this item
|
|
*/
|
|
UnlockGroup(pGroup->hwnd);
|
|
DeleteTag(pGroup->hGroup, lptag->wItem, lptag->wID);
|
|
lpgd = LockGroup(pGroup->hwnd);
|
|
}
|
|
|
|
/* Don't need Item anymore so delete it. */
|
|
RemoveItemFromList(pGroup, pItem);
|
|
|
|
cb = SizeofGroup(lpgd);
|
|
|
|
UnlockGroup(pGroup->hwnd);
|
|
|
|
CheckBeforeReAlloc(pGroup->hGroup);
|
|
GlobalReAlloc(pGroup->hGroup, cb, GMEM_MOVEABLE);
|
|
|
|
if (bAutoArrange && !bAutoArranging)
|
|
ArrangeItems(pGroup->hwnd);
|
|
else if (!bAutoArranging)
|
|
CalcGroupScrolls(pGroup->hwnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* CreateItemIcons() - */
|
|
|
|
/* Creates all the item windows... */
|
|
|
|
|
|
|
|
VOID PASCAL CreateItemIcons(HWND hwndGroup)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
int i;
|
|
|
|
lpgd = LockGroup(hwndGroup);
|
|
|
|
if (!lpgd) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Create the items in reverse Z-Order.
|
|
*/
|
|
for (i = lpgd->cItems - 1; i >= 0; i--) {
|
|
if (lpgd->rgiItems[i]) {
|
|
LoadItem(hwndGroup, (WORD)i, TRUE);
|
|
}
|
|
}
|
|
|
|
UnlockGroup(hwndGroup);
|
|
|
|
// REVIEW This may be not be needed because LoadGroupWindow does a
|
|
// SetInternalWindowPos which MIGHT already be generating the messages
|
|
// to do this.
|
|
if (bAutoArrange && !bAutoArranging)
|
|
ArrangeItems(hwndGroup);
|
|
else if (!bAutoArranging)
|
|
CalcGroupScrolls(hwndGroup);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* CheckIconResolution() - */
|
|
|
|
/* Makes sure we have the right icons loaded... reextracts and saves */
|
|
/* the file if not. */
|
|
|
|
|
|
|
|
VOID NEAR PASCAL CheckIconResolution(HWND hwndGroup)
|
|
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
LPITEMDEF lpid;
|
|
register PGROUP pGroup;
|
|
HANDLE hGroup;
|
|
BOOL fGottaDoIt;
|
|
register HDC hdc;
|
|
int i;
|
|
HICON hIcon;
|
|
WORD cbIconRes;
|
|
DWORD pIconRes;
|
|
LPTSTR lpIconRes;
|
|
WORD wFormat;
|
|
TCHAR szTemp[MAXITEMPATHLEN];
|
|
HANDLE hModule;
|
|
BOOL fWin32App;
|
|
//DWORD dwVer;
|
|
WORD wVer;
|
|
|
|
lpgd = LockGroup(hwndGroup);
|
|
if (!lpgd)
|
|
return;
|
|
|
|
hdc = GetDC(hwndGroup);
|
|
|
|
wFormat = (WORD)GetDeviceCaps(hdc, BITSPIXEL) |
|
|
(WORD)GetDeviceCaps(hdc, PLANES) * (WORD)256;
|
|
|
|
ReleaseDC(hwndGroup, hdc);
|
|
|
|
fGottaDoIt = lpgd->wIconFormat != wFormat ||
|
|
lpgd->cxIcon != (WORD)GetSystemMetrics(SM_CXICON) ||
|
|
lpgd->cyIcon != (WORD)GetSystemMetrics(SM_CYICON);
|
|
|
|
if (!fGottaDoIt) {
|
|
goto CleanUpAndLeave;
|
|
}
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
|
|
NukeIconBitmap(pGroup);
|
|
|
|
/* Save the new resolution parameters in the group file. */
|
|
lpgd->wIconFormat = wFormat;
|
|
lpgd->cxIcon = (WORD)GetSystemMetrics(SM_CXICON);
|
|
lpgd->cyIcon = (WORD)GetSystemMetrics(SM_CYICON);
|
|
|
|
hGroup = pGroup->hGroup;
|
|
|
|
for (i = 0; i < (int)lpgd->cItems; ++i) {
|
|
if (!lpgd->rgiItems[i])
|
|
continue;
|
|
|
|
lpid = ITEM(lpgd, i);
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pIconRes, lpid->cbIconRes);
|
|
lpid = ITEM(lpgd, i);
|
|
|
|
lstrcpy(szTemp, (LPTSTR)PTR(lpgd, lpid->pIconPath));
|
|
if (!*szTemp) {
|
|
/* Get default icon path */
|
|
lstrcpy(szTemp, (LPTSTR)PTR(lpgd, lpid->pCommand));
|
|
DoEnvironmentSubst(szTemp, (WORD)(MAXITEMPATHLEN + 1));
|
|
StripArgs(szTemp);
|
|
}
|
|
SheRemoveQuotes(szTemp);
|
|
cbIconRes = 0xFFFF;
|
|
lpIconRes = NULL;
|
|
hIcon = NULL;
|
|
|
|
if (hModule = LoadLibrary(szTemp)) {
|
|
// if WIN32 app
|
|
fWin32App = TRUE;
|
|
hIcon = (HICON)FindResource(hModule, (LPTSTR)MAKEINTRESOURCE(lpid->iIcon), (LPTSTR)MAKEINTRESOURCE(RT_ICON));
|
|
if (hIcon) {
|
|
//dwVer = 0x00030000;
|
|
wVer = 3;
|
|
cbIconRes = (WORD)SizeofResource(hModule, (HRSRC)hIcon);
|
|
hIcon = (HICON)LoadResource(hModule, (HRSRC)hIcon);
|
|
lpIconRes = LockResource(hIcon);
|
|
}
|
|
} else { // Win 3.1 app
|
|
fWin32App = FALSE;
|
|
if (wVer = ExtractIconResInfo(hAppInstance, szTemp, lpid->iIcon, &cbIconRes, (LPHANDLE)&hIcon)) {
|
|
lpIconRes = GlobalLock(hIcon);
|
|
}
|
|
}
|
|
|
|
|
|
UnlockGroup(hwndGroup);
|
|
|
|
pIconRes = AddThing(pGroup->hGroup, lpIconRes, cbIconRes);
|
|
lpgd = LockGroup(hwndGroup);
|
|
if (!lpgd)
|
|
continue;
|
|
|
|
/* In case the segment got moved... */
|
|
lpid = ITEM(lpgd, i);
|
|
|
|
if (hIcon)
|
|
if (fWin32App) {
|
|
UnlockResource(hIcon);
|
|
FreeResource(hIcon);
|
|
FreeLibrary(hModule);
|
|
} else {
|
|
GlobalUnlock(hIcon);
|
|
GlobalFree(hIcon);
|
|
}
|
|
|
|
lpid->pIconRes = pIconRes;
|
|
//lpid->dwIconVer = dwVer;
|
|
lpid->wIconVer = wVer;
|
|
lpid->cbIconRes = cbIconRes;
|
|
}
|
|
|
|
#ifdef ORGCODE
|
|
// Check everythings OK.
|
|
if (!pHdr || !pAND || !pXOR) {
|
|
// FU - delete icon stuff for this item..
|
|
// REVIEW UNDONE - warn user about memory problem ?
|
|
|
|
#ifdef DEBUG
|
|
KdPrint(("PM.CIR: Corrupted icon %s \n\r", (LPTSTR)PTR(lpid->pName)));
|
|
#endif
|
|
lpid = ITEM(lpgd, i);
|
|
DeleteThing(lpgd, (LPDWORD)&lpid->pIconRes, lpid->cbIconRes);
|
|
|
|
// Mark item as being effed - the header is checked by
|
|
// GetItemIcon.
|
|
lpid = ITEM(lpgd, i);
|
|
lpid->pIconRes = NULL;
|
|
lpid->wIconVer = 0; // This is the important one.
|
|
lpid->cbIconRes = 0;
|
|
|
|
// Warn user when we're through, not right in the middle.
|
|
fErrorOnExtract = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!GroupCheck(pGroup))
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_EEGROUPRO,
|
|
(LPTSTR)PTR(lpgd, lpgd->pName),
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
|
|
|
|
CleanUpAndLeave:
|
|
UnlockGroup(hwndGroup);
|
|
|
|
//REVIEW See above.
|
|
KeepGroupAround(hwndGroup, FALSE);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/* CreateGroupHandle() - */
|
|
|
|
/* Creates a discarded handle for use as a group handle... on the first */
|
|
/* LockGroup() the file will be loaded. */
|
|
|
|
|
|
|
|
HANDLE NEAR PASCAL CreateGroupHandle(void)
|
|
{
|
|
register HANDLE hGroup;
|
|
|
|
if (hGroup = GlobalAlloc(GMEM_MOVEABLE | GMEM_DISCARDABLE, 1L))
|
|
GlobalDiscard(hGroup);
|
|
|
|
return(hGroup);
|
|
}
|
|
|
|
|
|
|
|
/* StartupGroup() - */
|
|
|
|
|
|
|
|
VOID FAR PASCAL StartupGroup(HWND hwnd)
|
|
{
|
|
PITEM pItemCur, pItemExec;
|
|
LPGROUPDEF lpgd;
|
|
INT xLast, yLast; // Coord of icon to exec.
|
|
INT xBest, yBest; // Coord of next topmost-leftmost icon.
|
|
MSG msg; // Peek a message.
|
|
PGROUP pGroup; // The group.
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
|
|
|
|
/*
|
|
* Handle Startup group in icon position order - not Z-order.
|
|
*/
|
|
lpgd = LockGroup(hwnd);
|
|
if (!lpgd)
|
|
return;
|
|
|
|
/*
|
|
* Starts with the top left and works from left to right then
|
|
* top to bottom.
|
|
* This is really naff in terms of speed, but groups are usually
|
|
* small and the time cost is still small compared to that of execing.
|
|
*/
|
|
yLast = WORD_MIN;
|
|
xLast = WORD_MIN;
|
|
for (;;) {
|
|
/*
|
|
* Init
|
|
*/
|
|
xBest = WORD_MAX;
|
|
yBest = WORD_MAX;
|
|
pItemExec = NULL;
|
|
/*
|
|
* Find next icon to the right of this one.
|
|
*/
|
|
for (pItemCur = pGroup->pItems; pItemCur; pItemCur = pItemCur->pNext) {
|
|
/*
|
|
* Look for Icon to the right of this one.
|
|
* REVIEW This will ignore icons stacked on top of each other.
|
|
*/
|
|
if (pItemCur->rcIcon.top >= yLast
|
|
&& pItemCur->rcIcon.top <= yLast + (cyArrange / 2)
|
|
&& pItemCur->rcIcon.left < xBest
|
|
&& pItemCur->rcIcon.left > xLast) {
|
|
pItemExec = pItemCur;
|
|
xBest = pItemCur->rcIcon.left;
|
|
}
|
|
/*
|
|
* Check if it'll be suitable for the next row.
|
|
*/
|
|
else if (pItemCur->rcIcon.top > yLast + (cyArrange / 2)
|
|
&& pItemCur->rcIcon.top < yBest) {
|
|
yBest = pItemCur->rcIcon.top;
|
|
}
|
|
|
|
}
|
|
|
|
if (pItemExec) {
|
|
/*
|
|
* Found one on the current row.
|
|
*/
|
|
|
|
xLast = xBest;
|
|
/*
|
|
* Move this item to the top of the z-order so that any searches
|
|
* done during DDE will find the last execed item first.
|
|
* REVIEW This messes with the z-odrder of the startup group.
|
|
*/
|
|
BringItemToTop(pGroup, pItemExec, TRUE);
|
|
/* Start it up. */
|
|
ExecItem(pGroup, pItemExec, FALSE, TRUE);
|
|
/*
|
|
* Handle any DDE before doing anything else to stop
|
|
* the message queue from over-flowing.
|
|
*/
|
|
while (PeekMessage(&msg, hwndProgman, 0, 0, PM_REMOVE | PM_NOYIELD)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
|
|
} else if (yBest != WORD_MAX) {
|
|
/*
|
|
* Nothing left on the current row but there is another row.
|
|
*/
|
|
yLast = yBest;
|
|
xLast = WORD_MIN;
|
|
|
|
} else {
|
|
/*
|
|
* Nothing left.
|
|
*/
|
|
goto Quit;
|
|
}
|
|
}
|
|
|
|
Quit:
|
|
UnlockGroup(pGroup->hwnd);
|
|
}
|
|
|
|
|
|
/*
|
|
* Check for null item pointers and item pointers that go out of the group.
|
|
* REVIEW UNDONE this doesn't check that the pointers point to things
|
|
* after the end of the items array.
|
|
*/
|
|
BOOL NEAR PASCAL ValidItems(LPGROUPDEF lpgd)
|
|
{
|
|
INT i;
|
|
LPITEMDEF lpid;
|
|
DWORD cbGroup;
|
|
|
|
if (!lpgd)
|
|
return FALSE;
|
|
|
|
|
|
cbGroup = lpgd->cbGroup;
|
|
|
|
for (i = 0; (WORD)i < lpgd->cItems; i++) {
|
|
if (!lpgd->rgiItems[i])
|
|
continue;
|
|
|
|
lpid = ITEM(lpgd, i);
|
|
if (!lpid)
|
|
return FALSE;
|
|
|
|
if (lpid->pName > cbGroup)
|
|
return FALSE;
|
|
if (lpid->pCommand > cbGroup)
|
|
return FALSE;
|
|
if (lpid->pIconPath > cbGroup)
|
|
return FALSE;
|
|
if ((lpid->cbIconRes != (WORD)-1) && (lpid->pIconRes > cbGroup))
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* IsGroupAlreadyLoaded() - */
|
|
|
|
/* Determines if the user is trying to load a currently loaded group. */
|
|
|
|
|
|
|
|
HWND NEAR PASCAL IsGroupAlreadyLoaded(LPTSTR lpGroupKey, BOOL bCommonGroup)
|
|
{
|
|
HWND hwndT;
|
|
PGROUP pGroup;
|
|
|
|
for (hwndT = GetWindow(hwndMDIClient, GW_CHILD); hwndT; hwndT = GetWindow(hwndT, GW_HWNDNEXT)) {
|
|
if (GetWindow(hwndT, GW_OWNER))
|
|
continue;
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndT, GWLP_PGROUP);
|
|
if (!lstrcmpi(lpGroupKey, pGroup->lpKey)) {
|
|
|
|
if (bCommonGroup) {
|
|
|
|
if (pGroup->fCommon)
|
|
return(hwndT);
|
|
|
|
} else {
|
|
|
|
if (!pGroup->fCommon)
|
|
return(hwndT);
|
|
|
|
}
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
BOOL NEAR PASCAL IndexUsed(WORD wIndex, BOOL bCommonGroup)
|
|
{
|
|
PGROUP pGroup;
|
|
|
|
for (pGroup = pFirstGroup; pGroup; pGroup = pGroup->pNext)
|
|
if ((pGroup->wIndex == wIndex) && (pGroup->fCommon == bCommonGroup))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/* LoadGroupWindow() - */
|
|
|
|
/* Creates a group window by sending an MDI create message to the MDI client. */
|
|
/* The MDICREATESTRUCT contains a parameter pointer to the name of the group */
|
|
/* key. */
|
|
|
|
|
|
// GroupFile must be ANSI.
|
|
|
|
HWND PASCAL LoadGroupWindow(LPTSTR lpKey, WORD wIndex, BOOL bCommonGroup)
|
|
{
|
|
MDICREATESTRUCT mdics;
|
|
PGROUP pGroup;
|
|
TCHAR szGroupClass[64];
|
|
LPGROUPDEF lpgd;
|
|
HWND hwnd;
|
|
TCHAR szCommonGroupSuffix[MAXKEYLEN];
|
|
TCHAR szCommonGroupTitle[2 * MAXKEYLEN];
|
|
WINDOWPLACEMENT wp;
|
|
|
|
|
|
// Check if the group is already loaded. This will prevent duplicate groups.
|
|
|
|
if (hwnd = IsGroupAlreadyLoaded(lpKey, bCommonGroup)) {
|
|
return(hwnd);
|
|
}
|
|
|
|
if (!wIndex) {
|
|
while (IndexUsed(++wIndex, bCommonGroup))
|
|
;
|
|
}
|
|
|
|
if (!LoadString(hAppInstance, IDS_GROUPCLASS, szGroupClass,
|
|
CharSizeOf(szGroupClass))) {
|
|
return NULL;
|
|
}
|
|
|
|
pGroup = (PGROUP)LocalAlloc(LPTR, sizeof(GROUP));
|
|
if (!pGroup) {
|
|
return NULL;
|
|
}
|
|
|
|
pGroup->hGroup = CreateGroupHandle();
|
|
pGroup->pItems = NULL;
|
|
pGroup->hbm = NULL;
|
|
pGroup->wIndex = wIndex;
|
|
pGroup->fCommon = bCommonGroup;
|
|
pGroup->ftLastWriteTime.dwLowDateTime = 0;
|
|
pGroup->ftLastWriteTime.dwHighDateTime = 0;
|
|
|
|
pGroup->lpKey = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * (lstrlen(lpKey) + 1));
|
|
pGroup->fLoaded = FALSE;
|
|
|
|
if (!pGroup->lpKey) {
|
|
GoAway:
|
|
GlobalFree(pGroup->hGroup);
|
|
LocalFree((HANDLE)pGroup);
|
|
if (!fLowMemErrYet) {
|
|
MyMessageBox(hwndProgman, IDS_APPTITLE, IDS_LOWMEMONINIT,
|
|
lpKey, MB_OK | MB_ICONEXCLAMATION);
|
|
fLowMemErrYet = TRUE;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
lstrcpy(pGroup->lpKey, lpKey);
|
|
|
|
mdics.szTitle = TEXT("");
|
|
mdics.hOwner = hAppInstance;
|
|
mdics.szClass = szGroupClass;
|
|
mdics.style = WS_VSCROLL | WS_HSCROLL;
|
|
mdics.x = mdics.y = mdics.cx = mdics.cy = CW_USEDEFAULT;
|
|
mdics.lParam = (LPARAM)pGroup;
|
|
|
|
/*
|
|
* REVIEW HACK - Set the auto arranging flag to stop the group being
|
|
* loaded by ArrangingIcons doing a LockGroup and then producing
|
|
* an error if something goes wrong. We're going to do a lock
|
|
* later on anyway and we don't want two error messages.
|
|
*/
|
|
bAutoArranging = TRUE;
|
|
pGroup->hwnd = (HWND)SendMessage(hwndMDIClient, WM_MDICREATE, 0, (LPARAM)(LPTSTR)&mdics);
|
|
bAutoArranging = FALSE;
|
|
|
|
if (!pGroup->hwnd) {
|
|
LocalFree((HANDLE)pGroup->lpKey);
|
|
goto GoAway;
|
|
}
|
|
|
|
/*
|
|
* Note that we're about to load a group for the first time.
|
|
* NB Stting this tells LockGroup that the caller can handle the errors.
|
|
*/
|
|
fFirstLoad = TRUE;
|
|
lpgd = LockGroup(pGroup->hwnd);
|
|
/*
|
|
* The group has been loaded or at least we tried.
|
|
*/
|
|
fFirstLoad = FALSE;
|
|
if (!lpgd) {
|
|
LoadFail:
|
|
/* Loading the group failed somehow... */
|
|
SendMessage(hwndMDIClient, WM_MDIDESTROY, (WPARAM)pGroup->hwnd, 0L);
|
|
|
|
// stop handling of Program Groups key changes.
|
|
|
|
bHandleProgramGroupsEvent = FALSE;
|
|
RegDeleteKey(hkeyProgramGroups, pGroup->lpKey);
|
|
|
|
|
|
// reset handling of Program Groups key changes.
|
|
|
|
ResetProgramGroupsEvent(bCommonGroup);
|
|
bHandleProgramGroupsEvent = TRUE;
|
|
|
|
LocalFree((HANDLE)pGroup->lpKey);
|
|
GlobalFree(pGroup->hGroup);
|
|
LocalFree((HANDLE)pGroup);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* test if it is a Windows 3.1 group file format. If so it is not
|
|
* valid in WIN32. In Windows 3.1 RECT and POINT are WORD instead of LONG.
|
|
*/
|
|
|
|
if ((lpgd->rcNormal.left != (INT)(SHORT)lpgd->rcNormal.left) ||
|
|
(lpgd->rcNormal.right != (INT)(SHORT)lpgd->rcNormal.right) ||
|
|
(lpgd->rcNormal.top != (INT)(SHORT)lpgd->rcNormal.top) ||
|
|
(lpgd->rcNormal.bottom != (INT)(SHORT)lpgd->rcNormal.bottom)) {
|
|
/* The group is invalid. */
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_BADFILE,
|
|
(LPTSTR)PTR(lpgd, lpgd->pName),
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
UnlockGroup(pGroup->hwnd);
|
|
goto LoadFail;
|
|
}
|
|
|
|
if (!ValidItems(lpgd)) {
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_BADFILE,
|
|
(LPTSTR)PTR(lpgd, lpgd->pName),
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
UnlockGroup(pGroup->hwnd);
|
|
goto LoadFail;
|
|
}
|
|
|
|
|
|
if (lpgd->nCmdShow) {
|
|
SetInternalWindowPos(pGroup->hwnd, (UINT)lpgd->nCmdShow, &lpgd->rcNormal,
|
|
&lpgd->ptMin);
|
|
}
|
|
|
|
if (pGroup->fCommon) {
|
|
|
|
|
|
// Add the common group suffix to the name of the group e.g. (Common)
|
|
// Only do this if the group window is not minimized.
|
|
|
|
|
|
wp.length = sizeof(WINDOWPLACEMENT);
|
|
GetWindowPlacement(pGroup->hwnd, &wp);
|
|
if ((wp.showCmd == SW_MINIMIZE) ||
|
|
(wp.showCmd == SW_SHOWMINIMIZED) ||
|
|
(wp.showCmd == SW_SHOWMINNOACTIVE)) {
|
|
SetWindowText(pGroup->hwnd, (LPTSTR)PTR(lpgd, lpgd->pName));
|
|
} else {
|
|
lstrcpy(szCommonGroupTitle, (LPTSTR)PTR(lpgd, lpgd->pName));
|
|
if (LoadString(hAppInstance, IDS_COMMONGRPSUFFIX, szCommonGroupSuffix,
|
|
CharSizeOf(szCommonGroupSuffix))) {
|
|
lstrcat(szCommonGroupTitle, szCommonGroupSuffix);
|
|
}
|
|
SetWindowText(pGroup->hwnd, szCommonGroupTitle);
|
|
if (!UserIsAdmin) {
|
|
pGroup->fRO = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
SetWindowText(pGroup->hwnd, (LPTSTR)PTR(lpgd, lpgd->pName));
|
|
}
|
|
|
|
|
|
UnlockGroup(pGroup->hwnd);
|
|
|
|
//CheckIconResolution(pGroup->hwnd);
|
|
|
|
CreateItemIcons(pGroup->hwnd);
|
|
|
|
/*
|
|
* Link the group.
|
|
*/
|
|
pGroup->pNext = NULL;
|
|
*pLastGroup = pGroup;
|
|
pLastGroup = &pGroup->pNext;
|
|
pCurrentGroup = pGroup;
|
|
|
|
#ifdef NOTINUSER
|
|
CalcChildScroll(pGroup->hwnd, SB_BOTH);
|
|
#endif
|
|
|
|
GroupCheck(pGroup);
|
|
|
|
return(pGroup->hwnd);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* UnloadGroupWindow() - */
|
|
|
|
|
|
|
|
void FAR PASCAL UnloadGroupWindow(HWND hwnd)
|
|
{
|
|
PGROUP pGroup, * ppGroup;
|
|
PITEM pItem, pItemNext;
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
|
|
|
|
/* Destroy the window. */
|
|
SendMessage(hwndMDIClient, WM_MDIDESTROY, (WPARAM)hwnd, 0L);
|
|
|
|
/* Free the group segment. */
|
|
GlobalFree(pGroup->hGroup);
|
|
|
|
/* Free the local stuff. */
|
|
LocalFree((HANDLE)pGroup->lpKey);
|
|
|
|
/* The cached bitmap if there is one. */
|
|
if (pGroup->hbm) {
|
|
DeleteObject(pGroup->hbm);
|
|
pGroup->hbm = NULL;
|
|
}
|
|
|
|
/* The item data. */
|
|
for (pItem = pGroup->pItems; pItem; pItem = pItemNext) {
|
|
pItemNext = pItem->pNext;
|
|
LocalFree((HANDLE)pItem);
|
|
}
|
|
|
|
/* Remove the group from the linked list. */
|
|
for (ppGroup = &pFirstGroup; *ppGroup; ppGroup = &((*ppGroup)->pNext)) {
|
|
if (*ppGroup == pGroup) {
|
|
*ppGroup = pGroup->pNext;
|
|
break;
|
|
}
|
|
}
|
|
if (pLastGroup == &pGroup->pNext)
|
|
pLastGroup = ppGroup;
|
|
|
|
/* Lastly, free the group structure itself. */
|
|
LocalFree((HANDLE)pGroup);
|
|
}
|
|
|
|
|
|
|
|
/* RemoveBackslashFromKeyName() - */
|
|
|
|
/* replace the invalid characters for a key name by some valid charater. */
|
|
/* the same characters that are invalid for a file name are invalid for a */
|
|
/* key name. */
|
|
|
|
|
|
|
|
void RemoveBackslashFromKeyName(LPTSTR lpKeyName)
|
|
{
|
|
LPTSTR lpt;
|
|
|
|
for (lpt = lpKeyName; *lpt; lpt++) {
|
|
if ((*lpt == TEXT('\\')) || (*lpt == TEXT(':')) || (*lpt == TEXT('>')) || (*lpt == TEXT('<')) ||
|
|
(*lpt == TEXT('*')) || (*lpt == TEXT('?'))) {
|
|
*lpt = TEXT('.');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* CreateNewGroup() - */
|
|
|
|
/* This function creates a new, empty group. */
|
|
|
|
|
|
|
|
HWND PASCAL CreateNewGroup(LPTSTR pGroupName, BOOL bCommonGroup)
|
|
{
|
|
HANDLE hT;
|
|
LPGROUPDEF lpgd;
|
|
PGROUP pGroup;
|
|
HDC hdc;
|
|
int i;
|
|
int cb;
|
|
TCHAR szKeyName[MAXKEYLEN + 1];
|
|
HWND hwnd;
|
|
DWORD status = 0;
|
|
WORD cGroups;
|
|
INT wGroupNameLen; //length of pGroupName DWORD aligned.
|
|
HKEY hkeyGroups;
|
|
HKEY hKey;
|
|
HWND hwndT;
|
|
PSECURITY_ATTRIBUTES pSecAttr;
|
|
|
|
/*
|
|
* Check we're not trying to create too many groups.
|
|
* Count the current number of groups.
|
|
*/
|
|
cGroups = 0;
|
|
|
|
for (hwnd = GetWindow(hwndMDIClient, GW_CHILD); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT)) {
|
|
if (GetWindow(hwnd, GW_OWNER))
|
|
continue;
|
|
|
|
// count the common groups seperately from the personal group.
|
|
// Both have a maximum of CGROUPSMAX groups.
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
|
|
if (bCommonGroup && pGroup->fCommon ||
|
|
!bCommonGroup && !pGroup->fCommon) {
|
|
cGroups++;
|
|
}
|
|
}
|
|
|
|
// Compare with limit.
|
|
if (cGroups >= CGROUPSMAX) {
|
|
status = bCommonGroup ? IDS_TOOMANYCOMMONGROUPS : IDS_TOOMANYGROUPS;
|
|
goto Exit;
|
|
}
|
|
|
|
if (bCommonGroup) {
|
|
|
|
hkeyGroups = hkeyCommonGroups;
|
|
pSecAttr = pAdminSecAttr;
|
|
if (!hkeyGroups) {
|
|
if (MyMessageBox(hwndProgman,
|
|
IDS_COMMONGROUPERR,
|
|
IDS_NOCOMMONGRPS,
|
|
pGroupName,
|
|
MB_OKCANCEL | MB_ICONEXCLAMATION | MB_TASKMODAL)
|
|
== IDOK) {
|
|
|
|
hkeyGroups = hkeyProgramGroups;
|
|
pSecAttr = pSecurityAttributes;
|
|
bCommonGroup = FALSE;
|
|
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
hkeyGroups = hkeyProgramGroups;
|
|
pSecAttr = pSecurityAttributes;
|
|
|
|
}
|
|
|
|
if (!hkeyGroups) {
|
|
status = IDS_NOGRPFILE;
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// Replace backslash in the group name because the registry does not
|
|
// allow key names with backslash, bckslash is used to separate keys.
|
|
|
|
lstrcpy(szKeyName, pGroupName);
|
|
RemoveBackslashFromKeyName(szKeyName);
|
|
|
|
|
|
// Test for existing key.
|
|
|
|
while (!RegOpenKeyEx(hkeyGroups, szKeyName, 0, KEY_READ, &hKey)) {
|
|
/* a group with this name already exists */
|
|
if (hwndT = IsGroupAlreadyLoaded(szKeyName, bCommonGroup)) {
|
|
if (lstrlen(szKeyName) < MAXKEYLEN) {
|
|
lstrcat(szKeyName, TEXT("."));
|
|
GlobalUnlock(pGroup->hGroup);
|
|
RegCloseKey(hKey);
|
|
continue;
|
|
}
|
|
}
|
|
RegCloseKey(hKey);
|
|
goto LoadGroupFile;
|
|
}
|
|
|
|
wGroupNameLen = MyDwordAlign(sizeof(TCHAR) * (lstrlen(pGroupName) + 1));
|
|
cb = sizeof(GROUPDEF) + (NSLOTS * sizeof(DWORD)) + wGroupNameLen;
|
|
|
|
/*
|
|
* In CreateNewGroup before GlobalAlloc.
|
|
*/
|
|
hT = GlobalAlloc(GHND, (DWORD)cb);
|
|
if (!hT) {
|
|
status = IDS_LOWMEM;
|
|
goto Exit;
|
|
}
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(hT);
|
|
|
|
lpgd->dwMagic = GROUP_UNICODE;
|
|
lpgd->cbGroup = (DWORD)cb;
|
|
lpgd->nCmdShow = 0; /* use MDI defaults */
|
|
lpgd->pName = sizeof(GROUPDEF) + NSLOTS * sizeof(DWORD);
|
|
hdc = GetDC(NULL);
|
|
lpgd->wIconFormat = (WORD)GetDeviceCaps(hdc, BITSPIXEL) + (WORD)256 *
|
|
(WORD)GetDeviceCaps(hdc, PLANES);
|
|
ReleaseDC(NULL, hdc);
|
|
lpgd->cxIcon = (WORD)GetSystemMetrics(SM_CXICON);
|
|
lpgd->cyIcon = (WORD)GetSystemMetrics(SM_CYICON);
|
|
lpgd->Reserved1 = (WORD)-1;
|
|
lpgd->Reserved2 = (DWORD)-1;
|
|
|
|
lpgd->cItems = NSLOTS;
|
|
|
|
for (i = 0; i < NSLOTS; i++) {
|
|
lpgd->rgiItems[i] = 0;
|
|
}
|
|
|
|
lstrcpy((LPTSTR)((LPSTR)lpgd + sizeof(GROUPDEF) + NSLOTS * sizeof(DWORD)),
|
|
pGroupName);
|
|
|
|
/*
|
|
* In CreateNewGroup before SizeofGroup.
|
|
*/
|
|
cb = (int)SizeofGroup(lpgd);
|
|
|
|
|
|
|
|
// stop handling of Program Groups key changes when creating groups.
|
|
bHandleProgramGroupsEvent = FALSE;
|
|
|
|
// BUGBUG pSecurityAttributes might change for Common groups.
|
|
if (!RegCreateKeyEx(hkeyGroups, szKeyName, 0, 0, 0, DELETE | KEY_READ | KEY_WRITE, pSecAttr, &hKey, NULL)) {
|
|
if (RegSetValueEx(hKey, NULL, 0, REG_BINARY, (LPBYTE)lpgd, cb))
|
|
status = IDS_CANTWRITEGRP;
|
|
RegCloseKey(hKey);
|
|
} else
|
|
status = IDS_NOGRPFILE;
|
|
|
|
GlobalUnlock(hT);
|
|
GlobalFree(hT);
|
|
|
|
Exit:
|
|
if (status) {
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, (WORD)status, pGroupName,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
|
|
ResetProgramGroupsEvent(bCommonGroup);
|
|
bHandleProgramGroupsEvent = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
LoadGroupFile:
|
|
/*
|
|
* The group file now exists on the disk... load it in!
|
|
*/
|
|
fLowMemErrYet = FALSE;
|
|
fErrorOnExtract = FALSE;
|
|
hwnd = LoadGroupWindow(szKeyName, 0, bCommonGroup);
|
|
|
|
if (fErrorOnExtract) {
|
|
// On observed problem with icon extraction has been to do
|
|
// with a low memory.
|
|
MyMessageBox(hwndProgman, IDS_OOMEXITTITLE, IDS_LOWMEMONEXTRACT,
|
|
NULL, MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
|
|
}
|
|
|
|
// Save the group section even if SaveSettings is off to stop
|
|
// stupid users from hosing themselves.
|
|
if (!bCommonGroup) {
|
|
WriteGroupsSection();
|
|
}
|
|
ResetProgramGroupsEvent(bCommonGroup);
|
|
bHandleProgramGroupsEvent = TRUE;
|
|
return hwnd;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* DeleteGroup() - */
|
|
|
|
|
|
|
|
VOID FAR PASCAL DeleteGroup(HWND hwndGroup)
|
|
{
|
|
PGROUP pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
PGROUP* ppGroup;
|
|
PITEM pItem;
|
|
TCHAR szT[10];
|
|
BOOL bCommonGroup;
|
|
HKEY hkeyGroups;
|
|
|
|
|
|
// stop handling of Program Groups key changes when deleting groups.
|
|
|
|
bHandleProgramGroupsEvent = FALSE;
|
|
|
|
bCommonGroup = pGroup->fCommon;
|
|
if (bCommonGroup)
|
|
hkeyGroups = hkeyCommonGroups;
|
|
else
|
|
hkeyGroups = hkeyProgramGroups;
|
|
|
|
if (pGroup->fRO || RegDeleteKey(hkeyGroups, pGroup->lpKey) != ERROR_SUCCESS) {
|
|
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_ERRORDELETEGROUP,
|
|
pGroup->lpKey, MB_OK);
|
|
|
|
// reset handling of Program Groups key changes.
|
|
|
|
ResetProgramGroupsEvent(bCommonGroup);
|
|
bHandleProgramGroupsEvent = TRUE;
|
|
return; // cannot delete the group
|
|
}
|
|
|
|
|
|
// reset handling of Program Groups key changes.
|
|
|
|
ResetProgramGroupsEvent(bCommonGroup);
|
|
bHandleProgramGroupsEvent = TRUE;
|
|
|
|
/* Destroy the window, the global memory block, and the file. */
|
|
SendMessage(hwndMDIClient, WM_MDIDESTROY, (WPARAM)hwndGroup, 0L);
|
|
NukeIconBitmap(pGroup);
|
|
GlobalFree(pGroup->hGroup);
|
|
|
|
if (!bCommonGroup) {
|
|
|
|
|
|
// Remove the program manager's settings for that personal group.
|
|
|
|
|
|
wsprintf(szT, TEXT("Group%d"), pGroup->wIndex);
|
|
RegDeleteValue(hkeyPMGroups, szT);
|
|
}
|
|
|
|
/* Unlink the group structure. */
|
|
for (ppGroup = &pFirstGroup; *ppGroup && *ppGroup != pGroup; ppGroup = &(*ppGroup)->pNext)
|
|
;
|
|
|
|
if (*ppGroup)
|
|
*ppGroup = pGroup->pNext;
|
|
|
|
if (pLastGroup == &pGroup->pNext)
|
|
pLastGroup = ppGroup;
|
|
|
|
/* Destroying the window should activate another one, but if it is the
|
|
* last one, nothing will get activated, so to make sure punt the
|
|
* current group pointer...
|
|
*/
|
|
if (pCurrentGroup == pGroup)
|
|
pCurrentGroup = NULL;
|
|
|
|
/* Lastly, toss out the group and item structures. */
|
|
while (pGroup->pItems) {
|
|
pItem = pGroup->pItems;
|
|
pGroup->pItems = pItem->pNext;
|
|
LocalFree((HANDLE)pItem);
|
|
}
|
|
LocalFree((HANDLE)pGroup->lpKey);
|
|
LocalFree((HANDLE)pGroup);
|
|
|
|
if (!bCommonGroup) {
|
|
|
|
|
|
// Change the program manager's settings for that personal group.
|
|
|
|
|
|
WriteGroupsSection();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ChangeGroupTitle() - */
|
|
|
|
/* Modifies the name of a program group, on the screen and in the file. */
|
|
|
|
|
|
|
|
VOID FAR PASCAL ChangeGroupTitle(HWND hwndGroup, LPTSTR lpName, BOOL bCommonGroup)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
PGROUP pGroup;
|
|
DWORD pName;
|
|
TCHAR szCommonGroupSuffix[MAXKEYLEN];
|
|
TCHAR szCommonGroupTitle[2 * MAXKEYLEN];
|
|
WINDOWPLACEMENT wp;
|
|
|
|
if (!hwndGroup)
|
|
return;
|
|
|
|
|
|
// Change the title of the window.
|
|
|
|
|
|
if (bCommonGroup) {
|
|
|
|
|
|
// Add the common group suffix to the name of the group e.g. (Common),
|
|
// do not append the common suffix if the group window is minimized.
|
|
|
|
wp.length = sizeof(WINDOWPLACEMENT);
|
|
GetWindowPlacement(hwndGroup, &wp);
|
|
if (wp.showCmd == SW_MINIMIZE || wp.showCmd == SW_SHOWMINIMIZED ||
|
|
wp.showCmd == SW_SHOWMINNOACTIVE) {
|
|
SetWindowText(hwndGroup, lpName);
|
|
} else {
|
|
|
|
lstrcpy(szCommonGroupTitle, lpName);
|
|
if (LoadString(hAppInstance, IDS_COMMONGRPSUFFIX, szCommonGroupSuffix,
|
|
CharSizeOf(szCommonGroupSuffix))) {
|
|
lstrcat(szCommonGroupTitle, szCommonGroupSuffix);
|
|
}
|
|
SetWindowText(hwndGroup, szCommonGroupTitle);
|
|
}
|
|
} else {
|
|
SetWindowText(hwndGroup, lpName);
|
|
}
|
|
|
|
|
|
// Remove the old name.
|
|
|
|
|
|
lpgd = LockGroup(hwndGroup);
|
|
if (!lpgd)
|
|
return;
|
|
|
|
DeleteThing(lpgd, (LPDWORD)&lpgd->pName, 0);
|
|
UnlockGroup(hwndGroup);
|
|
|
|
|
|
// Insert the new one.
|
|
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
pName = AddThing(pGroup->hGroup, lpName, (WORD)0);
|
|
|
|
|
|
// Set the new offset...
|
|
|
|
|
|
if (lpgd = LockGroup(hwndGroup)) {
|
|
lpgd->pName = pName;
|
|
UnlockGroup(hwndGroup);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* SetGroupDimensions() - */
|
|
|
|
/* Saves the size and position of the group file in the group segment, as */
|
|
/* as well as the positions of all the item icons */
|
|
|
|
|
|
|
|
VOID NEAR PASCAL SetGroupDimensions(HWND hwndGroup)
|
|
{
|
|
LPGROUPDEF lpgd;
|
|
LPITEMDEF lpid;
|
|
PGROUP pGroup;
|
|
PITEM pItem;
|
|
WORD i;
|
|
|
|
lpgd = LockGroup(hwndGroup);
|
|
if (!lpgd)
|
|
return;
|
|
|
|
lpgd->nCmdShow = (WORD)GetInternalWindowPos(hwndGroup, &lpgd->rcNormal,
|
|
&lpgd->ptMin);
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
NukeIconBitmap(pGroup); // invalidate the bitmap
|
|
|
|
for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) {
|
|
lpid = ITEM(lpgd, pItem->iItem);
|
|
lpid->pt.x = pItem->rcIcon.left;
|
|
lpid->pt.y = pItem->rcIcon.top;
|
|
|
|
/* save offset of ITEMDEF for each item
|
|
*/
|
|
ChangeTagID(lpgd, pItem->iItem, (int)lpgd->rgiItems[pItem->iItem]);
|
|
pItem->iItem = (int)lpgd->rgiItems[pItem->iItem];
|
|
}
|
|
|
|
for (i = 0, pItem = pGroup->pItems; pItem; pItem = pItem->pNext, i++) {
|
|
/* write offsets back out in Z order and update the index
|
|
*/
|
|
ChangeTagID(lpgd, pItem->iItem, (int)i);
|
|
lpgd->rgiItems[i] = (DWORD)pItem->iItem;
|
|
pItem->iItem = (int)i;
|
|
}
|
|
|
|
/* Clear out remaining pointers to prevent duped item wierdness. */
|
|
while (i < lpgd->cItems)
|
|
lpgd->rgiItems[i++] = 0;
|
|
|
|
UnlockGroup(hwndGroup);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* WriteGroupsSection() - */
|
|
|
|
|
|
|
|
void PASCAL WriteGroupsSection(VOID)
|
|
{
|
|
PGROUP pGroup;
|
|
HCURSOR hCursor;
|
|
HWND hwndGroup;
|
|
LPGROUPDEF lpgd;
|
|
TCHAR szT[66];
|
|
TCHAR szOrd[CGROUPSMAX * 8 + 7];
|
|
TCHAR szFmt[] = TEXT("Group%d");
|
|
TCHAR szFmtCommonGrp[] = TEXT("GroupC%d");
|
|
INT cGroups;
|
|
TCHAR szGroupKey[MAXKEYLEN];
|
|
INT i;
|
|
RECT rc;
|
|
POINT ptMin;
|
|
TCHAR szCGrpInfo[MAXKEYLEN];
|
|
INT cbValueName;
|
|
|
|
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
ShowCursor(TRUE);
|
|
|
|
if (!(hwndGroup = GetWindow(hwndMDIClient, GW_CHILD))) {
|
|
goto WPIExit;
|
|
}
|
|
|
|
hwndGroup = GetWindow(hwndGroup, GW_HWNDLAST);
|
|
|
|
szOrd[0] = 0;
|
|
cGroups = 0;
|
|
|
|
|
|
// Clear user's previous positioning of common groups.
|
|
|
|
if (hkeyPMCommonGroups) {
|
|
cbValueName = CharSizeOf(szGroupKey);
|
|
while (!RegEnumValue(hkeyPMCommonGroups, 0, szGroupKey, &cbValueName, 0, 0,
|
|
0, 0)) {
|
|
RegDeleteValue(hkeyPMCommonGroups, szGroupKey);
|
|
cbValueName = CharSizeOf(szGroupKey);
|
|
}
|
|
}
|
|
|
|
for (; hwndGroup; hwndGroup = GetWindow(hwndGroup, GW_HWNDPREV)) {
|
|
/*
|
|
* Check to make sure we're not out of room for the order string.
|
|
*/
|
|
if (cGroups > CGROUPSMAX) {
|
|
MessageBeep(0);
|
|
break;
|
|
}
|
|
|
|
if (GetWindow(hwndGroup, GW_OWNER)) {
|
|
continue;
|
|
}
|
|
|
|
pGroup = (PGROUP)GetWindowLongPtr(hwndGroup, GWLP_PGROUP);
|
|
|
|
if (!pGroup->lpKey || !*pGroup->lpKey) {
|
|
if (pGroup->lpKey) {
|
|
LocalFree((HANDLE)pGroup->lpKey);
|
|
}
|
|
|
|
lpgd = LockGroup(hwndGroup);
|
|
if (!lpgd) {
|
|
continue;
|
|
}
|
|
|
|
lstrcpy(szGroupKey, (LPTSTR)PTR(lpgd, lpgd->pName));
|
|
pGroup->lpKey = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * (lstrlen(szGroupKey) + 1));
|
|
lstrcpy(pGroup->lpKey, szGroupKey);
|
|
UnlockGroup(hwndGroup);
|
|
}
|
|
|
|
if (pGroup->fCommon) {
|
|
wsprintf(szT, szFmtCommonGrp, pGroup->wIndex);
|
|
lstrcat(szOrd, TEXT(" C"));
|
|
lstrcat(szOrd, szT + CCHCOMMONGROUP);
|
|
if (hkeyPMCommonGroups) {
|
|
i = GetInternalWindowPos(hwndGroup, &rc, &ptMin);
|
|
|
|
if (i == SW_SHOWMINNOACTIVE)
|
|
i = SW_SHOWNORMAL;
|
|
|
|
wsprintf(szCGrpInfo, TEXT("%d %d %d %d %d %d %d "),
|
|
rc.left, rc.top, rc.right, rc.bottom,
|
|
ptMin.x, ptMin.y, i);
|
|
lstrcat(szCGrpInfo, pGroup->lpKey);
|
|
RegSetValueEx(hkeyPMCommonGroups, szT, 0, REG_SZ, (LPBYTE)szCGrpInfo, sizeof(TCHAR) * (lstrlen(szCGrpInfo) + 1));
|
|
}
|
|
} else {
|
|
wsprintf(szT, szFmt, pGroup->wIndex);
|
|
lstrcat(szOrd, TEXT(" "));
|
|
lstrcat(szOrd, szT + CCHGROUP);
|
|
if (hkeyPMGroups) {
|
|
RegSetValueEx(hkeyPMGroups, szT, 0, REG_SZ, (LPBYTE)pGroup->lpKey, sizeof(TCHAR) * (lstrlen(pGroup->lpKey) + 1));
|
|
}
|
|
}
|
|
|
|
cGroups++;
|
|
}
|
|
|
|
if (hkeyPMSettings) {
|
|
RegSetValueEx(hkeyPMSettings, szOrder, 0, REG_SZ, (LPBYTE)szOrd, sizeof(TCHAR) * (lstrlen(szOrd) + 1));
|
|
}
|
|
|
|
WPIExit:
|
|
ShowCursor(FALSE);
|
|
SetCursor(hCursor);
|
|
}
|
|
|
|
|
|
|
|
/* SaveGroupsContent() -
|
|
/*
|
|
/* Save the contents of the all the groups, doesn't save changes in
|
|
/* size or position if bSaveGroupSettings is FALSE, only the group items.
|
|
|
|
|
|
|
|
BOOL SaveGroupsContent(BOOL bSaveGroupSettings)
|
|
{
|
|
HWND hwndGroup;
|
|
|
|
hwndGroup = GetWindow(hwndMDIClient, GW_CHILD);
|
|
if (!hwndGroup)
|
|
return FALSE;
|
|
|
|
for (hwndGroup=GetWindow(hwndGroup, GW_HWNDLAST); hwndGroup; hwndGroup=GetWindow(hwndGroup, GW_HWNDPREV)) {
|
|
if (GetWindow(hwndGroup, GW_OWNER))
|
|
continue;
|
|
|
|
/* Save the latest sizes and positions. */
|
|
if (bSaveGroupSettings) {
|
|
SetGroupDimensions(hwndGroup);
|
|
if (wLockError == LOCK_LOWMEM && !fLowMemErrYet) {
|
|
// No more error messages.
|
|
fLowMemErrYet = TRUE;
|
|
wLockError = 0;
|
|
// Warn user that some settings couldn't be saved.
|
|
MyMessageBox(hwndProgman, IDS_OOMEXITTITLE, IDS_LOWMEMONEXIT, NULL,
|
|
MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
|
|
}
|
|
}
|
|
|
|
SaveGroup(hwndGroup, TRUE);
|
|
}
|
|
|
|
RegFlushKey(HKEY_CURRENT_USER);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* WriteINIFile() - */
|
|
|
|
|
|
|
|
void FAR PASCAL WriteINIFile()
|
|
{
|
|
register int i;
|
|
RECT rc;
|
|
TCHAR szT[40];
|
|
HANDLE hCursor;
|
|
|
|
|
|
// Don't save if restricted. But force save if we've just converted the
|
|
// ansi groups to unicode so we can work from unicode the next time around.
|
|
|
|
if (fNoSave && !bUseANSIGroups)
|
|
return;
|
|
|
|
fLowMemErrYet = FALSE;
|
|
|
|
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
ShowCursor(TRUE);
|
|
|
|
i = GetInternalWindowPos(hwndProgman, &rc, NULL);
|
|
|
|
if (i == SW_SHOWMINNOACTIVE)
|
|
i = SW_SHOWNORMAL;
|
|
|
|
wsprintf(szT, TEXT("%d %d %d %d %d"), rc.left, rc.top, rc.right, rc.bottom, i);
|
|
if (hkeyPMSettings) {
|
|
RegSetValueEx(hkeyPMSettings, szWindow, 0, REG_SZ, (LPBYTE)szT, sizeof(TCHAR) * (lstrlen(szT) + 1));
|
|
}
|
|
|
|
if (!bLoadEvil)
|
|
WriteGroupsSection();
|
|
|
|
// Save all groups content, TRUE means we want the size and position saved
|
|
// as well.
|
|
|
|
SaveGroupsContent(TRUE);
|
|
|
|
ShowCursor(FALSE);
|
|
SetCursor(hCursor);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* GetItemCommand() - */
|
|
|
|
|
|
|
|
void FAR PASCAL GetItemCommand(
|
|
PGROUP pGroup,
|
|
PITEM pItem,
|
|
LPTSTR lpCommand,
|
|
LPTSTR lpDir)
|
|
{
|
|
BYTE b = 0;
|
|
LPTSTR lp1, lp2, lp3;
|
|
LPGROUPDEF lpgd;
|
|
LPITEMDEF lpid;
|
|
BOOL bNoFirstQuote = TRUE;
|
|
|
|
if (!GetGroupTag(pGroup, pItem, (WORD)ID_APPLICATIONDIR, lpCommand, MAXITEMPATHLEN)) {
|
|
// the application directory is not defined
|
|
*lpCommand = 0;
|
|
}
|
|
|
|
lpgd = LockGroup(pGroup->hwnd);
|
|
if (!lpgd) {
|
|
*lpCommand = 0;
|
|
*lpDir = 0;
|
|
return;
|
|
}
|
|
lpid = ITEM(lpgd, pItem->iItem);
|
|
|
|
// init working directory
|
|
lp3 = lpDir;
|
|
*lp3 = 0;
|
|
|
|
// item command
|
|
lp1 = (LPTSTR)PTR(lpgd, lpid->pCommand);
|
|
if (*lp1 == TEXT('"')) {
|
|
lp2 = lp1;
|
|
while (lp2 && (lp2 = wcschr(lp2 + 1, TEXT('"'))) && *(lp2 + 1) != TEXT('\\')) { //go to next quote
|
|
;
|
|
}
|
|
if (!lp2) {
|
|
|
|
// The directory is not in quotes and since the command path starts
|
|
// with a quote, there's no working directory.
|
|
|
|
lp2 = lpDir;
|
|
*lp2 = 0;
|
|
} else {
|
|
if (*(lp2 + 1) == TEXT('\\')) {
|
|
|
|
// the working directory is in quotes
|
|
|
|
*lp3++ = *lp1++; //write first quote
|
|
|
|
for (; *lp1 && lp1 != lp2; lp1 = CharNext(lp1)) {
|
|
*lp3++ = *lp1;
|
|
}
|
|
if (*lp1 == TEXT('"')) {
|
|
*lp3++ = *lp1++; //write last quote
|
|
lp1++;
|
|
*lp3 = 0;
|
|
}
|
|
}
|
|
lp2 = lp3 + lstrlen(lp3);
|
|
}
|
|
} else {
|
|
|
|
// if there's a working directory, it is not in quotes
|
|
|
|
|
|
for (lp2 = lp3 = lpDir; *lp1 && *lp1 != TEXT(' ') && *lp1 != TEXT('"'); // the command line might be in quotes
|
|
lp1 = CharNext(lp1)) {
|
|
|
|
*lp3++ = *lp1;
|
|
|
|
if (*lp1 == TEXT(':') || *lp1 == TEXT('\\'))
|
|
lp2 = lp3;
|
|
}
|
|
*lp3 = 0;
|
|
}
|
|
|
|
/* we are assuming the exe dir contains the necessary separator
|
|
* add the filename to the command line
|
|
*/
|
|
lstrcat(lpCommand, lp2);
|
|
/* add the arguments to the command line
|
|
*/
|
|
lstrcat(lpCommand, lp1);
|
|
|
|
|
|
/* truncate the command name from the exe path. note this implies
|
|
* that if there is no path, lpDir will be empty
|
|
*/
|
|
*lp2 = 0;
|
|
lp2 = CharPrev(lpDir, lp2);
|
|
if (*lp2 == TEXT('\\') && *CharPrev(lpDir, lp2) != TEXT(':'))
|
|
*lp2 = 0;
|
|
|
|
UnlockGroup(pGroup->hwnd);
|
|
}
|
|
|
|
|
|
PITEM PASCAL DuplicateItem(
|
|
PGROUP pGroup,
|
|
PITEM pItem,
|
|
PGROUP pGNew,
|
|
LPPOINT lppt)
|
|
{
|
|
WORD wIconId;
|
|
WORD wIconIndex;
|
|
LPITEMDEF lpid;
|
|
LPGROUPDEF lpgd;
|
|
TCHAR szCommand[MAXITEMPATHLEN + 1];
|
|
TCHAR szDefDir[2 * (MAXITEMPATHLEN + 1)];
|
|
TCHAR szIconPath[MAXITEMPATHLEN + 1];
|
|
TCHAR szName[64];
|
|
TCHAR szExpPath[MAXITEMPATHLEN + 1];
|
|
TCHAR szExpDir[MAXITEMPATHLEN + 1];
|
|
DWORD dwFlags = CI_ACTIVATE;
|
|
|
|
lpid = LockItem(pGroup, pItem);
|
|
if (lpid == 0L) {
|
|
UnlockGroup(pGroup->hwnd);
|
|
return NULL;
|
|
}
|
|
|
|
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
|
|
|
|
lstrcpy(szName, (LPTSTR)PTR(lpgd, lpid->pName));
|
|
lstrcpy(szIconPath, (LPTSTR)PTR(lpgd, lpid->pIconPath));
|
|
wIconId = lpid->iIcon;
|
|
wIconIndex = lpid->wIconIndex;
|
|
GlobalUnlock(pGroup->hGroup);
|
|
UnlockGroup(pGroup->hwnd);
|
|
|
|
GetItemCommand(pGroup, pItem, szCommand, szDefDir);
|
|
|
|
// I f there's no icon path, check if we have an executable associated with the command path.
|
|
if (!*szIconPath) {
|
|
lstrcpy(szExpPath, szCommand);
|
|
DoEnvironmentSubst(szExpPath, CharSizeOf(szExpPath));
|
|
StripArgs(szExpPath);
|
|
lstrcpy(szExpDir, szDefDir);
|
|
DoEnvironmentSubst(szExpDir, CharSizeOf(szExpDir));
|
|
FindExecutable(szExpPath, szExpDir, szIconPath);
|
|
if (!*szIconPath) {
|
|
dwFlags |= CI_NO_ASSOCIATION;
|
|
} else
|
|
*szIconPath = 0;
|
|
}
|
|
if (GroupFlag(pGroup, pItem, (WORD)ID_NEWVDM)) {
|
|
dwFlags |= CI_SEPARATE_VDM;
|
|
}
|
|
|
|
return CreateNewItem(pGNew->hwnd,
|
|
szName,
|
|
szCommand,
|
|
szIconPath,
|
|
szDefDir,
|
|
GroupFlag(pGroup, pItem, (WORD)ID_HOTKEY),
|
|
GroupFlag(pGroup, pItem, (WORD)ID_MINIMIZE),
|
|
wIconId,
|
|
wIconIndex,
|
|
NULL,
|
|
lppt,
|
|
dwFlags);
|
|
} |