Windows2000/private/windows/shell/progman/pmgroup.c
2020-09-30 17:12:32 +02:00

2152 lines
56 KiB
C

#include "progman.h"
#include "ime.h"
BOOL FAR PASCAL IMEStringWindow(HWND,HANDLE);
int FAR PASCAL IMEWindowGetCnt(LPTSTR,LPTSTR);
#define HK_SHIFT 0x0100
#define HK_CONTROL 0x0200
#define HK_ALT 0x0400
#define HK_EXT 0x0800
#define F_EXT 0x01000000L
#define OBJ_ITEM 1
BOOL bNoScrollCalc = FALSE;
RECT rcArrangeRect;
HWND hwndScrolling = NULL;
BOOL APIENTRY InQuotes(LPTSTR sz);
void NEAR PASCAL ViewActiveItem(PGROUP pGroup);
/* Make the first item in a list the last and return a pointer to it.*/
PITEM PASCAL MakeFirstItemLast(PGROUP pGroup)
{
PITEM pItemCur;
/* Just quit if there's no list.*/
if ((pItemCur = pGroup->pItems) == NULL)
return NULL;
/* Find the end of the list.*/
for( ; pItemCur->pNext ; pItemCur = pItemCur->pNext)
;
/* End of the list.*/
/* This works even if there is only one item in the */
/* list, it's a waste of time though.*/
pItemCur->pNext = pGroup->pItems;
pGroup->pItems = pGroup->pItems->pNext;
pItemCur->pNext->pNext = NULL;
return pItemCur->pNext;
}
VOID PASCAL GetItemText(PGROUP pGroup, PITEM pItem, LPTSTR lpT, int index)
{
LPITEMDEF lpid;
LPGROUPDEF lpgd;
lpid = LockItem(pGroup,pItem);
if (!lpid) {
UnlockGroup(pGroup->hwnd);
*lpT = 0;
return;
}
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
switch (index) {
case 0:
lstrcpy(lpT, (LPTSTR) PTR(lpgd, lpid->pName));
break;
case 1:
lstrcpy(lpT, (LPTSTR) PTR(lpgd, lpid->pCommand));
break;
case 2:
lstrcpy(lpT, (LPTSTR) PTR(lpgd, lpid->pIconPath));
break;
default:
*lpT = 0;
break;
}
GlobalUnlock(pGroup->hGroup);
UnlockGroup(pGroup->hwnd);
}
ULONG Color16Palette[] = {
0x00000000, 0x00800000, 0x00008000, 0x00808000,
0x00000080, 0x00800080, 0x00008080, 0x00808080,
0x00c0c0c0, 0x00ff0000, 0x0000ff00, 0x00ffff00,
0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
};
ULONG Color256Palette[] = {
0x00000000, 0x00800000, 0x00008000, 0x00808000, 0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0,
0x00c0dcc0, 0x00a6caf0, 0x00cccccc, 0x00580800, 0x00600800, 0x00680800, 0x00700800, 0x00780800,
0x00801000, 0x00881000, 0x00901000, 0x00981000, 0x00a01000, 0x00a81000, 0x00b01000, 0x00b81000,
0x00c01800, 0x00c81800, 0x00d01800, 0x00d81800, 0x00e01800, 0x00e81800, 0x00f01800, 0x00f81800,
0x00002000, 0x00082000, 0x00102000, 0x00182000, 0x00202000, 0x00282000, 0x00302000, 0x00382000,
0x00402800, 0x00482800, 0x00502800, 0x00582800, 0x00602800, 0x00682800, 0x00702800, 0x00782800,
0x00803000, 0x00883000, 0x00903000, 0x00983000, 0x00a03000, 0x00a83000, 0x00b03000, 0x00b83000,
0x00c03800, 0x00c83800, 0x00d03800, 0x00d83800, 0x00e03800, 0x00e83800, 0x00f03800, 0x00f83800,
0x00004010, 0x00084010, 0x00104010, 0x00184010, 0x00204010, 0x00284010, 0x00304010, 0x00384010,
0x00404810, 0x00484810, 0x00504810, 0x00584810, 0x00604810, 0x00684810, 0x00704810, 0x00784810,
0x00805010, 0x00885010, 0x00905010, 0x00985010, 0x00a05010, 0x00a85010, 0x00b05010, 0x00b85010,
0x00c05810, 0x00c85810, 0x00d05810, 0x00d85810, 0x00e05810, 0x00e85810, 0x00f05810, 0x00f85810,
0x00006010, 0x00086010, 0x00106010, 0x00186010, 0x00206010, 0x00286010, 0x00306010, 0x00386010,
0x00406810, 0x00486810, 0x00506810, 0x00586810, 0x00606810, 0x00686810, 0x00706810, 0x00786810,
0x00807010, 0x00887010, 0x00907010, 0x00987010, 0x00a07010, 0x00a87010, 0x00b07010, 0x00b87010,
0x00c07810, 0x00c87810, 0x00d07810, 0x00d87810, 0x00e07810, 0x00e87810, 0x00f07810, 0x00f87810,
0x00008020, 0x00088020, 0x00108020, 0x00188020, 0x00208020, 0x00288020, 0x00308020, 0x00388020,
0x00408820, 0x00488820, 0x00508820, 0x00588820, 0x00608820, 0x00688820, 0x00708820, 0x00788820,
0x00809020, 0x00889020, 0x00909020, 0x00989020, 0x00a09020, 0x00a89020, 0x00b09020, 0x00b89020,
0x00c09820, 0x00c89820, 0x00d09820, 0x00d89820, 0x00e09820, 0x00e89820, 0x00f09820, 0x00f89820,
0x0000a020, 0x0008a020, 0x0010a020, 0x0018a020, 0x0020a020, 0x0028a020, 0x0030a020, 0x0038a020,
0x0040a820, 0x0048a820, 0x0050a820, 0x0058a820, 0x0060a820, 0x0068a820, 0x0070a820, 0x0078a820,
0x0080b020, 0x0088b020, 0x0090b020, 0x0098b020, 0x00a0b020, 0x00a8b020, 0x00b0b020, 0x00b8b020,
0x00c0b820, 0x00b820c8, 0x00b820d0, 0x00b820d8, 0x00b820e0, 0x00b820e8, 0x00b820f0, 0x00b820f8,
0x0000c030, 0x00c03008, 0x00c03010, 0x00c03018, 0x00c03020, 0x00c03028, 0x00c03030, 0x00c03038,
0x0040c830, 0x00c83048, 0x00c83050, 0x00c83058, 0x00c83060, 0x00c83068, 0x00c83070, 0x00c83078,
0x0080d030, 0x00d03088, 0x00d03090, 0x00d03098, 0x00d030a0, 0x00d030a8, 0x00d030b0, 0x00d030b8,
0x00c0d830, 0x00c8d830, 0x00d0d830, 0x00d8d830, 0x00e0d830, 0x00e8d830, 0x00f0d830, 0x00f8d830,
0x0000e030, 0x0008e030, 0x0010e030, 0x0018e030, 0x0020e030, 0x0028e030, 0x0030e030, 0x0038e030,
0x0040e830, 0x0048e830, 0x0050e830, 0x0058e830, 0x0060e830, 0x0068e830, 0x0070e830, 0x0078e830,
0x0080f030, 0x0088f030, 0x0090f030, 0x0098f030, 0x00a0f030, 0x00a8f030, 0x00fffbf0, 0x00a0a0a4,
0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00, 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
};
HICON APIENTRY GetItemIcon(HWND hwnd, PITEM pitem)
{
LPGROUPDEF lpgd;
LPITEMDEF lpid;
HICON hIcon = NULL;
DWORD dwVer;
HANDLE h;
LPBYTE p;
INT id = 0;
INT cb;
DWORD colors, size;
PBITMAPINFOHEADER pbih, pbihNew;
LPVOID palette;
lpgd = LockGroup(hwnd);
if (!lpgd)
return DuplicateIcon(hAppInstance, hItemIcon);
//return hItemIcon;
lpid = ITEM(lpgd,pitem->iItem);
if ((SHORT)lpid->cbIconRes > 0) {
if (lpid->wIconVer == 2)
dwVer = 0x00020000;
else
dwVer = 0x00030000;
pbihNew = NULL;
pbih = (PBITMAPINFOHEADER)PTR(lpgd, lpid->pIconRes);
size = lpid->cbIconRes;
if (pbih->biClrUsed == -1) {
colors = (1 << (pbih->biPlanes * pbih->biBitCount));
size += colors * sizeof(RGBQUAD);
if (colors == 16) {
palette = Color16Palette;
} else if (colors == 256) {
palette = Color256Palette;
} else {
palette = NULL;
}
if (palette != NULL)
pbihNew = (PBITMAPINFOHEADER)LocalAlloc(LPTR, size);
if (pbihNew != NULL) {
RtlCopyMemory(pbihNew, pbih, sizeof( *pbih ));
pbihNew->biClrUsed = 0;
RtlCopyMemory((pbihNew+1), palette, colors * sizeof(RGBQUAD));
RtlCopyMemory((PCHAR)(pbihNew+1) + (colors * sizeof(RGBQUAD)),
(pbih+1),
lpid->cbIconRes - sizeof(*pbih)
);
pbih = pbihNew;
}
else {
// reset size
size = lpid->cbIconRes;
}
}
hIcon = CreateIconFromResource((PBYTE)pbih, size, TRUE, dwVer);
if (pbihNew != NULL)
LocalFree(pbihNew);
}
if (!hIcon) {
if (h = FindResource(hAppInstance, (LPTSTR) MAKEINTRESOURCE(ITEMICON), RT_GROUP_ICON)) {
h = LoadResource(hAppInstance, h);
p = LockResource(h);
id = LookupIconIdFromDirectory(p, TRUE);
UnlockResource(h);
FreeResource(h);
}
if (h = FindResource(hAppInstance, (LPTSTR) MAKEINTRESOURCE(id), (LPTSTR) MAKEINTRESOURCE(RT_ICON))) {
cb = SizeofResource(hAppInstance, h);
h = LoadResource(hAppInstance, h);
p = LockResource(h);
hIcon = CreateIconFromResource(p, cb, TRUE, 0x00030000);
UnlockResource(h);
FreeResource(h);
}
}
UnlockGroup(hwnd);
return hIcon;
}
COLORREF PASCAL GetTitleTextColor(VOID)
{
COLORREF color;
color = GetSysColor(COLOR_WINDOW);
if (((WORD)LOBYTE(LOWORD(color)) +
(WORD)HIBYTE(LOWORD(color)) +
(WORD)LOBYTE(HIWORD(color))) >= 3*127)
{
return RGB(0,0,0);
}
else
{
return RGB(255,255,255);
}
}
#define REVERSEPAINT
void NEAR PASCAL ReverseGroupList(PGROUP pGroup)
{
PITEM pitem, p1, p2;
for (p1 = pGroup->pItems, p2 = p1->pNext, p1->pNext = NULL; p2; ) {
pitem = p2->pNext;
p2->pNext = p1;
p1 = p2;
p2 = pitem;
}
pGroup->pItems = p1;
}
VOID PASCAL PaintGroup(HWND hwnd)
{
register HDC hdc;
PGROUP pGroup;
PITEM pitem;
PAINTSTRUCT ps;
LPGROUPDEF lpgd;
LPITEMDEF lpid;
int fontheight;
HBRUSH hbrTitle;
HFONT hFontT;
RECT rcWin;
TEXTMETRIC tm;
HDC hdcMem;
HBITMAP hbmTemp;
HBRUSH hbr, hbrTemp;
INT nMax;
HICON hIcon;
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
hdc = BeginPaint(hwnd, &ps);
if (!pGroup->pItems) {
goto Exit;
}
GetClientRect(hwnd, &rcWin);
#ifdef WEIRDBUG
/* DavidPe - 05/15/91
* For some reason RectVisible() will return FALSE in
* situations it shouldn't. Since this is only a
* performance optimization, we can ignore the problem
* for now.
*/
if (!RectVisible(hdc, &rcWin))
goto Exit;
#endif
if (!(lpgd = LockGroup(hwnd)))
goto Exit;
hFontT = SelectObject(hdc, hFontTitle);
GetTextMetrics(hdc, &tm);
// ToddB: This seems like a good point, I don't see why tmExternalLeading should ever
// need to be considered. The result of decreasing the FontHeight is that DrawText
// will be used instead of TextOut in some cases, which should be harmless but might
// effect the apearence of some single line icon titles.
//#ifdef JAPAN
// Why we should think about ExternalLeading though we calculate the
// title rectange by DrawText() without DT_EXTERNALLEADING. -YN
fontheight = tm.tmHeight;
//#else
// fontheight = tm.tmHeight + tm.tmExternalLeading;
//#endif
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, GetTitleTextColor());
hbrTitle = NULL;
hdcMem = CreateCompatibleDC(hdc);
if (!hdcMem)
goto BitmapSetupComplete;
if (pGroup->hbm) {
hbmTemp = SelectObject(hdcMem, pGroup->hbm);
if (hbmTemp)
goto BitmapSetupComplete;
else
DeleteObject(pGroup->hbm);
}
for (nMax = 1, pitem = pGroup->pItems; pitem; pitem = pitem->pNext) {
if (nMax <= pitem->iItem)
nMax = pitem->iItem + 1;
}
pGroup->hbm = CreateDiscardableBitmap(hdc, cxIcon*lpgd->cItems, cyIcon);
if (!pGroup->hbm)
goto NukeMemDC;
hbmTemp = SelectObject(hdcMem, pGroup->hbm);
if (!hbmTemp)
goto NukeBitmap;
hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
if (!hbr)
goto NukeBitmap;
hbrTemp = SelectObject(hdcMem, hbr);
if (!hbrTemp)
goto NukeBrush;
PatBlt(hdcMem, 0, 0, cxIcon * lpgd->cItems, cyIcon, PATCOPY);
SelectObject(hdcMem, hbrTemp);
DeleteObject(hbr);
for (pitem = pGroup->pItems; pitem != NULL; pitem = pitem->pNext) {
if (hIcon = GetItemIcon(hwnd, pitem)) {
DrawIcon(hdcMem, pitem->iItem * cxIcon, 0, hIcon);
DestroyIcon(hIcon);
} else {
goto DeselectAndNukeBitmap;
}
}
goto BitmapSetupComplete;
NukeBrush:
DeleteObject(hbr);
DeselectAndNukeBitmap:
SelectObject(hdcMem, hbmTemp);
NukeBitmap:
DeleteObject(pGroup->hbm);
pGroup->hbm = NULL;
NukeMemDC:
DeleteDC(hdcMem);
hdcMem = NULL;
BitmapSetupComplete:
ReverseGroupList(pGroup); // reverse the icon list
/* Paint the icons */
for (pitem = pGroup->pItems; pitem; pitem = pitem->pNext) {
if (!pitem->pNext
&& pGroup == pCurrentGroup
&& hwndProgman == GetActiveWindow()) {
hbrTitle = (HANDLE)1; // Use it as a flag
}
else
hbrTitle = (HANDLE)0;
lpid = ITEM(lpgd,pitem->iItem);
if (!bMove || !hbrTitle) {
if (hdcMem) {
BitBlt(hdc, pitem->rcIcon.left + cxOffset,
pitem->rcIcon.top + cyOffset,
lpgd->cxIcon, lpgd->cyIcon, hdcMem,
lpgd->cxIcon*pitem->iItem, 0,
SRCCOPY);
}
else {
if (RectVisible(hdc,&pitem->rcIcon)) {
if (hIcon = GetItemIcon(hwnd,pitem)) {
DrawIcon(hdc, pitem->rcIcon.left + cxOffset,
pitem->rcIcon.top + cyOffset, hIcon);
}
else {
PatBlt(hdc,pitem->rcIcon.left + cxOffset,
pitem->rcIcon.top + cyOffset,
cxIcon, cyIcon, BLACKNESS);
}
}
}
}
}
/* Paint the titles. */
for (pitem = pGroup->pItems; pitem; pitem = pitem->pNext) {
/* test for the active icon */
if (!pitem->pNext
&& pGroup == pCurrentGroup
&& hwndProgman == GetActiveWindow()) {
SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
hbrTitle = CreateSolidBrush(GetSysColor(COLOR_ACTIVECAPTION));
}
else {
hbrTitle = (HANDLE)0;
}
lpid = ITEM(lpgd,pitem->iItem);
if (!bMove || !hbrTitle) {
if (hbrTitle)
FillRect(hdc, &(pitem->rcTitle), hbrTitle);
/* draw multi line titles like USER does */
if (pitem->rcTitle.bottom - pitem->rcTitle.top < fontheight*2)
TextOut(hdc, pitem->rcTitle.left+cxOffset, pitem->rcTitle.top,
(LPTSTR) PTR(lpgd, lpid->pName), lstrlen((LPTSTR) PTR(lpgd, lpid->pName)));
else {
if (RectVisible(hdc,&pitem->rcTitle)) {
DrawText(hdc,
(LPTSTR)PTR(lpgd, lpid->pName), -1,
&(pitem->rcTitle),
bIconTitleWrap ?
DT_CENTER | DT_WORDBREAK | DT_NOPREFIX :
DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_SINGLELINE);
}
}
}
if (hbrTitle) {
SetTextColor(hdc, GetTitleTextColor());
DeleteObject(hbrTitle);
hbrTitle = NULL;
}
}
ReverseGroupList(pGroup); // re-reverse the icon list
if (hFontT) {
SelectObject(hdc, hFontT);
}
if (hdcMem) {
SelectObject(hdcMem, hbmTemp);
DeleteDC(hdcMem);
}
UnlockGroup(hwnd);
Exit:
SetBkMode(hdc, OPAQUE);
EndPaint(hwnd, &ps);
#ifdef DEBUG
ProfStop();
{
TCHAR buf[80];
wsprintf(buf, TEXT("msec to paint group = %ld\r\n"), GetTickCount() - dwStartTime);
OutputDebugString(buf);
}
#endif
}
/* Draw the group icon. */
VOID DrawGroupIcon(HWND hwnd)
{
PAINTSTRUCT ps;
HDC hDC;
PGROUP pGroup;
hDC = BeginPaint(hwnd, &ps);
pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
if (pGroup->fCommon) {
DrawIcon(hDC, cxOffset, cyOffset, hCommonGrpIcon);
}
else {
DrawIcon(hDC, cxOffset, cyOffset, hGroupIcon);
}
EndPaint(hwnd, &ps);
}
PITEM PASCAL ItemHitTest(
PGROUP pGroup,
POINTS pts)
{
PITEM pItem;
POINT pt;
pt.x = (int)pts.x;
pt.y = (int)pts.y;
for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) {
if (PtInRect(&pItem->rcIcon, pt) || PtInRect(&pItem->rcTitle, pt)) {
break;
}
}
return pItem;
}
HRGN PASCAL IconExcludedRgn(PGROUP pGroup, PITEM pItem)
{
RECT rc;
PITEM pItemT;
HRGN hrgn, hrgnT;
hrgn = CreateRectRgn(0,0,0,0);
if (!hrgn)
return NULL;
hrgnT = CreateRectRgn(0,0,0,0);
if (!hrgnT)
{
return hrgn;
}
for (pItemT = pGroup->pItems;
pItemT && pItemT != pItem;
pItemT = pItemT->pNext)
{
if (IntersectRect(&rc,&pItem->rcIcon,&pItemT->rcIcon))
{
SetRectRgn(hrgnT,rc.left,rc.top,rc.right,rc.bottom);
CombineRgn(hrgn,hrgn,hrgnT,RGN_OR);
}
if (IntersectRect(&rc,&pItem->rcIcon,&pItemT->rcTitle))
{
SetRectRgn(hrgnT,rc.left,rc.top,rc.right,rc.bottom);
CombineRgn(hrgn,hrgn,hrgnT,RGN_OR);
}
}
DeleteObject(hrgnT);
return hrgn;
}
VOID APIENTRY InvalidateIcon(PGROUP pGroup, PITEM pItem)
{
RECT rc;
if (!pGroup || !pItem)
return;
UnionRect(&rc, &pItem->rcIcon, &pItem->rcTitle);
if (bAutoArranging)
UnionRect(&rcArrangeRect, &rcArrangeRect, &rc);
else
InvalidateRect(pGroup->hwnd,&rc,TRUE);
}
VOID PASCAL BringItemToTop(PGROUP pGroup, PITEM pItem, BOOL fUpdate)
{
PITEM pItemT;
HRGN hrgn;
if (pItem == pGroup->pItems) {
return;
}
if (hrgn = IconExcludedRgn(pGroup, pItem)) {
InvalidateRgn(pGroup->hwnd, hrgn, TRUE);
DeleteObject(hrgn);
}
/*
* At this point we know there is at least two items, and we're not the
* first one...
*/
for (pItemT = pGroup->pItems; pItemT->pNext != pItem; pItemT = pItemT->pNext)
;
pItemT->pNext = pItem->pNext;
pItem->pNext = pGroup->pItems;
pGroup->pItems = pItem;
/*
* Invalidate the whole titles in order to change the color.
*/
if (fUpdate) {
InvalidateRect(pGroup->hwnd, &pItem->rcTitle, TRUE);
InvalidateRect(pGroup->hwnd, &pItem->pNext->rcTitle, TRUE);
}
}
VOID PASCAL ClickOn(HWND hwnd, POINTS pts)
{
PGROUP pGroup;
PITEM pItem;
POINT pt;
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
pItem = ItemHitTest(pGroup, pts);
if (!pItem) {
return;
}
BringItemToTop(pGroup, pItem, TRUE);
ViewActiveItem(pGroup);
pt.x = (int)pts.x;
pt.y = (int)pts.y;
*(LPPOINT)&rcDrag.left = pt;
*(LPPOINT)&rcDrag.right = pt;
hwndDrag = hwnd;
InflateRect(&rcDrag, GetSystemMetrics(SM_CXDOUBLECLK) / 2,
GetSystemMetrics(SM_CYDOUBLECLK) / 2);
}
VOID PASCAL DragItem(HWND hwnd)
{
PGROUP pGroup;
PITEM pItem;
HICON hIcon;
pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
pItem = pGroup->pItems;
if (!pItem || hwndDrag != hwnd)
goto ProcExit;
/* If the control key isn't down, do a Move operation. */
bMove = !(GetKeyState(VK_CONTROL) & 0x8000);
/* Don't allow "moves" from RO groups. */
if (pGroup->fRO && bMove == TRUE)
goto ProcExit;
/*
* Redraw the window minus the item we're moving.
* REVIEW - if you just painted the background colour into the
* pItem->rcIcon area then you could remove the bMove code from
* PaintGroup().
*/
if (bMove) {
InvalidateIcon(pGroup,pItem);
UpdateWindow(hwnd);
}
hIcon = GetItemIcon(hwnd,pItem);
// BUG BUG MAKELONG(pGroup,pItem) doesn't make sense since all
// pointers all LOMG in WIN32. Will need to change the parameters!
// johannec 08-19-91
if (DragObject(hwndMDIClient, hwnd, (UINT)OBJ_ITEM,
MAKELONG(pGroup,pItem), hIcon) == DRAG_COPY) {
if (bMove)
DeleteItem(pGroup,pItem);
}
else {
/* Drag was SWP or drag failed... just show the item. */
if (bMove) {
bMove = FALSE;
InvalidateIcon(pGroup,pItem);
}
}
DestroyIcon(hIcon);
ProcExit:
bMove = FALSE;
}
void PASCAL GetRealClientRect(
HWND hwnd,
DWORD dwStyle,
LPRECT lprcClient)
{
DWORD Style;
Style = GetWindowLong(hwnd, GWL_STYLE);
/*BUG BUG will GWL_STYLE work???*/
SetWindowLong(hwnd,GWL_STYLE,dwStyle);
GetWindowRect(hwnd,lprcClient);
ScreenToClient(hwnd,(LPPOINT)&lprcClient->left);
ScreenToClient(hwnd,(LPPOINT)&lprcClient->right);
SendMessage(hwnd,WM_NCCALCSIZE,0,(LPARAM)lprcClient);
}
VOID APIENTRY CalcGroupScrolls(HWND hwnd)
{
register PGROUP pGroup;
register PITEM pItem;
RECT rcClient;
RECT rcRange;
RECT rcT;
DWORD dwStyle, dwStyleNew, dwStyleT;
int iMinPos, iMaxPos;
if (bNoScrollCalc || IsIconic(hwnd))
return;
// Stop re-entrance of this routine.
bNoScrollCalc = TRUE;
pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
if (!pGroup->pItems) {
// No items...
SetRectEmpty(&rcRange);
goto ChangeStyle;
}
hwndScrolling = hwnd;
// If the user has selected auto arranging then make
// the next item in the z-order visable.
if (bAutoArrange)
ViewActiveItem(pGroup);
SetRectEmpty(&rcRange);
for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext)
{
UnionRect(&rcRange,&rcRange,&pItem->rcIcon);
rcT.top = pItem->rcTitle.top; // don't include the
rcT.bottom = pItem->rcTitle.bottom; // title overhang part
rcT.left = pItem->rcIcon.left;
rcT.right = pItem->rcIcon.right;
UnionRect(&rcRange,&rcRange,&rcT);
}
if (rcRange.left != rcRange.right)
{
// Add on a bit for the left border here.
rcRange.left -= ((cxArrange-cxIconSpace)/2)+cxOffset;
// Don't add on a right border so we can cram as many icons in as poss.
// rcRange.right += ((cxArrange-cxIconSpace)/2);
// ADJUST THE RECT SO THAT WE DON'T GET SCROLL BARS IF ONLY THE BORDERS
// OF TEXT ARE NOT VISIBLE ~~~
}
ChangeStyle:
dwStyleNew = dwStyle = GetWindowLong(hwnd,GWL_STYLE);
dwStyleNew &= ~(WS_HSCROLL | WS_VSCROLL);
for (;;)
{
dwStyleT = dwStyleNew;
GetRealClientRect(hwnd, dwStyleNew, &rcClient);
if (rcRange.left < rcClient.left || rcRange.right > rcClient.right)
dwStyleNew |= WS_HSCROLL;
if (rcRange.top < rcClient.top || rcRange.bottom > rcClient.bottom)
dwStyleNew |= WS_VSCROLL;
if (dwStyleNew == dwStyleT)
break;
}
if (dwStyleNew == dwStyle && !(dwStyle & (WS_VSCROLL|WS_HSCROLL)))
{
/* none there and don't need to add 'em!
*/
goto ProcExit;
}
UnionRect(&rcRange,&rcClient,&rcRange);
/* union garantees that left==right or top==bottom in case of no
* scrollbar.
*/
rcRange.right -= rcClient.right-rcClient.left;
rcRange.bottom -= rcClient.bottom-rcClient.top;
/* if the style changed, don't redraw in sb code, just redraw the
* frame at the end cause the whole ncarea has to be repainted
* if it hasn't changed, just move the thumb
*/
if (dwStyleNew==dwStyle)
{
if (dwStyleNew & WS_HSCROLL)
{
if (GetScrollPos(hwnd,SB_HORZ)!=0)
goto SetScrollInfo;
GetScrollRange(hwnd,SB_HORZ,&iMinPos,&iMaxPos);
if ((iMinPos != rcRange.left) || (iMaxPos != rcRange.right))
goto SetScrollInfo;
}
if (dwStyleNew & WS_VSCROLL)
{
if (GetScrollPos(hwnd,SB_VERT)!=0)
goto SetScrollInfo;
GetScrollRange(hwnd,SB_VERT,&iMinPos,&iMaxPos);
if ((iMinPos != rcRange.top) || (iMaxPos != rcRange.bottom))
goto SetScrollInfo;
}
goto ProcExit;
}
SetScrollInfo:
SetScrollPos(hwnd,SB_HORZ,0,FALSE);
SetScrollPos(hwnd,SB_VERT,0,FALSE);
SetScrollRange(hwnd,SB_HORZ,rcRange.left,rcRange.right,FALSE);
SetScrollRange(hwnd,SB_VERT,rcRange.top,rcRange.bottom,FALSE);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE
| SWP_NOMOVE | SWP_NOACTIVATE
| SWP_DRAWFRAME);
ProcExit:
// Allow other scroll calculations.
bNoScrollCalc = FALSE;
}
VOID PASCAL ScrollGroup(PGROUP pGroup, int xMove, int yMove, BOOL fCalc)
{
register PITEM pItem;
for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext)
{
OffsetRect(&pItem->rcIcon,xMove,yMove);
OffsetRect(&pItem->rcTitle,xMove,yMove);
}
ScrollWindow(pGroup->hwnd,xMove,yMove,NULL,NULL);
UpdateWindow(pGroup->hwnd);
if (fCalc)
CalcGroupScrolls(pGroup->hwnd);
}
VOID APIENTRY ViewActiveItem(PGROUP pGroup)
{
RECT rcClient, rc;
int xMove = 0, yMove = 0;
GetClientRect(pGroup->hwnd,&rcClient);
UnionRect(&rc, &pGroup->pItems->rcIcon, &pGroup->pItems->rcTitle);
// Clip width to that of icon i.e. ignore width of text.
rc.left = pGroup->pItems->rcIcon.left;
rc.right = pGroup->pItems->rcIcon.right;
if (rc.left < rcClient.left)
xMove = rcClient.left - rc.left;
else if (rc.right > rcClient.right)
xMove = rcClient.right - rc.right;
if (rc.top < rcClient.top)
yMove = rcClient.top - rc.top;
else if (rc.bottom > rcClient.bottom)
yMove = rcClient.bottom - rc.bottom;
if (xMove || yMove)
ScrollGroup(pGroup, xMove, yMove,TRUE);
}
BOOL FAR PASCAL CheckHotKey(WPARAM wParam, LPARAM lParam)
{
HWND hwndT;
PGROUP pGroup;
PITEM pItem;
switch (wParam)
{
case VK_SHIFT:
case VK_CONTROL:
case VK_MENU:
case VK_RETURN:
return FALSE;
}
if (GetKeyState(VK_SHIFT) < 0) {
// DBG((" + SHIFT"));
wParam |= HK_SHIFT;
}
if (GetKeyState(VK_CONTROL) < 0) {
// DBG((" + CONTROL"));
wParam |= HK_CONTROL;
}
if (GetKeyState(VK_MENU) < 0) {
// DBG((" + ALT"));
wParam |= HK_ALT;
}
if (lParam & F_EXT) {
// DBG((" EXTENDED"));
wParam |= HK_EXT;
}
// DBG(("... Full code %4.4X...\r\n",wParam));
for (hwndT = GetWindow(hwndMDIClient,GW_CHILD);
hwndT;
hwndT = GetWindow(hwndT,GW_HWNDNEXT)) {
if (GetWindow(hwndT,GW_OWNER))
continue;
pGroup = (PGROUP)GetWindowLongPtr(hwndT,GWLP_PGROUP);
for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) {
// DBG(("Checking (%4.4X,%4.4X)...\r\n",pGroup,pItem));
if (GroupFlag(pGroup,pItem,(WORD)ID_HOTKEY) == (WORD)wParam) {
// DBG(("F*O*U*N*D\r\n"));
ExecItem(pGroup,pItem,FALSE,FALSE);
return TRUE;
}
}
}
return FALSE;
}
VOID APIENTRY KeyWindow(HWND hwnd, WORD wDirection)
{
int wT;
int wNext;
RECT rc;
RECT rcT;
POINT ptA;
POINT ptT;
PGROUP pGroup;
PITEM pItem, pItemNext;
pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
if (!pGroup->pItems)
return;
wNext = 0x7FFF;
pItemNext = NULL;
CopyRect(&rc,&pGroup->pItems->rcIcon);
for (pItem = pGroup->pItems->pNext; pItem; pItem = pItem->pNext) {
CopyRect(&rcT,&pItem->rcIcon);
ptT.x = rcT.left - rc.left;
ptT.y = rcT.top - rc.top;
ptA.x = (ptT.x < 0) ? -ptT.x : ptT.x;
ptA.y = (ptT.y < 0) ? -ptT.y : ptT.y;
switch (wDirection) {
case VK_LEFT:
if ((ptT.x >= 0) || (ptA.x < ptA.y))
continue;
break;
case VK_RIGHT:
if ((ptT.x <= 0) || (ptA.x < ptA.y))
continue;
break;
case VK_DOWN:
if ((ptT.y <= 0) || (ptA.y < ptA.x))
continue;
break;
case VK_UP:
if ((ptT.y >= 0) || (ptA.y < ptA.x))
continue;
break;
default:
/* illegal key
*/
return;
}
wT = ptA.y + ptA.x;
if (wT <= wNext) {
wNext = wT;
pItemNext = pItem;
}
}
if (pItemNext) {
BringItemToTop(pGroup,pItemNext, TRUE);
ViewActiveItem(pGroup);
}
}
VOID APIENTRY CharWindow(register HWND hwnd, register WORD wChar)
{
LPGROUPDEF lpgd;
LPITEMDEF lpid;
PGROUP pGroup;
PITEM pItem, pItemLast;
pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
if (!pGroup->pItems)
return;
lpgd = LockGroup(hwnd);
if (!lpgd)
return;
/* Search for item, skip the currently selected one.*/
for ( pItem = pGroup->pItems->pNext; pItem; pItem=pItem->pNext)
{
lpid = ITEM(lpgd,pItem->iItem);
if (CharUpper((LPTSTR)(DWORD)wChar)
== CharUpper((LPTSTR)(DWORD)(BYTE)*PTR(lpgd, lpid->pName)))
{
pItemLast = MakeFirstItemLast(pGroup);
BringItemToTop(pGroup,pItem, FALSE);
/* Handle updates.*/
InvalidateRect(pGroup->hwnd,&pItem->rcTitle,TRUE);
InvalidateRect(pGroup->hwnd,&pItemLast->rcTitle,TRUE);
ViewActiveItem(pGroup);
break;
}
}
UnlockGroup(hwnd);
}
VOID APIENTRY ScrollMessage(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
int wMin;
int wMax;
int wPos;
int wInc;
int wPage;
int wNewPos;
RECT rcClient;
int yMove;
int xMove;
BOOL fTemp;
GetClientRect(hwnd, &rcClient);
if (uiMsg == WM_HSCROLL) {
GetScrollRange(hwnd, SB_HORZ, &wMin, &wMax);
wPos = GetScrollPos(hwnd, SB_HORZ);
wInc = cxIconSpace + cxArrange / 2;
wPage = rcClient.right-rcClient.left;
}
else {
GetScrollRange(hwnd, SB_VERT, &wMin, &wMax);
wPos = GetScrollPos(hwnd, SB_VERT);
wInc = cyArrange;
wPage = rcClient.bottom-rcClient.top;
}
switch (GET_WM_VSCROLL_CODE(wParam, lParam)) {
case SB_BOTTOM:
wNewPos = wMax;
break;
case SB_TOP:
wNewPos = wMin;
break;
case SB_LINEDOWN:
wNewPos = wPos + wInc;
break;
case SB_LINEUP:
wNewPos = wPos - wInc;
break;
case SB_PAGEDOWN:
wNewPos = wPos + wPage;
break;
case SB_PAGEUP:
wNewPos = wPos - wPage;
break;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
wNewPos = (INT)(SHORT)GET_WM_VSCROLL_POS(wParam, lParam);
break;
case SB_ENDSCROLL:
// We might suddenly not need the scroll bars anymore so
// check now.
// Stop CGS from moving the view.
fTemp = bAutoArrange;
bAutoArrange = FALSE;
CalcGroupScrolls(hwnd);
bAutoArrange = fTemp;
/*** FALL THRU ***/
default:
return;
}
if (wNewPos < wMin)
wNewPos = wMin;
else if (wNewPos > wMax)
wNewPos = wMax;
if (uiMsg == WM_VSCROLL) {
SetScrollPos(hwnd, SB_VERT, wNewPos, TRUE);
yMove = wPos - wNewPos;
xMove = 0;
}
else {
SetScrollPos(hwnd, SB_HORZ, wNewPos, TRUE);
yMove = 0;
xMove = wPos - wNewPos;
}
ScrollGroup((PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP),xMove,yMove,FALSE);
}
VOID PASCAL OfficialRect(
LPRECT lprc,
int x,
int y,
int xOffset,
int yOffset)
{
// Work out were the icon should go in the icon grid, taking
// note of where the scroll bars are.
lprc->right = (lprc->left = x-xOffset + (cxIconSpace - cxArrange) / 2) +
cxArrange - 1;
lprc->bottom = (lprc->top = y-yOffset) + cyArrange - 1;
}
BOOL PASCAL IconOverlaps(
PGROUP pGroup,
LPRECT lprc,
int xOffset,
int yOffset)
{
PITEM pItem;
RECT rcT;
for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) {
// Ignore icons at -1. This is where icon's get put when
// we don't know where to put them and they will get moved
// later.
if (pItem->rcIcon.left == -1) {
continue;
}
OfficialRect(&rcT, pItem->rcIcon.left, pItem->rcIcon.top, xOffset, yOffset);
if (IntersectRect(&rcT, &rcT, lprc)) {
return TRUE;
}
}
return FALSE;
}
/*
* NB This is called for every icon at init time so put anything to do with
* finding icon positions inside the `if' because that's skipped on init.
* If you don't it'll get tres slow.
*/
VOID PASCAL ComputeIconPosition(
PGROUP pGroup,
POINT pt,
LPRECT lprcIcon,
LPRECT lprcTitle,
LPTSTR lpText)
{
HDC hdc;
int cch;
RECT rcClient, rcT;
HFONT hFontT;
int xsp, ysp; // Current position of scrollbar.
int vMax, vMin; // Range.
int hMax, hMin; // Range.
int xOffset, yOffset;
DWORD dwStyle;
if (pt.x == -1) {
/*
* Icon is in "find me a default position" mode...
* so search the icon space for it...
*/
// Get the current window style.
dwStyle = GetWindowLong(pGroup->hwnd,GWL_STYLE);
if (dwStyle & WS_MINIMIZE) {
// DBG(("PM.CIP: Window Minimised\n\r"));
// We want to use the restored state of the window.
GetInternalWindowPos(pGroup->hwnd, &rcClient, NULL);
// Convert from screen coords to client coords.
OffsetRect(&rcClient, -rcClient.left, -rcClient.top);
}
else {
// DBG(("PM.CIP: Window normal or maxed.\n\r"));
// Take into account scroll bars.
GetClientRect(pGroup->hwnd, &rcClient);
}
if (dwStyle & WS_HSCROLL) {
xsp = GetScrollPos(pGroup->hwnd, SB_HORZ);
GetScrollRange(pGroup->hwnd, SB_HORZ, &hMin, &hMax);
xOffset = xsp-hMin; // Offset icon grid to match scroll bar pos.
}
else {
xOffset = 0;
}
if (dwStyle & WS_VSCROLL) {
ysp = GetScrollPos(pGroup->hwnd, SB_VERT);
GetScrollRange(pGroup->hwnd, SB_VERT, &vMin, &vMax);
yOffset = ysp-vMin; // Offset icon grid.
}
else {
yOffset = 0;
}
pt.x = (cxArrange - cxIconSpace) / 2 + cxOffset - xOffset;
pt.y = 0 - yOffset;
/* Set this icon's left to -1 so that it'll be excluded
* by the IconOverlaps check.
*/
lprcIcon->left = -1;
for (;;) {
OfficialRect(&rcT, pt.x, pt.y, xOffset, yOffset);
if (!IconOverlaps(pGroup, &rcT, xOffset, yOffset)) {
break;
}
if (rcT.right + cxArrange > rcClient.right) {
pt.x = (cxArrange-cxIconSpace)/2 + cxOffset - xOffset;
pt.y += cyArrange;
}
else {
pt.x += cxArrange;
}
}
}
SetRect(lprcIcon, pt.x, pt.y, pt.x+cxIconSpace, pt.y+cyIconSpace);
if (IsRectEmpty(lprcTitle)) {
cch = lstrlen(lpText);
hdc = GetDC(pGroup->hwnd);
hFontT = SelectObject(hdc, hFontTitle);
/*
* Compute the icon rect using DrawText.
*/
lprcTitle->right = cxArrange - (2 * cxOffset);
DrawText(hdc, lpText, -1, lprcTitle, bIconTitleWrap ?
(WORD)(DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX) :
(WORD)(DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX | DT_SINGLELINE));
if (hFontT) {
SelectObject(hdc, hFontT);
}
ReleaseDC(pGroup->hwnd, hdc);
lprcTitle->right += cxOffset * 2;
lprcTitle->bottom += dyBorder * 2;
}
else {
SetRect(lprcTitle, 0, 0, lprcTitle->right - lprcTitle->left,
lprcTitle->bottom - lprcTitle->top);
}
OffsetRect(lprcTitle, pt.x+(cxIconSpace/2)-((lprcTitle->right-lprcTitle->left)/2),
pt.y + cyIconSpace - dyBorder);
// REVIEW Very expensive to do this here.
// if ((bAutoArrange) && (!bAutoArranging))
// ArrangeItems(pGroup->hwnd);
}
VOID APIENTRY MoveItem(PGROUP pGroup, PITEM pItem, POINT pt)
{
LPITEMDEF lpid;
LPGROUPDEF lpgd;
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
/*
* If the position is the same, ignore
*/
if ((pt.x == pItem->rcIcon.left) && (pt.y == pItem->rcIcon.top)) {
GlobalUnlock(pGroup->hGroup);
return;
}
/*
* Repaint the original position
*/
InvalidateIcon(pGroup, pItem);
lpid = LockItem(pGroup,pItem);
if (!lpid) {
GlobalUnlock(pGroup->hGroup);
return;
}
ComputeIconPosition(pGroup, pt, &pItem->rcIcon, &pItem->rcTitle,
(LPTSTR) PTR(lpgd, lpid->pName));
UnlockGroup(pGroup->hwnd);
GlobalUnlock(pGroup->hGroup);
/*
* Repaint the new position
*/
InvalidateIcon(pGroup,pItem);
// CalcGroupScrolls(pGroup->hwnd);
}
/* DropObject() - */
LONG NEAR PASCAL DropObject(HWND hWnd, LPDROPSTRUCT lpds)
{
BOOL fNC;
BOOL fOk;
POINT pt;
LPPOINT lppt;
PGROUP pGroup;
PITEM pItem;
RECT rcClient;
pGroup = pCurrentGroup;
pItem = pGroup->pItems;
pt = lpds->ptDrop;
// A drop anywhere in the window is valid.
GetWindowRect(hWnd, &rcClient);
// Convert to client coords.
ScreenToClient(hWnd,(LPPOINT)&(rcClient.left));
ScreenToClient(hWnd,(LPPOINT)&(rcClient.right));
if (pt.x >= rcClient.left && pt.y >= rcClient.top && pt.x <= rcClient.right
&& pt.y <= rcClient.bottom) {
/* Dropped in given point of client area. */
fNC = FALSE;
pt.x -= (GetSystemMetrics(SM_CXICON) / 2) + 2;
pt.y -= (GetSystemMetrics(SM_CYICON) / 2) + 2;
lppt = &pt;
}
else {
/* Dropped in nonclient area. */
fNC = TRUE;
lppt = NULL;
}
/* Are we iconic ? */
if (IsIconic(hWnd)) {
// Yep, we'll need to use default positioning.
fNC = TRUE;
lppt = NULL;
}
#if 0
// this if statement code if obsolete, it is never called. - johannec 8/11/93
if (lpds->wFmt == DOF_EXECUTABLE || lpds->wFmt == DOF_DOCUMENT) {
BuildDescription(szNameField, szPathName);
return((LONG)(CreateNewItem(hWnd,
szNameField, szPathName, szPathName, TEXT(""),
0, FALSE, 0, 0, NULL, lppt, CI_SET_DOS_FULLSCRN) != NULL));
}
#endif
if ((hWnd == pGroup->hwnd) && (bMove)) {
/* Don't drop on our own non-client area. */
if (fNC)
return 0L;
/* We are just moving the item within its own group.
* Hide it first so the icon title is treated correctly.
*/
MoveItem(pGroup,pItem, pt);
if ((bAutoArrange) && (!bAutoArranging))
ArrangeItems(pGroup->hwnd);
else if (!bAutoArranging)
CalcGroupScrolls(pGroup->hwnd);
return(DRAG_SWP);
}
else {
/* Copy the item to the new group... Set the hourglass
* cursor (it will get unset after the message returns),
* select the new group, and add the item at the specified
* point.
*/
fOk = DuplicateItem(pGroup,pItem,
(PGROUP)GetWindowLongPtr(hWnd,GWLP_PGROUP),lppt) != NULL;
/*
* Re-Arrange items within the destination group.
* NB The source will been taken care of by the DeleteItem routine
* called from DragItem.
*/
if ((bAutoArrange) && (!bAutoArranging)) {
/* Destination */
ArrangeItems(hWnd);
}
else if (!bAutoArranging) {
/* Destination */
CalcGroupScrolls(hWnd);
}
/* View the current item. */
BringItemToTop(pGroup,pItem, TRUE);
ViewActiveItem(pGroup);
/* If the dest isn't minimised then move the focus to it. */
if (!IsIconic(hWnd))
SetFocus(hWnd);
return (fOk ? DRAG_COPY : 0L);
}
}
LONG APIENTRY DropFiles(HWND hwnd, HANDLE hDrop)
{
POINT pt;
LPPOINT lppt;
UINT i;
HCURSOR hCursor;
DWORD dwRet;
DWORD dwFlags = CI_ACTIVATE | CI_SET_DOS_FULLSCRN;
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
ShowCursor(TRUE);
if (!DragQueryPoint(hDrop, &pt) ||
DragQueryFile(hDrop, (UINT)-1, NULL, 0) != 1) {
lppt = NULL;
}
else {
pt.x -= (GetSystemMetrics(SM_CXICON) / 2) + 2;
pt.y -= (GetSystemMetrics(SM_CYICON) / 2) + 2;
lppt = &pt;
}
for (i=0; DragQueryFile(hDrop, i, szPathField, MAXITEMPATHLEN); i++) {
// if filename or directory have spaces, put the path
// between quotes.
CheckEscapes(szPathField, MAXITEMPATHLEN+1);
/* Verify the file's existance... */
dwRet = ValidatePath(hwndProgman, szPathField, NULL, szIconPath);
if (dwRet == PATH_INVALID) {
continue;
}
else if (dwRet == PATH_INVALID_OK) {
dwFlags |= CI_NO_ASSOCIATION;
}
BuildDescription(szNameField,szPathField);
GetDirectoryFromPath(szPathField, szDirField);
if (!InQuotes(szDirField)) {
CheckEscapes(szDirField, MAXITEMPATHLEN+1);
}
HandleDosApps(szIconPath);
if (!CreateNewItem(hwnd,
szNameField, /* name*/
szPathField, /* command*/
szIconPath , /* icon path*/
szDirField, /* no default dir*/
0,0, /* no hotkey, no min on run*/
0,0,0, /* default icon*/
lppt, /* at this point*/
dwFlags))
break;
}
DragFinish(hDrop);
ShowCursor(FALSE);
SetCursor(hCursor);
if ((bAutoArrange) && (!bAutoArranging))
ArrangeItems(hwnd);
else if (!bAutoArranging)
CalcGroupScrolls(hwnd);
return 1L;
}
LRESULT APIENTRY GroupWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
POINT pt;
switch (uiMsg) {
case WM_CREATE:
{
LPMDICREATESTRUCT lpmdics;
lpmdics = (LPMDICREATESTRUCT)(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_PGROUP, lpmdics->lParam);
DragAcceptFiles(hwnd,TRUE);
SetFocus(hwnd);
break;
}
case WM_ERASEBKGND:
if (IsIconic(hwnd)) {
// Erase background with the APPWORKSPACE color
RECT rc;
if (!hbrWorkspace)
hbrWorkspace = CreateSolidBrush(GetSysColor(COLOR_APPWORKSPACE));
GetUpdateRect(hwnd, &rc, FALSE);
if (IsRectEmpty(&rc)) {
GetClientRect(hwnd, &rc);
}
FillRect((HDC)wParam, &rc, hbrWorkspace);
}
else {
goto DefProc;
}
break;
case WM_PAINT:
if (IsIconic(hwnd)) {
DrawGroupIcon(hwnd);
}
else {
PaintGroup(hwnd);
}
break;
case WM_QUERYDRAGICON:
{
PGROUP pGroup;
HICON hIcon = NULL;
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
if (pGroup->fCommon) {
hIcon = LoadIcon(hAppInstance, (LPTSTR) MAKEINTRESOURCE(COMMGROUPICON));
}
else {
hIcon = LoadIcon(hAppInstance, (LPTSTR) MAKEINTRESOURCE(PERSGROUPICON));
}
if (hIcon) {
return((LRESULT)hIcon);
}
else {
goto DefProc;
}
break;
}
case WM_LBUTTONDOWN:
ClickOn(hwnd, MAKEPOINTS(lParam));
break;
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON) {
pt.x = (int)(MAKEPOINTS(lParam).x);
pt.y = (int)(MAKEPOINTS(lParam).y);
if (!IsRectEmpty(&rcDrag) && !PtInRect(&rcDrag, pt)
&& !fNoFileMenu && (dwEditLevel < 2)) {
SetRect(&rcDrag,0,0,0,0);
DragItem(hwnd);
}
}
else {
SetRect(&rcDrag,0,0,0,0);
}
break;
case WM_LBUTTONUP:
SetRect(&rcDrag,0,0,0,0);
break;
case WM_NCLBUTTONDBLCLK:
if (IsIconic(hwnd) && (GetKeyState(VK_MENU) < 0)) {
PostMessage(hwndProgman, WM_COMMAND, IDM_PROPS, 0L);
} else {
goto DefProc;
}
break;
case WM_LBUTTONDBLCLK:
if (ItemHitTest((PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP),
MAKEPOINTS(lParam))) {
if (GetKeyState(VK_MENU) < 0) {
if (!fNoFileMenu)
PostMessage(hwndProgman,WM_COMMAND,IDM_PROPS,0L);
} else {
PostMessage(hwndProgman,WM_COMMAND,IDM_OPEN,0L);
}
} else {
/*
* Check for Alt-dblclk on nothing to get new item.
*/
if (GetKeyState(VK_MENU) < 0 && !fNoFileMenu &&
(dwEditLevel <= 1) && !(pCurrentGroup->fRO) ) {
MyDialogBox(ITEMDLG, hwndProgman, NewItemDlgProc);
}
}
break;
case WM_VSCROLL:
case WM_HSCROLL:
ScrollMessage(hwnd,uiMsg,wParam,lParam);
break;
case WM_CLOSE:
SendMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0L);
break;
case WM_SYSCOMMAND:
{
PGROUP pGroup;
LPGROUPDEF lpgd;
TCHAR szCommonGroupSuffix[MAXKEYLEN];
TCHAR szCommonGroupTitle[2*MAXKEYLEN];
if (wParam == SC_MINIMIZE) {
// if the group is common remove the common suffix from the group
// window title
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
if (pGroup->fCommon) {
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
SetWindowText(hwnd, (LPTSTR) PTR(lpgd, lpgd->pName));
GlobalUnlock(pGroup->hGroup);
}
}
if (wParam == SC_RESTORE) {
if (!LockGroup(hwnd)) {
if (wLockError == LOCK_LOWMEM) {
MyMessageBox(hwndProgman, IDS_GROUPFILEERR, IDS_LOWMEM, NULL, MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
break;
}
else {
/*
* Lock failed for some other reason - hopefully just
* a group change. Stop the icon group from being
* restored.
*/
break;
}
}
else {
UnlockGroup(hwnd);
}
}
if ((wParam == SC_MAXIMIZE) || (wParam == SC_RESTORE)) {
// if the group is common add the common suffix to the group
// window title
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
if (pGroup->fCommon) {
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
if (!lpgd)
goto DefProc;
lstrcpy(szCommonGroupTitle, (LPTSTR) PTR(lpgd, lpgd->pName));
GlobalUnlock(pGroup->hGroup);
if (LoadString(hAppInstance, IDS_COMMONGRPSUFFIX, szCommonGroupSuffix,
CharSizeOf(szCommonGroupSuffix))) {
lstrcat(szCommonGroupTitle, szCommonGroupSuffix);
}
SetWindowText(pGroup->hwnd, szCommonGroupTitle);
}
InvalidateRect(hwnd, NULL, 0);
}
if (wParam == SC_MAXIMIZE)
SetWindowLong(hwnd, GWL_STYLE,
(GetWindowLong(hwnd,GWL_STYLE) & ~(WS_HSCROLL | WS_VSCROLL)));
goto DefProc;
}
case WM_SYSKEYDOWN:
if (!CheckHotKey(wParam,lParam))
goto DefProc;
break;
case WM_KEYDOWN:
if (!CheckHotKey(wParam,lParam))
KeyWindow(hwnd,(WORD)wParam);
break;
//IME Support
//by yutakas 1992.10.22
// When user input DBCS, go and activate icon which has that
// DBCS charcter in the first of description.
case WM_IME_REPORT:
switch (wParam)
{
case IR_STRING:
IMEStringWindow(hwnd,(HANDLE)lParam);
return TRUE;
default:
goto DefProc;
}
break;
case WM_CHAR:
CharWindow(hwnd, (WORD) wParam);
break;
case WM_QUERYDROPOBJECT:
{
#define lpds ((LPDROPSTRUCT)lParam)
PGROUP pGroup;
pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
if (pGroup->fRO) {
return FALSE;
}
if (lpds->wFmt == OBJ_ITEM) {
return TRUE;
}
#undef lpds
goto DefProc;
}
case WM_DROPOBJECT:
#define lpds ((LPDROPSTRUCT)lParam)
if (lpds->wFmt == OBJ_ITEM)
return DropObject(hwnd, lpds);
#undef lpds
goto DefProc;
case WM_DROPFILES:
return DropFiles(hwnd,(HANDLE)wParam);
case WM_NCACTIVATE:
{
PGROUP pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
if (pGroup->pItems != NULL) {
InvalidateRect(hwnd,&pGroup->pItems->rcTitle,TRUE);
}
goto DefProc;
}
case WM_QUERYOPEN:
{
PGROUP pGroup;
LPGROUPDEF lpgd;
TCHAR szCommonGroupSuffix[MAXKEYLEN];
TCHAR szCommonGroupTitle[2*MAXKEYLEN];
// if the group is common add the common suffix to the group
// window title
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
if (pGroup->fCommon) {
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
if (!lpgd)
goto DefProc;
lstrcpy(szCommonGroupTitle,(LPTSTR) PTR(lpgd, lpgd->pName));
GlobalUnlock(pGroup->hGroup);
if (LoadString(hAppInstance, IDS_COMMONGRPSUFFIX, szCommonGroupSuffix,
CharSizeOf(szCommonGroupSuffix))) {
lstrcat(szCommonGroupTitle, szCommonGroupSuffix);
}
SetWindowText(pGroup->hwnd, szCommonGroupTitle);
}
goto DefProc;
}
case WM_SIZE:
lParam = DefMDIChildProc(hwnd, uiMsg, wParam, lParam);
if (wParam != SIZEICONIC) {
if ((bAutoArrange) && (!bAutoArranging)) {
ArrangeItems(hwnd);
} else if (!bArranging) {
CalcGroupScrolls(hwnd);
}
}
else {
PGROUP pGroup;
LPGROUPDEF lpgd;
// reset window text of common groups
pGroup = (PGROUP)GetWindowLongPtr(hwnd, GWLP_PGROUP);
if (pGroup->fCommon) {
lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup);
SetWindowText(pGroup->hwnd, (LPTSTR) PTR(lpgd, lpgd->pName));
GlobalUnlock(pGroup->hGroup);
}
}
return lParam;
case WM_MDIACTIVATE:
if (!pCurrentGroup) {
goto DefProc;
}
/*
* If we are de-activating this window...
*/
if (lParam == 0) {
/*
* We're the last window... punt.
*/
pCurrentGroup = NULL;
} else if (hwnd == (HWND)wParam) {
/*
* We're being deactivated. Update pCurrentGroup
* to the node being activated.
*/
pCurrentGroup = (PGROUP)GetWindowLongPtr((HWND)lParam, GWLP_PGROUP);
} else {
SetFocus(hwnd);
}
goto DefProc;
case WM_MENUSELECT:
// to handle F1 on group window system menu
if (lParam) { /*make sure menu handle isn't null*/
wMenuID = GET_WM_COMMAND_ID(wParam, lParam); /*get cmd from loword of wParam*/
hSaveMenuHandle = (HANDLE)lParam; /*Save hMenu into one variable*/
wSaveFlags = HIWORD(wParam);/*Save flags into another*/
bFrameSysMenu = FALSE;
}
break;
default:
DefProc:
return DefMDIChildProc(hwnd, uiMsg, wParam, lParam);
}
return 0L;
}
/* ArrangeItems() - */
/* Arranges iconic windows within a group. */
VOID FAR PASCAL ArrangeItems(HWND hwnd)
{
PGROUP pGroup;
register PITEM pItem;
PITEM pItemT;
int xSlots;
register int i;
int j,k;
RECT rc;
LPGROUPDEF lpgd;
PITEM rgpitem[CITEMSMAX];
POINT pt;
int t1, t2;
LONG style;
if (bAutoArranging || IsIconic(hwnd))
return;
pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
if (!pGroup)
return;
/*
* If the group is RO then don't rearrange the items, just update the
* scroll bars
*/
if (!GroupCheck(pGroup)) {
CalcGroupScrolls(hwnd);
return;
}
bAutoArranging = TRUE;
SetRectEmpty(&rcArrangeRect);
style = GetWindowLong(hwnd,GWL_STYLE);
GetRealClientRect(hwnd,style&~(WS_VSCROLL|WS_HSCROLL),&rc);
SetWindowLong(hwnd,GWL_STYLE,style);
xSlots = (rc.right - rc.left)/cxArrange;
if (xSlots < 1)
xSlots = 1;
/* sort the items by x location within a row, or by row if the
* rows are different
*/
k = 0;
for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) {
/* find nearest row
*/
t1 = pItem->rcIcon.top + cyArrange/2;
if (t1 >= 0)
t1 -= t1 % cyArrange;
else
t1 += t1 % cyArrange;
for (i = 0; i < k; i++) {
pItemT = rgpitem[i];
t2 = pItemT->rcIcon.top + cyArrange/2;
if (t2 >= 0)
t2 -= t2 % cyArrange;
else
t2 += t2 % cyArrange;
if (t2 > t1)
break;
else if (t2 == t1 && pItemT->rcIcon.left > pItem->rcIcon.left)
break;
}
for (j = k; j > i; j--) {
rgpitem[j] = rgpitem[j-1];
}
rgpitem[i] = pItem;
k++;
}
lpgd = LockGroup(hwnd);
if (!lpgd) {
bAutoArranging = FALSE;
return;
}
bNoScrollCalc = TRUE;
for (i = 0; i < k; i++) {
pItem = rgpitem[i];
/* cxOffset necessary to match (buggy???) win 3 USER
*/
pt.x = (i%xSlots)*cxArrange + (cxArrange-cxIconSpace)/2 + cxOffset;
pt.y = (i/xSlots)*cyArrange;
MoveItem(pGroup,pItem,pt);
}
if (!IsRectEmpty(&rcArrangeRect))
InvalidateRect(pGroup->hwnd,&rcArrangeRect,TRUE);
UnlockGroup(hwnd);
bNoScrollCalc = FALSE;
CalcGroupScrolls(hwnd);
bAutoArranging = FALSE;
}
/*
* IMEStringWindow(hwnd,hstr)
* Change activate item by the strings come from IME.
* When Get WM_IME_REPORT with IR_STRING,this function is called.
* by yutakas 1992.10.22
*/
BOOL FAR PASCAL IMEStringWindow(HWND hwnd, HANDLE hStr)
{
LPTSTR lpStr;
LPGROUPDEF lpgd;
LPITEMDEF lpid;
PGROUP pGroup;
PITEM pItem = NULL;
PITEM pItemLast,pTItem;
int nCnt = 0;
int nTCnt = 0;
BOOL ret = FALSE;
if (!hStr)
return ret;
if (!(lpStr = GlobalLock(hStr)))
return ret;
pGroup = (PGROUP)GetWindowLongPtr(hwnd,GWLP_PGROUP);
if (!pGroup->pItems)
return ret;
lpgd = LockGroup(hwnd);
if (!lpgd)
return ret;
#ifdef _DEBUG
{
TCHAR szDev[80];
OutputDebugString((LPTSTR)TEXT("In IME Winsdow\r\n"));
wsprintf ((LPTSTR)szDev,TEXT("IMEStringWindow: lpStr is %s \r\n"),lpStr);
OutputDebugString((LPSTR)szDev);
}
#endif
// Search for item, skip the currently selected one.
for ( pTItem = pGroup->pItems->pNext; pTItem; pTItem=pTItem->pNext)
{
lpid = ITEM(lpgd,pTItem->iItem);
nTCnt = IMEWindowGetCnt(lpStr,(LPTSTR)PTR(lpgd,lpid->pName));
if (nCnt < nTCnt)
{
nCnt = nTCnt;
pItem = pTItem;
}
}
lpid = ITEM(lpgd,pGroup->pItems->iItem);
nTCnt = IMEWindowGetCnt(lpStr,(LPTSTR)PTR(lpgd,lpid->pName));
if ((nCnt >= nTCnt) && pItem)
{
pItemLast = MakeFirstItemLast(pGroup);
BringItemToTop(pGroup,pItem, FALSE);
// Handle updates.
InvalidateRect(pGroup->hwnd,&pItem->rcTitle,TRUE);
InvalidateRect(pGroup->hwnd,&pItemLast->rcTitle,TRUE);
ViewActiveItem(pGroup);
ret = TRUE;
}
GlobalUnlock(hStr);
UnlockGroup(hwnd);
#ifdef _DEBUG
{
TCHAR szDev[80];
wsprintf ((LPTSTR)szDev,TEXT("IMEStringWindow: ret is %s \r\n"),ret);
OutputDebugString((LPTSTR)szDev);
}
#endif
return ret;
}
/*
* IMEWindowGetCnt(LPSTR,LPSTR)
* Compare strings from ahead and return the number of same character
* by yutakas 1992.10.22
*/
int FAR PASCAL IMEWindowGetCnt(LPTSTR lp1, LPTSTR lp2)
{
int cnt = 0;
while (*lp1 && *lp2)
{
// ToddB: This typecasting is to prevent lp1 and lp2 from being modified
// by CharUpper'ing one char at a time instead of the whole string
if (CharUpper((LPTSTR)(DWORD)(BYTE)*lp1) ==
CharUpper((LPTSTR)(DWORD)(BYTE)*lp2))
{
cnt++;
}
else
break;
lp1++;
lp2++;
}
return (*lp1 ? 0 : cnt);
}