mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1758 lines
50 KiB
1758 lines
50 KiB
/****************************************************************************\
|
|
* Module Name: movesize.c (formerly wmmove.c)
|
|
*
|
|
* Copyright (C) 1985-1995, Microsoft Corporation.
|
|
*
|
|
* This module contains Window Moving and Sizing Routines
|
|
*
|
|
* History:
|
|
* 12-Nov-1990 MikeHar Ported from win3
|
|
* 13-Feb-1991 IanJa HWND revalidation added
|
|
\****************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#define DRAG_START 0
|
|
#define DRAG_MOVE 1
|
|
#define DRAG_END 2
|
|
|
|
#define LOSHORT(l) ((SHORT)LOWORD(l))
|
|
#define HISHORT(l) ((SHORT)HIWORD(l))
|
|
|
|
/****************************************************************************\
|
|
* These values are indexes that represent rect sides. These indexes are
|
|
* used as indexes into rgimpiwx and rgimpiwy (which are indexes into the
|
|
* the rect structure) which tell the move code where to store the new x & y
|
|
* coordinates. Notice that when two of these values that represent sides
|
|
* are added together, we get a unique list of contiguous values starting at
|
|
* 1 that represent all the ways we can size a rect. That also leaves 0 free
|
|
* a initialization value.
|
|
*
|
|
* The reason we need rgimpimpiw is for the keyboard interface - we
|
|
* incrementally decide what our 'move command' is. With the mouse interface
|
|
* we know immediately because we registered a mouse hit on the segment(s)
|
|
* we're moving.
|
|
*
|
|
* 4 5
|
|
* \ ___3___ /
|
|
* | |
|
|
* 1 2
|
|
* |_______|
|
|
* / 6 \
|
|
* 7 8
|
|
*
|
|
\****************************************************************************/
|
|
|
|
static int rgimpimpiw[] = {1, 3, 2, 6};
|
|
static int rgimpiwx[] = {0, 0, 2, -1, 0, 2, -1, 0, 2, 0};
|
|
static int rgimpiwy[] = {0, -1, -1, 1, 1, 1, 3, 3, 3, 1};
|
|
static int rgcmdmpix[] = {0, 1, 2, 0, 1, 2, 0, 1, 2, 1};
|
|
static int rgcmdmpiy[] = {0, 0, 0, 3, 3, 3, 6, 6, 6, 3};
|
|
|
|
/***************************************************************************\
|
|
* SizeRect
|
|
*
|
|
* Match corner or side (defined by cmd) to pt.
|
|
*
|
|
* History:
|
|
* 12-Nov-1990 MikeHar Ported from win3 asm code
|
|
\***************************************************************************/
|
|
|
|
BOOL SizeRect(
|
|
PMOVESIZEDATA pmsd,
|
|
DWORD pt)
|
|
{
|
|
int ax;
|
|
int dx;
|
|
int index;
|
|
int indexOpp;
|
|
PINT psideDragCursor = ((PINT)(&pmsd->rcDragCursor));
|
|
PINT psideParent = ((PINT)(&pmsd->rcParent));
|
|
|
|
|
|
/*
|
|
* DO HORIZONTAL
|
|
*/
|
|
|
|
/*
|
|
* We know what part of the rect we're moving based on
|
|
* what's in cmd. We use cmd as an index into rgimpiw? which
|
|
* tells us what part of the rect we're dragging.
|
|
*/
|
|
|
|
/*
|
|
* Get the approriate array entry.
|
|
*/
|
|
index = (int)rgimpiwx[pmsd->cmd]; // AX
|
|
|
|
/*
|
|
* Is it one of the entries we don't map (i.e. -1)?
|
|
*/
|
|
if (index < 0)
|
|
goto mrLoopBottom;
|
|
|
|
psideDragCursor[index] = LOSHORT(pt);
|
|
|
|
indexOpp = index ^ 0x2;
|
|
|
|
/*
|
|
* Now check to see if we're below the min or above the max. Get the width
|
|
* of the rect in this direction (either x or y) and see if it's bad. If
|
|
* so, map the side we're moving to the min or max.
|
|
*/
|
|
ax = psideDragCursor[index] - psideDragCursor[indexOpp];
|
|
|
|
if (indexOpp & 0x2)
|
|
ax = -ax;
|
|
|
|
if ((ax >= (dx = pmsd->ptMinTrack.x)) &&
|
|
(ax <= (dx = pmsd->ptMaxTrack.x))) {
|
|
|
|
/*
|
|
* Only test for the parent's client boundary if we are a child
|
|
* window...Otherwise we are bound to the client of the desktop
|
|
* which causes strange drag problems.
|
|
*/
|
|
if (!TestWF(pmsd->spwnd,WFCHILD))
|
|
goto mrLoopBottom;
|
|
|
|
/*
|
|
* Now see if we're extending beyond our parent's client rect.
|
|
* Compute the size the rect can be expanded to in this direction.
|
|
*/
|
|
dx = abs(psideParent[index] - psideDragCursor[indexOpp]);
|
|
|
|
if (ax <= dx)
|
|
goto mrLoopBottom;
|
|
|
|
/*
|
|
* The width is invalid - map the side we're moving to the other
|
|
* side +/- the width.
|
|
*/
|
|
}
|
|
|
|
if (indexOpp & 0x2)
|
|
dx = -dx;
|
|
|
|
psideDragCursor[index] = dx + psideDragCursor[indexOpp];
|
|
|
|
mrLoopBottom:
|
|
|
|
/*
|
|
* DO VERTICAL
|
|
*/
|
|
|
|
/*
|
|
* We know what part of the rect we're moving based on
|
|
* what's in cmd. We use cmd as an index into rgimpiw? which
|
|
* tells us what part of the rect we're dragging.
|
|
*/
|
|
|
|
/*
|
|
* Get the approriate array entry.
|
|
*/
|
|
index = (int)rgimpiwy[pmsd->cmd]; // AX
|
|
|
|
/*
|
|
* Is it one of the entries we don't map (i.e. -1)?
|
|
*/
|
|
if (index < 0)
|
|
return TRUE;
|
|
|
|
psideDragCursor[index] = HISHORT(pt);
|
|
|
|
indexOpp = index ^ 0x2;
|
|
|
|
/*
|
|
* Now check to see if we're below the min or above the max. Get the width
|
|
* of the rect in this direction (either x or y) and see if it's bad. If
|
|
* so, map the side we're moving to the min or max.
|
|
*/
|
|
ax = psideDragCursor[index] - psideDragCursor[indexOpp];
|
|
|
|
if (indexOpp & 0x2)
|
|
ax = -ax;
|
|
|
|
if ((ax >= (dx = pmsd->ptMinTrack.y)) &&
|
|
(ax <= (dx = pmsd->ptMaxTrack.y))) {
|
|
|
|
/*
|
|
* Only test for the parent's client boundary if we are a child
|
|
* window...Otherwise we are bound to the client of the desktop
|
|
* which causes strange drag problems.
|
|
*/
|
|
if (!TestWF(pmsd->spwnd,WFCHILD))
|
|
return TRUE;
|
|
|
|
/*
|
|
* Now see if we're extending beyond our parent's client rect.
|
|
* Compute the size the rect can be expanded to in this direction.
|
|
*/
|
|
dx = abs(psideParent[index] - psideDragCursor[indexOpp]);
|
|
|
|
if (ax <= dx)
|
|
return TRUE;
|
|
|
|
/*
|
|
* The width is invalid - map the side we're moving to the other
|
|
* side +/- the width.
|
|
*/
|
|
}
|
|
|
|
if (indexOpp & 0x2)
|
|
dx = -dx;
|
|
|
|
psideDragCursor[index] = dx + psideDragCursor[indexOpp];
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MoveRect
|
|
*
|
|
* Move the rect to pt, make sure we're not going out of the parent rect.
|
|
*
|
|
* History:
|
|
* 12-Nov-1990 MikeHar Ported from win3 asm code
|
|
\***************************************************************************/
|
|
|
|
BOOL MoveRect(
|
|
PMOVESIZEDATA pmsd,
|
|
DWORD pt)
|
|
{
|
|
RECT rcAnd;
|
|
|
|
OffsetRect(&pmsd->rcDragCursor,
|
|
LOSHORT(pt) - pmsd->rcDragCursor.left,
|
|
HISHORT(pt) - pmsd->rcDragCursor.top);
|
|
|
|
/*
|
|
* Don't move the entire rectangle off the screen.
|
|
* However, if the window started offscreen completely, let it move.
|
|
*/
|
|
if (!pmsd->fOffScreen) {
|
|
return IntersectRect(&rcAnd, &pmsd->rcDragCursor, &pmsd->rcParent);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxTM_MoveDragRect
|
|
*
|
|
* History:
|
|
* 12-Nov-1990 MikeHar Ported from win3
|
|
\***************************************************************************/
|
|
|
|
VOID xxxTM_MoveDragRect(
|
|
PMOVESIZEDATA pmsd,
|
|
DWORD lParam)
|
|
{
|
|
UINT msg;
|
|
RECT rc;
|
|
|
|
UserAssert(pmsd == PtiCurrent()->pmsd);
|
|
UserAssert(pmsd->cmd != WMSZ_KEYSIZE);
|
|
|
|
CopyRect(&pmsd->rcDragCursor, &pmsd->rcDrag);
|
|
|
|
if (pmsd->cmd == WMSZ_MOVE) {
|
|
|
|
if (!MoveRect(pmsd, lParam))
|
|
return;
|
|
|
|
msg = WM_MOVING;
|
|
|
|
} else {
|
|
|
|
if (!SizeRect(pmsd, lParam))
|
|
return;
|
|
|
|
msg = WM_SIZING;
|
|
}
|
|
|
|
CopyRect(&rc, &pmsd->rcDragCursor);
|
|
xxxSendMessage(pmsd->spwnd, msg, pmsd->cmd, (LPARAM)(LPRECT)&rc);
|
|
xxxDrawDragRect(pmsd, &rc, DRAG_MOVE);
|
|
|
|
if (pmsd->cmd == WMSZ_MOVE) {
|
|
|
|
/*
|
|
* Keep dxMouse & dxMouse relative to the offset from the top left
|
|
* corner, the rectangle could've changed on WM_MOVING
|
|
*/
|
|
pmsd->dxMouse += (rc.left - LOSHORT(lParam));
|
|
pmsd->dyMouse += (rc.top - HISHORT(lParam));
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CkptRestore
|
|
*
|
|
*
|
|
* History:
|
|
* 14-Nov-1990 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
PCHECKPOINT CkptRestore(
|
|
PWND pwnd,
|
|
RECT rcWindow)
|
|
{
|
|
PCHECKPOINT pcp;
|
|
|
|
/*
|
|
* Don't return or create a checkpoint if the window is dying.
|
|
*/
|
|
if (HMIsMarkDestroy(pwnd))
|
|
return NULL;
|
|
|
|
/*
|
|
* If it doesn't exist, create it.
|
|
*/
|
|
if ((pcp = (PCHECKPOINT)_GetProp(pwnd,
|
|
PROP_CHECKPOINT,
|
|
PROPF_INTERNAL)) == NULL) {
|
|
|
|
if ((pcp = (PCHECKPOINT)UserAllocPoolWithQuota(sizeof(CHECKPOINT),
|
|
TAG_CHECKPT)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!InternalSetProp(pwnd,
|
|
PROP_CHECKPOINT,
|
|
(HANDLE)pcp,
|
|
PROPF_INTERNAL)) {
|
|
|
|
UserFreePool(pcp);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Initialize it to -1 so first minimize will park the icon.
|
|
*/
|
|
pcp->ptMin.x = -1;
|
|
pcp->ptMin.y = -1;
|
|
pcp->ptMax.x = -1;
|
|
pcp->ptMax.y = -1;
|
|
|
|
/*
|
|
* Initialize pwndTitle to NULL so we create a title window on the
|
|
* first minimize of the window
|
|
*/
|
|
pcp->fDragged = FALSE;
|
|
pcp->fWasMaximizedBeforeMinimized = FALSE;
|
|
pcp->fWasMinimizedBeforeMaximized = FALSE;
|
|
pcp->fMinInitialized = FALSE;
|
|
pcp->fMaxInitialized = FALSE;
|
|
|
|
CopyRect(&pcp->rcNormal, &rcWindow);
|
|
}
|
|
|
|
/*
|
|
* If the window is minimized/maximized, then set the min/max
|
|
* point. Otherwise use checkpoint the window-size.
|
|
*/
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
pcp->ptMin.x = rcWindow.left;
|
|
pcp->ptMin.y = rcWindow.top;
|
|
} else if (TestWF(pwnd, WFMAXIMIZED)) {
|
|
pcp->ptMax.x = rcWindow.left;
|
|
pcp->ptMax.y = rcWindow.top;
|
|
} else {
|
|
CopyRect(&pcp->rcNormal, &rcWindow);
|
|
}
|
|
|
|
return pcp;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMS_TrackMove
|
|
*
|
|
* History:
|
|
* 12-Nov-1990 MikeHar Ported from win3
|
|
\***************************************************************************/
|
|
|
|
void xxxMS_TrackMove(
|
|
PWND pwnd,
|
|
UINT message,
|
|
DWORD wParam,
|
|
DWORD lParam,
|
|
PMOVESIZEDATA pmsd,
|
|
LPRECT lpRect)
|
|
{
|
|
int dxMove;
|
|
int dyMove;
|
|
POINT pt;
|
|
BOOL fSlower;
|
|
RECT rc;
|
|
PCHECKPOINT pcp;
|
|
LPWORD ps;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
CheckLock(pwnd);
|
|
UserAssert(pmsd == ptiCurrent->pmsd);
|
|
|
|
pt.x = LOSHORT(lParam);
|
|
pt.y = HISHORT(lParam);
|
|
|
|
switch (message) {
|
|
case WM_LBUTTONUP:
|
|
|
|
/*
|
|
* Do final move!
|
|
*/
|
|
xxxTM_MoveDragRect(pmsd, lParam);
|
|
|
|
|
|
/*
|
|
* Don't reset the mouse position when done.
|
|
*/
|
|
pmsd->fmsKbd = FALSE;
|
|
|
|
Accept:
|
|
|
|
/*
|
|
* Turn off rect, unlock screen, release capture, and stop tracking.
|
|
* 1 specifies end and accept drag.
|
|
*/
|
|
bSetDevDragRect(gpDispInfo->hDev, NULL, NULL);
|
|
if (ptiCurrent->TIF_flags & TIF_TRACKRECTVISIBLE) {
|
|
xxxDrawDragRect(pmsd, NULL, DDR_ENDACCEPT);
|
|
ptiCurrent->TIF_flags &= ~TIF_TRACKRECTVISIBLE;
|
|
}
|
|
|
|
TrackMoveCancel:
|
|
|
|
/*
|
|
* Revalidation: if pwnd is unexpectedly deleted, jump here to cleanup.
|
|
* If pwnd is/becomes invalid between here and return, continue with
|
|
* cleanup as best as possible.
|
|
*/
|
|
_ClipCursor((LPRECT)NULL);
|
|
xxxLockWindowUpdate2(NULL, FALSE);
|
|
xxxReleaseCapture();
|
|
|
|
/*
|
|
* First unlock task and reset cursor.
|
|
*/
|
|
pmsd->fTrackCancelled = TRUE;
|
|
|
|
/*
|
|
* If using the keyboard, restore the initial mouse position.
|
|
*/
|
|
if (pmsd->fmsKbd) {
|
|
InternalSetCursorPos(pmsd->ptRestore.x,
|
|
pmsd->ptRestore.y,
|
|
grpdeskRitInput);
|
|
}
|
|
|
|
/*
|
|
* Move to new location relative to parent.
|
|
*/
|
|
if (pwnd->spwndParent != NULL) {
|
|
CopyRect(&rc, &(pwnd->spwndParent->rcClient));
|
|
} else {
|
|
SetRectEmpty(&rc);
|
|
}
|
|
|
|
if (!EqualRect(&pmsd->rcDrag, &pmsd->rcWindow)) {
|
|
|
|
if (!xxxCallHook(HCBT_MOVESIZE,
|
|
(DWORD)HWq(pwnd),
|
|
(DWORD)&pmsd->rcDrag,
|
|
WH_CBT)) {
|
|
|
|
RECT rcT;
|
|
|
|
if (pmsd->cmd != WMSZ_MOVE) {
|
|
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
|
|
CopyOffsetRect(&rcT,
|
|
&pmsd->rcWindow,
|
|
-rc.left,
|
|
-rc.top);
|
|
|
|
/*
|
|
* Save the minimized position.
|
|
*/
|
|
CkptRestore(pwnd, rcT);
|
|
ClrWF(pwnd, WFMINIMIZED);
|
|
|
|
} else if (TestWF(pwnd, WFMAXIMIZED)) {
|
|
ClrWF(pwnd, WFMAXIMIZED);
|
|
}
|
|
|
|
} else if (TestWF(pwnd, WFMINIMIZED)) {
|
|
|
|
CopyOffsetRect(&rcT,
|
|
&pmsd->rcWindow,
|
|
-rc.left,
|
|
-rc.top);
|
|
|
|
|
|
if (pcp = CkptRestore(pwnd, rcT))
|
|
pcp->fDragged = TRUE;
|
|
}
|
|
|
|
} else {
|
|
CopyRect(&pmsd->rcDrag, &pmsd->rcWindow);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Move to new location relative to parent.
|
|
*/
|
|
OffsetRect(&pmsd->rcDrag, -rc.left, -rc.top);
|
|
|
|
/*
|
|
* For top level windows, make sure at least part of the caption
|
|
* caption is always visible in the desktop area. This will
|
|
* ensure that once moved, the window can be moved back.
|
|
*/
|
|
if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
|
|
int dy = (TestWF(pwnd, WEFTOOLWINDOW)) ? SYSMET(CYSMCAPTION)
|
|
: SYSMET(CYCAPTION);
|
|
|
|
/*
|
|
* Topmost windows aren't clipped to the working area
|
|
* in the same way that most windows are.
|
|
*/
|
|
if (TestWF(pwnd, WEFTOPMOST) || TestWF(pwnd, WEFTOOLWINDOW)) {
|
|
pmsd->rcDrag.top = max(pmsd->rcDrag.top,
|
|
gpDispInfo->rcScreen.top + SYSMET(CYBORDER) - dy);
|
|
} else {
|
|
pmsd->rcDrag.top = max(pmsd->rcDrag.top,
|
|
gpsi->rcWork.top + SYSMET(CYBORDER) - dy);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* OR in SWP_NOSIZE so it doesn't redraw if we're just moving.
|
|
*/
|
|
xxxSetWindowPos(
|
|
pwnd,
|
|
NULL,
|
|
pmsd->rcDrag.left,
|
|
pmsd->rcDrag.top,
|
|
pmsd->rcDrag.right - pmsd->rcDrag.left,
|
|
pmsd->rcDrag.bottom - pmsd->rcDrag.top,
|
|
(DWORD)((pmsd->cmd == (int)WMSZ_MOVE) ? SWP_NOSIZE : 0));
|
|
|
|
if (TestWF(pwnd, WFMINIMIZED))
|
|
CkptRestore(pwnd, pmsd->rcDrag);
|
|
|
|
/*
|
|
* Send this message for winoldapp support
|
|
*/
|
|
xxxSendMessage(pwnd, WM_EXITSIZEMOVE, 0L, 0L);
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
case WM_SYSKEYDOWN:
|
|
|
|
/*
|
|
* Assume we're not moving the drag rectangle.
|
|
*/
|
|
dxMove =
|
|
dyMove = 0;
|
|
|
|
/*
|
|
* We move or size slower if the control key is down.
|
|
*/
|
|
fSlower = (_GetKeyState(VK_CONTROL) < 0);
|
|
|
|
switch (wParam) {
|
|
case VK_RETURN:
|
|
lParam = _GetMessagePos();
|
|
goto Accept;
|
|
|
|
case VK_ESCAPE:
|
|
|
|
/*
|
|
* 2 specifies end and cancel drag.
|
|
*/
|
|
bSetDevDragRect(gpDispInfo->hDev, NULL, NULL);
|
|
if (ptiCurrent->TIF_flags & TIF_TRACKRECTVISIBLE) {
|
|
xxxDrawDragRect(pmsd, NULL, DDR_ENDCANCEL);
|
|
ptiCurrent->TIF_flags &= ~TIF_TRACKRECTVISIBLE;
|
|
}
|
|
|
|
CopyRect(&pmsd->rcDrag, &pmsd->rcWindow);
|
|
|
|
goto TrackMoveCancel;
|
|
|
|
case VK_LEFT:
|
|
case VK_RIGHT:
|
|
|
|
if (pmsd->impx == 0) {
|
|
|
|
pmsd->impx = rgimpimpiw[wParam - VK_LEFT];
|
|
goto NoOffset;
|
|
|
|
} else {
|
|
|
|
dxMove = (fSlower ? 1 : max(SYSMET(CXSIZE) / 2, 1));
|
|
|
|
if (wParam == VK_LEFT)
|
|
dxMove = -dxMove;
|
|
|
|
goto KeyMove;
|
|
}
|
|
|
|
case VK_UP:
|
|
case VK_DOWN:
|
|
|
|
if (pmsd->impy == 0) {
|
|
|
|
pmsd->impy = rgimpimpiw[wParam - VK_LEFT];
|
|
NoOffset:
|
|
pmsd->dxMouse = pmsd->dyMouse = 0;
|
|
|
|
} else {
|
|
|
|
dyMove = (fSlower ? 1 : max(SYSMET(CYSIZE) / 2, 1));
|
|
|
|
if (wParam == VK_UP) {
|
|
dyMove = -dyMove;
|
|
}
|
|
}
|
|
|
|
KeyMove:
|
|
if (pmsd->cmd == WMSZ_MOVE) {
|
|
|
|
/*
|
|
* Use the current rect position as the current mouse
|
|
* position
|
|
*/
|
|
lParam = (DWORD)(POINTTOPOINTS(*((POINT *)&pmsd->rcDrag)));
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Get the current mouse position
|
|
*/
|
|
lParam = _GetMessagePos();
|
|
}
|
|
|
|
/*
|
|
* Calc the new 'mouse' pos
|
|
*/
|
|
if (pmsd->impx != 0) {
|
|
ps = ((WORD *)(&lParam)) + 0;
|
|
*ps = (WORD)(*((int *)&pmsd->rcDragCursor +
|
|
rgimpiwx[pmsd->impx]) +
|
|
dxMove);
|
|
}
|
|
|
|
if (pmsd->impy != 0) {
|
|
ps = ((WORD *)(&lParam)) + 1;
|
|
*ps = (WORD)(*((int *)&pmsd->rcDragCursor +
|
|
rgimpiwy[pmsd->impy]) +
|
|
dyMove);
|
|
}
|
|
|
|
if (pmsd->cmd != WMSZ_MOVE) {
|
|
|
|
/*
|
|
* Calculate the new move command.
|
|
*/
|
|
pmsd->cmd = pmsd->impx + pmsd->impy;
|
|
|
|
/*
|
|
* Change the mouse cursor for this condition.
|
|
*/
|
|
xxxSendMessage(
|
|
pwnd,
|
|
WM_SETCURSOR,
|
|
(DWORD)HW(pwnd),
|
|
MAKELONG((SHORT)(pmsd->cmd + HTSIZEFIRST - WMSZ_SIZEFIRST), WM_MOUSEMOVE));
|
|
}
|
|
|
|
/*
|
|
* We don't want to call InternalSetCursorPos() if the
|
|
* rect position is outside of rcParent because that'll
|
|
* generate a mouse move which will jerk the rect back
|
|
* again. This is here so we can move rects partially off
|
|
* screen without regard to the mouse position.
|
|
*/
|
|
pt.x = LOSHORT(lParam) - pmsd->dxMouse;
|
|
pt.y = HISHORT(lParam) - pmsd->dyMouse;
|
|
|
|
if (PtInRect(&pmsd->rcParent, pt)) {
|
|
InternalSetCursorPos(pt.x, pt.y, grpdeskRitInput);
|
|
}
|
|
|
|
/*
|
|
* Move or size the rect using lParam as our mouse
|
|
* coordinates
|
|
*/
|
|
xxxTM_MoveDragRect(pmsd, lParam);
|
|
break;
|
|
|
|
} // of inner switch
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
xxxTM_MoveDragRect(pmsd, lParam);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMS_FlushWigglies
|
|
*
|
|
* History:
|
|
* 12-Nov-1990 MikeHar Ported from win3
|
|
\***************************************************************************/
|
|
|
|
VOID xxxMS_FlushWigglies(VOID)
|
|
{
|
|
MSG msg;
|
|
|
|
/*
|
|
* HACK!
|
|
*
|
|
* Calling InternalSetCursorPos() while initializing the cursor
|
|
* position appears to be posting a bogus MouseMove
|
|
* message... don't really have the time
|
|
* now to figure out why... so spit out all the mouse move messages
|
|
* before entering the main move/size loop. CraigC.
|
|
*/
|
|
while (xxxPeekMessage(&msg,
|
|
NULL,
|
|
WM_MOUSEMOVE,
|
|
WM_MOUSEMOVE,
|
|
PM_REMOVE | PM_NOYIELD));
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxTrackInitSize
|
|
*
|
|
* NOTE: to recover from hwnd invalidation, just return and let ?
|
|
*
|
|
* History:
|
|
* 12-Nov-1990 MikeHar Ported from win3
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxTrackInitSize(
|
|
PWND pwnd,
|
|
UINT message,
|
|
DWORD wParam,
|
|
LONG lParam,
|
|
PMOVESIZEDATA pmsd)
|
|
{
|
|
int ht;
|
|
POINT pt;
|
|
RECT rc;
|
|
|
|
CheckLock(pwnd);
|
|
UserAssert(pmsd == PtiCurrent()->pmsd);
|
|
|
|
POINTSTOPOINT(pt, lParam);
|
|
|
|
_ClientToScreen(pwnd, (LPPOINT)&pt);
|
|
ht = FindNCHit(pwnd, POINTTOPOINTS(pt));
|
|
|
|
switch (message) {
|
|
|
|
case WM_KEYDOWN:
|
|
if (pmsd->cmd == WMSZ_MOVE) {
|
|
xxxSendMessage(pwnd,
|
|
WM_SETCURSOR,
|
|
(DWORD)HW(pwnd),
|
|
MAKELONG(WMSZ_KEYSIZE, WM_MOUSEMOVE));
|
|
}
|
|
pmsd->fInitSize = FALSE;
|
|
return TRUE;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
if (!PtInRect(&pmsd->rcDrag, pt)) {
|
|
|
|
/*
|
|
*** FALL THRU ***
|
|
*/
|
|
|
|
case WM_LBUTTONUP:
|
|
|
|
/*
|
|
* Cancel everything.
|
|
*/
|
|
{
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
bSetDevDragRect(gpDispInfo->hDev, NULL, NULL);
|
|
if (ptiCurrent->TIF_flags & TIF_TRACKRECTVISIBLE) {
|
|
xxxDrawDragRect(pmsd, NULL, DDR_ENDCANCEL);
|
|
ptiCurrent->TIF_flags &= ~TIF_TRACKRECTVISIBLE;
|
|
}
|
|
|
|
pmsd->fInitSize = FALSE;
|
|
_ClipCursor(NULL);
|
|
}
|
|
|
|
xxxReleaseCapture();
|
|
pmsd->fTrackCancelled = TRUE;
|
|
return FALSE;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Now start hit testing for a border.
|
|
*/
|
|
goto CheckFrame;
|
|
}
|
|
|
|
case WM_MOUSEMOVE:
|
|
|
|
/*
|
|
* The mouse is down, hit test for a border on mouse moves.
|
|
*/
|
|
if (wParam == MK_LBUTTON) {
|
|
|
|
CheckFrame:
|
|
|
|
switch (pmsd->cmd) {
|
|
case WMSZ_MOVE:
|
|
|
|
/*
|
|
* If we are on the caption bar, exit.
|
|
*/
|
|
if (ht == HTCAPTION) {
|
|
|
|
/*
|
|
* Change the mouse cursor.
|
|
*/
|
|
xxxSendMessage(pwnd,
|
|
WM_SETCURSOR,
|
|
(DWORD)HW(pwnd),
|
|
MAKELONG(WMSZ_KEYSIZE, WM_MOUSEMOVE));
|
|
|
|
pmsd->dxMouse = pmsd->rcWindow.left - pt.x;
|
|
pmsd->dyMouse = pmsd->rcWindow.top - pt.y;
|
|
pmsd->fInitSize = FALSE;
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WMSZ_KEYSIZE:
|
|
|
|
/*
|
|
* If we are on a frame control, change the cursor and exit.
|
|
*/
|
|
if (ht >= HTSIZEFIRST && ht <= HTSIZELAST) {
|
|
|
|
/*
|
|
* Change the mouse cursor
|
|
*/
|
|
xxxSendMessage(pwnd,
|
|
WM_SETCURSOR,
|
|
(DWORD)HW(pwnd),
|
|
MAKELONG(ht, WM_MOUSEMOVE));
|
|
|
|
pmsd->fInitSize = FALSE;
|
|
|
|
/*
|
|
* Set the proper cmd for SizeRect().
|
|
*
|
|
* HACK! Depends on order of HTSIZE* defines!
|
|
*/
|
|
pmsd->impx = rgcmdmpix[ht - HTSIZEFIRST + 1];
|
|
pmsd->impy = rgcmdmpiy[ht - HTSIZEFIRST + 1];
|
|
pmsd->cmd = pmsd->impx + pmsd->impy;
|
|
|
|
pmsd->dxMouse = *((UINT FAR *)&pmsd->rcWindow + rgimpiwx[pmsd->cmd]) - pt.x;
|
|
pmsd->dyMouse = *((UINT FAR *)&pmsd->rcWindow + rgimpiwy[pmsd->cmd]) - pt.y;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* If button not down, and we are moving the window, change the
|
|
* cursor shape depending upon where the mouse is pointing. This
|
|
* allows the cursor to change to the arrows when over the window
|
|
* frame.
|
|
*/
|
|
_GetWindowRect(pwnd, &rc);
|
|
if (PtInRect(&rc, pt)) {
|
|
if ((ht >= HTSIZEFIRST) && (ht <= HTSIZELAST)) {
|
|
xxxSendMessage(pwnd,
|
|
WM_SETCURSOR,
|
|
(DWORD)HW(pwnd),
|
|
MAKELONG(ht, WM_MOUSEMOVE));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
_SetCursor(SYSCUR(SIZEALL));
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMoveSize
|
|
*
|
|
* History:
|
|
* 12-Nov-1990 MikeHar Ported from win3
|
|
\***************************************************************************/
|
|
|
|
VOID xxxMoveSize(
|
|
PWND pwnd,
|
|
UINT cmdMove,
|
|
DWORD wptStart)
|
|
{
|
|
MSG msg;
|
|
int x;
|
|
int y;
|
|
int i;
|
|
RECT rcSys;
|
|
PTHREADINFO ptiCurrent;
|
|
PMOVESIZEDATA pmsd;
|
|
TL tlpwndT;
|
|
PWND pwndT;
|
|
PMENUSTATE pMenuState = GetpMenuState(pwnd); /* This can be NULL */
|
|
POINT ptStart;
|
|
|
|
|
|
CheckLock(pwnd);
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* Don't allow the app to track a window
|
|
* from another queue.
|
|
*/
|
|
if (GETPTI(pwnd)->pq != ptiCurrent->pq)
|
|
return;
|
|
|
|
if (ptiCurrent->pmsd != NULL)
|
|
return;
|
|
|
|
/*
|
|
* If the window with the focus is a combobox, hide the dropdown
|
|
* listbox before tracking starts. The dropdown listbox is not a
|
|
* child of the window being moved, therefore it won't be moved along
|
|
* with the window.
|
|
*
|
|
* NOTE: Win 3.1 doesn't perform this check.
|
|
*/
|
|
if ((pwndT = ptiCurrent->pq->spwndFocus) != NULL) {
|
|
|
|
if (GETFNID(pwndT) == FNID_COMBOBOX) {
|
|
;
|
|
} else if ((pwndT->spwndParent != NULL) &&
|
|
(GETFNID(pwndT->spwndParent) == FNID_COMBOBOX)) {
|
|
|
|
pwndT = pwndT->spwndParent;
|
|
} else {
|
|
pwndT = NULL;
|
|
}
|
|
|
|
if (pwndT != NULL) {
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwndT, &tlpwndT);
|
|
xxxSendMessage(pwndT, CB_SHOWDROPDOWN, FALSE, 0);
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Allocate and zero the movesize data structure
|
|
*/
|
|
pmsd = (PMOVESIZEDATA)UserAllocPoolWithQuota(sizeof(MOVESIZEDATA),
|
|
TAG_MOVESIZE);
|
|
if (pmsd == NULL)
|
|
return;
|
|
|
|
RtlZeroMemory(pmsd, sizeof(MOVESIZEDATA));
|
|
|
|
/*
|
|
* Assign the move data into the pti. If the thread is destroyed before
|
|
* we free the data the DestroyThreadInfo() routine will free the move data
|
|
*/
|
|
ptiCurrent->pmsd = pmsd;
|
|
|
|
Lock(&(pmsd->spwnd), pwnd);
|
|
|
|
/*
|
|
* Set fForeground so we know whether to draw or not.
|
|
*/
|
|
pmsd->fForeground = (ptiCurrent->pq == gpqForeground) ? TRUE : FALSE;
|
|
|
|
/*
|
|
* Get the client and window rects.
|
|
*/
|
|
CopyRect(&pmsd->rcWindow, &pwnd->rcWindow);
|
|
|
|
if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
if (TestWF(pwnd, WEFTOPMOST) || TestWF(pwnd, WEFTOOLWINDOW))
|
|
pmsd->rcParent = gpDispInfo->rcScreen;
|
|
else
|
|
pmsd->rcParent = gpsi->rcWork;
|
|
} else {
|
|
CopyRect(&pmsd->rcParent, &pwnd->spwndParent->rcClient);
|
|
}
|
|
|
|
/*
|
|
* If the parent does have a region, intersect with its bounding rect.
|
|
*/
|
|
if (pwnd->spwndParent->hrgnClip != NULL) {
|
|
|
|
RECT rcT;
|
|
|
|
GreGetRgnBox(pwnd->spwndParent->hrgnClip, &rcT);
|
|
IntersectRect(&pmsd->rcParent, &pmsd->rcParent, &rcT);
|
|
}
|
|
|
|
_ClipCursor(&pmsd->rcParent);
|
|
|
|
/*
|
|
* Are we completely offscreen?
|
|
*/
|
|
pmsd->fOffScreen = (IntersectRect(&rcSys, &pmsd->rcWindow, &pmsd->rcParent) == 0);
|
|
CopyRect(&rcSys, &pmsd->rcWindow);
|
|
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
|
|
/*
|
|
* No need to send WM_GETMINMAXINFO since we know the minimized size.
|
|
*/
|
|
pmsd->ptMinTrack.x = pmsd->ptMaxTrack.x = SYSMET(CXMINIMIZED);
|
|
pmsd->ptMinTrack.y = pmsd->ptMaxTrack.y = SYSMET(CYMINIMIZED);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Get the Min and the Max tracking size.
|
|
* rgpt[0] = Minimized size
|
|
* rgpt[1] = Maximized size
|
|
* rgpt[2] = Maximized position
|
|
* rgpt[3] = Minimum tracking size
|
|
* rgpt[4] = Maximum tracking size
|
|
*/
|
|
xxxInitSendValidateMinMaxInfo(pwnd);
|
|
pmsd->ptMinTrack = rgptMinMaxWnd[MMI_MINTRACK];
|
|
pmsd->ptMaxTrack = rgptMinMaxWnd[MMI_MAXTRACK];
|
|
}
|
|
|
|
/*
|
|
* Set up the drag rectangle.
|
|
*/
|
|
CopyRect(&pmsd->rcDrag, &pmsd->rcWindow);
|
|
CopyRect(&pmsd->rcDragCursor, &pmsd->rcDrag);
|
|
|
|
ptStart.x = LOSHORT(wptStart);
|
|
ptStart.y = HISHORT(wptStart);
|
|
|
|
/*
|
|
* Assume Move/Size from mouse.
|
|
*/
|
|
pmsd->fInitSize = FALSE;
|
|
pmsd->fmsKbd = FALSE;
|
|
|
|
/*
|
|
* Get the mouse position for this move/size command.
|
|
*/
|
|
switch (pmsd->cmd = cmdMove) {
|
|
case WMSZ_KEYMOVE:
|
|
pmsd->cmd = cmdMove = WMSZ_MOVE;
|
|
|
|
/*
|
|
** FALL THRU **
|
|
*/
|
|
|
|
case WMSZ_KEYSIZE:
|
|
|
|
_SetCursor(SYSCUR(SIZEALL));
|
|
|
|
if (!TestWF(pwnd, WFMINIMIZED))
|
|
pmsd->fInitSize = TRUE;
|
|
|
|
if (pMenuState == NULL) {
|
|
break;
|
|
}
|
|
|
|
if ((pMenuState->mnFocus == KEYBDHOLD) ||
|
|
((pMenuState->mnFocus == MOUSEHOLD) && TestWF(pwnd, WFMINIMIZED))) {
|
|
|
|
pmsd->fmsKbd = TRUE;
|
|
pmsd->ptRestore.x = LOSHORT(wptStart);
|
|
pmsd->ptRestore.y = HISHORT(wptStart);
|
|
|
|
/*
|
|
* Center cursor in caption area of window
|
|
*/
|
|
|
|
/*
|
|
* Horizontally
|
|
*/
|
|
ptStart.x = (pmsd->rcDrag.left + pmsd->rcDrag.right) / 2;
|
|
|
|
/*
|
|
* Vertically
|
|
*/
|
|
if (TestWF(pwnd,WFMINIMIZED) || (pmsd->cmd != WMSZ_MOVE)) {
|
|
ptStart.y = (pmsd->rcDrag.top + pmsd->rcDrag.bottom) / 2;
|
|
} else {
|
|
int dy;
|
|
|
|
dy = (TestWF(pwnd, WEFTOOLWINDOW)) ? SYSMET(CYSMCAPTION) : SYSMET(CYCAPTION);
|
|
ptStart.y = pmsd->rcDrag.top + SYSMET(CYFIXEDFRAME) + dy / 2;
|
|
}
|
|
|
|
InternalSetCursorPos(ptStart.x, ptStart.y, grpdeskRitInput);
|
|
|
|
xxxMS_FlushWigglies();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
gfDraggingFullWindow =
|
|
pmsd->fDragFullWindows = fDragFullWindows;
|
|
|
|
if (pMenuState != NULL) {
|
|
pMenuState->fIsSysMenu = FALSE;
|
|
}
|
|
|
|
/*
|
|
* If we hit with the mouse, set up impx and impy so that we
|
|
* can use the keyboard too.
|
|
*/
|
|
pmsd->impx = rgcmdmpix[cmdMove];
|
|
pmsd->impy = rgcmdmpiy[cmdMove];
|
|
|
|
/*
|
|
* Setup dxMouse and dyMouse - If we're sizing with the keyboard these
|
|
* guys are set to zero down in the keyboard code.
|
|
*/
|
|
if ((i = rgimpiwx[cmdMove]) != (-1))
|
|
pmsd->dxMouse = *((int *)&pmsd->rcWindow + (short)i) - ptStart.x;
|
|
|
|
if ((i = rgimpiwy[cmdMove]) != (-1))
|
|
pmsd->dyMouse = *((int *)&pmsd->rcWindow + (short)i) - ptStart.y;
|
|
|
|
/*
|
|
* Tell Gdi the width of the drag rect (if its a special size)
|
|
* Turn the drag rect on. 0 specifies start drag.
|
|
*/
|
|
if (!TestWF(pwnd, WFSIZEBOX))
|
|
bSetDevDragWidth(gpDispInfo->hDev, 1);
|
|
|
|
xxxDrawDragRect(pmsd, NULL, DDR_START);
|
|
ptiCurrent->TIF_flags |= TIF_TRACKRECTVISIBLE;
|
|
|
|
msg.lParam = MAKELONG(ptStart.x, ptStart.y);
|
|
|
|
/*
|
|
* Right here win3.1 calls LockWindowUpdate(). This calls SetFMouseMoved()
|
|
* which ensures that the next message in the queue is a mouse message.
|
|
* We need that mouse message as the first message because the first
|
|
* call to TrackInitSize() assumes that lParam is an x, y from a mouse
|
|
* message - scottlu.
|
|
*/
|
|
SetFMouseMoved();
|
|
|
|
/*
|
|
* Send this message for winoldapp support
|
|
*/
|
|
xxxSendMessage(pwnd, WM_ENTERSIZEMOVE, 0L, 0L);
|
|
xxxCapture(ptiCurrent, pwnd, CLIENT_CAPTURE_INTERNAL);
|
|
|
|
/*
|
|
* Show the move cursor for non-mouse systems.
|
|
*/
|
|
_ShowCursor(TRUE);
|
|
|
|
while (!(pmsd->fTrackCancelled)) {
|
|
|
|
/*
|
|
* Let other messages not related to dragging be dispatched
|
|
* to the application window.
|
|
* In the case of clock, clock will now receive messages to
|
|
* update the time displayed instead of having the time display
|
|
* freeze while we are dragging.
|
|
*/
|
|
while (ptiCurrent->pq->spwndCapture == pwnd) {
|
|
|
|
if (xxxPeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
|
|
if ((msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)
|
|
|| (msg.message == WM_QUEUESYNC)
|
|
|| (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)) {
|
|
|
|
break;
|
|
}
|
|
|
|
if (_CallMsgFilter(&msg, cmdMove == WMSZ_MOVE ? MSGF_MOVE : MSGF_SIZE)) {
|
|
continue;
|
|
}
|
|
|
|
_TranslateMessage(&msg, 0);
|
|
/*
|
|
* To prevent applications from doing
|
|
* a PeekMessage loop and getting the mouse move messages that
|
|
* are destined for the xxxMoveSize PeekMessage loop, we OR in
|
|
* this flag. See comments in input.c for xxxInternalGetMessage.
|
|
*/
|
|
ptiCurrent->TIF_flags |= TIF_MOVESIZETRACKING;
|
|
xxxDispatchMessage(&msg);
|
|
ptiCurrent->TIF_flags &= ~TIF_MOVESIZETRACKING;
|
|
|
|
} else {
|
|
/*
|
|
* If we've been cancelled by someone else, or our pwnd
|
|
* has been destroyed, blow out of here.
|
|
*/
|
|
if (pmsd->fTrackCancelled)
|
|
break;
|
|
|
|
if (!xxxSleepThread(QS_INPUT, 0, TRUE))
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we've lost capture while tracking,
|
|
* cancel the move/size operation.
|
|
*/
|
|
if (ptiCurrent->pq->spwndCapture != pwnd) {
|
|
|
|
/*
|
|
* Fake a key-down of the escape key to cancel.
|
|
*/
|
|
xxxMS_TrackMove(pwnd, WM_KEYDOWN, (DWORD)VK_ESCAPE, 1, pmsd, &rcSys);
|
|
goto MoveSizeCleanup;
|
|
}
|
|
|
|
/*
|
|
* If we've been cancelled by someone else, or our pwnd
|
|
* has been destroyed, blow out of here.
|
|
*/
|
|
if (pmsd->fTrackCancelled) {
|
|
pmsd->fTrackCancelled = FALSE;
|
|
goto MoveSizeCleanup;
|
|
}
|
|
|
|
/*
|
|
* If we get a WM_QUEUESYNC, let the CBT hook know.
|
|
*/
|
|
if (msg.message == WM_QUEUESYNC) {
|
|
xxxCallHook(HCBT_QS, 0, 0, WH_CBT);
|
|
}
|
|
|
|
if (pmsd->fInitSize) {
|
|
if (!xxxTrackInitSize(pwnd, msg.message, msg.wParam, msg.lParam,
|
|
pmsd)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert captured mouse into screen coordinates.
|
|
*/
|
|
x = msg.pt.x + pmsd->dxMouse;
|
|
y = msg.pt.y + pmsd->dyMouse;
|
|
|
|
/*
|
|
* This is checked twice so the same message is not processed both
|
|
* places.
|
|
*/
|
|
if (!pmsd->fInitSize) {
|
|
xxxMS_TrackMove(pwnd, msg.message, msg.wParam, MAKELONG(x, y),
|
|
pmsd, &rcSys);
|
|
}
|
|
}
|
|
|
|
MoveSizeCleanup:
|
|
|
|
/*
|
|
* Reset the border size if it was abnormal
|
|
*/
|
|
|
|
if (!TestWF(pwnd, WFSIZEBOX))
|
|
bSetDevDragWidth(gpDispInfo->hDev, gpsi->gclBorder + BORDER_EXTRA);
|
|
|
|
/*
|
|
* Revalidation: If pwnd is deleted unexpectedly, jump here to cleanup.
|
|
*/
|
|
|
|
bSetDevDragRect(gpDispInfo->hDev, NULL, NULL);
|
|
ptiCurrent->TIF_flags &= ~(TIF_TRACKRECTVISIBLE);
|
|
|
|
if (pmsd->fDragFullWindows) {
|
|
if (ghrgnUpdateSave != NULL) {
|
|
GreDeleteObject(ghrgnUpdateSave);
|
|
ghrgnUpdateSave = NULL;
|
|
gnUpdateSave = 0;
|
|
}
|
|
}
|
|
|
|
gfDraggingFullWindow = FALSE;
|
|
|
|
ptiCurrent->pmsd = NULL;
|
|
|
|
Unlock(&pmsd->spwnd);
|
|
|
|
|
|
_ShowCursor(FALSE);
|
|
|
|
/*
|
|
* Free the move/size data structure
|
|
*/
|
|
UserFreePool(pmsd);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* This calls RedrawHungWindow() on windows that do not belong to this thread.
|
|
*
|
|
* History:
|
|
* 27-May-1994 johannec
|
|
\***************************************************************************/
|
|
|
|
VOID UpdateOtherThreadsWindows(
|
|
PWND pwnd,
|
|
HRGN hrgnFullDrag)
|
|
{
|
|
PWND pwndChild;
|
|
|
|
RedrawHungWindow(pwnd, hrgnFullDrag);
|
|
|
|
/*
|
|
* If the parent window does not have the flag WFCLIPCHILDREN set,
|
|
* there is no need to redraw its children.
|
|
*/
|
|
if (!TestWF(pwnd, WFCLIPCHILDREN))
|
|
return;
|
|
|
|
pwndChild = pwnd->spwndChild;
|
|
|
|
while (pwndChild != NULL) {
|
|
UpdateOtherThreadsWindows(pwndChild, hrgnFullDrag);
|
|
pwndChild = pwndChild->spwndNext;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* This calls UpdateWindow() on every window that is owned by this thread
|
|
* and calls RedrawHungWindow() for windows owned by other threads.
|
|
*
|
|
* History:
|
|
* 28-Sep-1993 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
VOID xxxUpdateThreadsWindows(
|
|
PTHREADINFO pti,
|
|
PWND pwnd,
|
|
HRGN hrgnFullDrag)
|
|
{
|
|
HWND hwnd;
|
|
TL tlpwnd;
|
|
|
|
while (pwnd != NULL) {
|
|
if (GETPTI(pwnd) == pti) {
|
|
hwnd = HW(pwnd->spwndNext);
|
|
ThreadLockAlways(pwnd, &tlpwnd);
|
|
xxxUpdateWindow(pwnd);
|
|
ThreadUnlock(&tlpwnd);
|
|
pwnd = RevalidateHwnd(hwnd);
|
|
} else {
|
|
UpdateOtherThreadsWindows(pwnd, hrgnFullDrag);
|
|
pwnd = pwnd->spwndNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxDrawDragRect
|
|
*
|
|
* Draws the drag rect for sizing and moving windows. When moving windows,
|
|
* can move full windows including client area. lprc new rect to move to.
|
|
* if lprc is null, flags specify why.
|
|
*
|
|
* flags: DDR_START 0 - start drag.
|
|
* DDR_ENDACCEPT 1 - end and accept
|
|
* DDR_ENDCANCEL 2 - end and cancel.
|
|
*
|
|
* History:
|
|
* 07-29-91 darrinm Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxDrawDragRect(
|
|
PMOVESIZEDATA pmsd,
|
|
LPRECT lprc,
|
|
UINT type)
|
|
{
|
|
HDC hdc;
|
|
int lvBorder;
|
|
HRGN hrgnClip;
|
|
|
|
/*
|
|
* If we're dragging an icon, or we're not foreground, don't draw
|
|
* the dragging rect.
|
|
*/
|
|
if (!pmsd->fForeground) {
|
|
|
|
if (lprc != NULL)
|
|
CopyRect(&pmsd->rcDrag, lprc);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If it already equals, just return.
|
|
*/
|
|
if ((lprc != NULL) && EqualRect(&pmsd->rcDrag, lprc))
|
|
return;
|
|
|
|
if (!(pmsd->fDragFullWindows)) {
|
|
|
|
/*
|
|
* If we were not able to lock the screen (because some other process
|
|
* or thread had the screen locked), then get a dc but make sure
|
|
* it is totally clipped to nothing.
|
|
* NO longer a posibility
|
|
*/
|
|
|
|
/*
|
|
* Clip to client rect of parent. (Client given in screen coords.)
|
|
*/
|
|
hrgnClip = GreCreateRectRgnIndirect(&pmsd->rcParent);
|
|
|
|
/*
|
|
* Clip to the parent's window clipping rgn if it has one.
|
|
*/
|
|
if (hrgnClip != NULL && pmsd->spwnd->spwndParent->hrgnClip != NULL)
|
|
IntersectRgn(hrgnClip,
|
|
hrgnClip,
|
|
pmsd->spwnd->spwndParent->hrgnClip);
|
|
|
|
if (hrgnClip == NULL)
|
|
hrgnClip = MAXREGION;
|
|
|
|
/*
|
|
* If lprc == NULL, just draw rcDrag once. If lprc != NULL,
|
|
* undraw *lprc, draw rcDrag, copy in *lprc.
|
|
*/
|
|
|
|
/*
|
|
* Use size 1 for minimized or non-sizeable windows. Otherwise
|
|
* use the # of borders (2 for outer edge, 1 for border, clBorder for
|
|
* size border.
|
|
*/
|
|
if (TestWF(pmsd->spwnd, WFMINIMIZED) || !TestWF(pmsd->spwnd, WFSIZEBOX))
|
|
lvBorder = 1;
|
|
else
|
|
lvBorder = 3 + gpsi->gclBorder;
|
|
|
|
/*
|
|
* Get a screen DC clipped to the parent, select in a gray brush.
|
|
*/
|
|
hdc = _GetDCEx(
|
|
PWNDDESKTOP(pmsd->spwnd),
|
|
hrgnClip,
|
|
DCX_WINDOW | DCX_CACHE | DCX_INTERSECTRGN | DCX_LOCKWINDOWUPDATE);
|
|
|
|
if (lprc != NULL) {
|
|
|
|
/*
|
|
* Move the frame to a new location by delta drawing
|
|
*/
|
|
GreLockDisplay(gpDispInfo->pDevLock);
|
|
bMoveDevDragRect(gpDispInfo->hDev, (PRECTL) lprc);
|
|
CopyRect(&pmsd->rcDrag, lprc);
|
|
GreUnlockDisplay(gpDispInfo->pDevLock);
|
|
|
|
} else {
|
|
|
|
if (type == DDR_START) {
|
|
bSetDevDragRect(gpDispInfo->hDev,
|
|
(PRECTL)&pmsd->rcDrag,
|
|
(PRECTL)&pmsd->rcParent);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Release the DC & delete hrgnClip
|
|
*/
|
|
_ReleaseDC(hdc);
|
|
|
|
} else {
|
|
|
|
RECT rcSWP;
|
|
HRGN hrgnFullDragNew;
|
|
HRGN hrgnFullDragOld;
|
|
PTHREADINFO ptiCancel = GETPTI(pmsd->spwnd);
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* If ptiCancel != ptiCurrent, we must have come from xxxCancelTracking,
|
|
* which has already locked ptiCancel.
|
|
*/
|
|
if (ptiCancel != ptiCurrent) {
|
|
CheckLock(ptiCancel);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* To prevent applications (like Micrografx Draw) from doing
|
|
* a PeekMessage loop and getting the mouse move messages that
|
|
* are destined for the xxxMoveSize PeekMessage loop, we OR in
|
|
* this flag. See comments in input.c for xxxInternalGetMessage.
|
|
*/
|
|
ptiCancel->TIF_flags |= TIF_MOVESIZETRACKING;
|
|
|
|
if (lprc != NULL)
|
|
CopyRect(&(pmsd->rcDrag), lprc);
|
|
|
|
CopyRect(&rcSWP, &(pmsd->rcDrag));
|
|
|
|
/*
|
|
* Convert coordinates to client if the window is a child window or
|
|
* if it's a popup-with parent. The test for the popup is necessary
|
|
* to solve a problem where a popup was assigned a parent of a MDI-
|
|
* CLIENT window.
|
|
*/
|
|
if (TestWF(pmsd->spwnd, WFCHILD) ||
|
|
(TestWF(pmsd->spwnd, WFPOPUP) && pmsd->spwnd->spwndParent)) {
|
|
|
|
_ScreenToClient(pmsd->spwnd->spwndParent, (LPPOINT)&rcSWP);
|
|
_ScreenToClient(pmsd->spwnd->spwndParent, ((LPPOINT)&rcSWP)+1);
|
|
}
|
|
|
|
/*
|
|
* Don't bother being optimal here. There's one case where we
|
|
* really shouldn't blow away the SPB--the window is being sized
|
|
* bigger. We do want to do this when moving or sizing the window
|
|
* smaller. Why bother detecting the first case?
|
|
*/
|
|
if (TestWF(pmsd->spwnd, WFHASSPB)){
|
|
|
|
PSPB pspb;
|
|
RECT rc;
|
|
|
|
/*
|
|
* If we're intersecting the original window rect and the window
|
|
* has an SPB saved onboard, then just free it. Otherwise the
|
|
* window will move, the entire SPB will blt over it, we'll
|
|
* invalidate the intersection, and the window will repaint,
|
|
* causing mad flicker.
|
|
*/
|
|
pspb = FindSpb(pmsd->spwnd);
|
|
|
|
CopyRect(&rc, &pmsd->spwnd->rcWindow);
|
|
if (lprc && IntersectRect(&rc, &rc, lprc)){
|
|
FreeSpb(pspb);
|
|
}
|
|
}
|
|
|
|
hrgnFullDragOld = GreCreateRectRgnIndirect(&pmsd->spwnd->rcWindow);
|
|
|
|
if (pmsd->spwnd->hrgnClip != NULL)
|
|
IntersectRgn(hrgnFullDragOld,
|
|
hrgnFullDragOld,
|
|
pmsd->spwnd->hrgnClip);
|
|
|
|
xxxSetWindowPos(pmsd->spwnd,
|
|
NULL,
|
|
rcSWP.left, rcSWP.top,
|
|
rcSWP.right-rcSWP.left, rcSWP.bottom-rcSWP.top,
|
|
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
|
|
|
/*
|
|
* We locked ptiCancel, so ptiCancel->pmsd has not been unexpectedly
|
|
* freed in DeleteThreadInfo(), but xxxMoveSize() may have terminated
|
|
* during our callback to xxxSetWindowPos and freed the pmsd there.
|
|
*/
|
|
if (ptiCancel->pmsd != pmsd) {
|
|
RIPMSG3(RIP_ERROR,
|
|
"xxxDrawDragRect: ptiCancel(%lx)->pmsd(%lx) != pmsd(%lx)\n",
|
|
ptiCancel, ptiCancel->pmsd, pmsd);
|
|
goto CleanupAfterPmsdDisappearance;
|
|
}
|
|
hrgnFullDragNew = GreCreateRectRgnIndirect(&pmsd->spwnd->rcWindow);
|
|
|
|
if (pmsd->spwnd->hrgnClip != NULL) {
|
|
IntersectRgn(hrgnFullDragNew,
|
|
hrgnFullDragNew,
|
|
pmsd->spwnd->hrgnClip);
|
|
}
|
|
|
|
/*
|
|
* Set the full drag update region that is used in RedrawHungWindow.
|
|
*/
|
|
if (hrgnFullDragNew == NULL) {
|
|
|
|
/*
|
|
* We couldn't create the new full drag region so don't
|
|
* use the full drag region to RedrawHungWindow. Using
|
|
* NULL with force a redraw of the entire window's hrgnUpdate.
|
|
* (which is what we used to do, overdrawing but at least
|
|
* covering the invalidated areas).
|
|
*/
|
|
if (hrgnFullDragOld != NULL) {
|
|
GreDeleteObject(hrgnFullDragOld);
|
|
hrgnFullDragOld = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (hrgnFullDragOld != NULL) {
|
|
|
|
/*
|
|
* Subtract the new window rect from the old window rect
|
|
* to create the update region caused by the drag.
|
|
*/
|
|
SubtractRgn(hrgnFullDragOld, hrgnFullDragOld, hrgnFullDragNew);
|
|
}
|
|
}
|
|
|
|
xxxUpdateThreadsWindows(ptiCurrent,
|
|
PWNDDESKTOP(pmsd->spwnd)->spwndChild,
|
|
hrgnFullDragOld);
|
|
|
|
GreDeleteObject(hrgnFullDragNew);
|
|
CleanupAfterPmsdDisappearance:
|
|
GreDeleteObject(hrgnFullDragOld);
|
|
|
|
ptiCancel->TIF_flags &= ~TIF_MOVESIZETRACKING;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxCancelTrackingForThread
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
VOID xxxCancelTrackingForThread(
|
|
PTHREADINFO ptiCancel)
|
|
{
|
|
PMOVESIZEDATA pmsdCancel;
|
|
|
|
UserAssert(ptiCancel);
|
|
|
|
/*
|
|
* If this thread isn't around any more, skip it.
|
|
*/
|
|
if (ptiCancel == NULL)
|
|
return;
|
|
|
|
if ((pmsdCancel = ptiCancel->pmsd) != NULL) {
|
|
|
|
/*
|
|
* Found one, now stop tracking.
|
|
*/
|
|
pmsdCancel->fTrackCancelled = TRUE;
|
|
|
|
/*
|
|
* Only remove the tracking rectangle if it's
|
|
* been made visible.
|
|
*/
|
|
if (ptiCancel->TIF_flags & TIF_TRACKRECTVISIBLE) {
|
|
bSetDevDragRect(gpDispInfo->hDev, NULL, NULL);
|
|
if (!(pmsdCancel->fDragFullWindows)) {
|
|
xxxDrawDragRect(pmsdCancel, NULL, DDR_ENDCANCEL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Leave TIF_TRACKING set to prevent xxxMoveSize()
|
|
* recursion.
|
|
*/
|
|
ptiCancel->TIF_flags &= ~TIF_TRACKRECTVISIBLE;
|
|
if (ptiCancel->pq) {
|
|
SetWakeBit(ptiCancel, QS_MOUSEMOVE);
|
|
}
|
|
|
|
/*
|
|
* If the tracking window is still in menuloop, send the
|
|
* WM_CANCELMODE message so that it can exit the menu.
|
|
* This fixes the bug where we have 2 icons with their
|
|
* system menu up.
|
|
* 8/5/94 johannec
|
|
*/
|
|
if (IsInsideMenuLoop(ptiCancel) && ptiCancel->pmsd)
|
|
_PostMessage(ptiCancel->pmsd->spwnd, WM_CANCELMODE, 0, 0);
|
|
|
|
/*
|
|
* Turn off capture
|
|
*/
|
|
xxxCapture(ptiCancel, NULL, NO_CAP_CLIENT);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxCancelTracking
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#define MAX_THREADS 12
|
|
|
|
VOID xxxCancelTracking(VOID)
|
|
{
|
|
PTHREADINFO pti;
|
|
PTHREADINFO ptiList[MAX_THREADS];
|
|
TL tlptiList[MAX_THREADS];
|
|
TL tlspwndList[MAX_THREADS];
|
|
UINT cThreads = 0;
|
|
INT i;
|
|
PLIST_ENTRY pHead;
|
|
PLIST_ENTRY pEntry;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* Build a list of threads that we need to look at. We can't just
|
|
* walk the pointer list while we're doing the work, because we
|
|
* might leave the critical section and the pointer could get
|
|
* deleted out from under us.
|
|
*/
|
|
pHead = &grpdeskRitInput->PtiList;
|
|
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
|
|
|
|
pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
|
|
|
|
if (pti->pmsd != NULL) {
|
|
|
|
UserAssert(cThreads < MAX_THREADS);
|
|
|
|
if (cThreads < MAX_THREADS) {
|
|
ThreadLockPti(ptiCurrent, pti, &tlptiList[cThreads]);
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pti->pmsd->spwnd, &tlspwndList[cThreads]);
|
|
ptiList[cThreads++] = pti;
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
if (cThreads > 1) {
|
|
RIPMSG1(RIP_ERROR,
|
|
"xxxCancelTracking: %d threads with a pmsd! - call IanJa x63321\n",
|
|
cThreads);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Walk the list backwards so the unlocks will be done in the right order.
|
|
*/
|
|
for (i = cThreads - 1; i >= 0; i--) {
|
|
if (!(ptiList[i]->TIF_flags & TIF_INCLEANUP)) {
|
|
xxxCancelTrackingForThread(ptiList[i]);
|
|
}
|
|
|
|
ThreadUnlock(&tlspwndList[i]);
|
|
ThreadUnlockPti(ptiCurrent, &tlptiList[i]);
|
|
}
|
|
}
|