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.
1172 lines
34 KiB
1172 lines
34 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: scrollw.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Window and DC scrolling routines.
|
|
*
|
|
* History:
|
|
* 18-Jul-1991 DarrinM Recreated from Win 3.1 source.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/*
|
|
* Problems so far:
|
|
* DCs not at origin (0, 0)
|
|
* funny coordinate systems
|
|
*/
|
|
|
|
/***************************************************************************\
|
|
* GetTrueClipRgn
|
|
*
|
|
* Get copy of true clip region and its bounds.
|
|
*
|
|
* History:
|
|
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
int GetTrueClipRgn(
|
|
HDC hdc,
|
|
HRGN hrgnClip)
|
|
{
|
|
POINT pt;
|
|
int code;
|
|
|
|
code = GreCopyVisRgn(hdc, hrgnClip);
|
|
|
|
/*
|
|
* NOTE!!! The global ghrgnScrl2 is used in this routine!
|
|
*/
|
|
GreGetDCOrg(hdc, &pt);
|
|
|
|
if (GreGetRandomRgn(hdc, ghrgnScrl2, 1)) {
|
|
GreOffsetRgn(ghrgnScrl2, pt.x, pt.y);
|
|
code = IntersectRgn(hrgnClip, hrgnClip, ghrgnScrl2);
|
|
}
|
|
|
|
/*
|
|
* Finally convert the result to DC coordinates
|
|
*/
|
|
GreOffsetRgn(hrgnClip, -pt.x, -pt.y);
|
|
|
|
return code;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InternalScrollDC
|
|
*
|
|
* This function requires all input parameters in device coordinates
|
|
* (NOT screen!)
|
|
*
|
|
* History:
|
|
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
int InternalScrollDC(
|
|
HDC hdc,
|
|
int dx,
|
|
int dy,
|
|
RECT *prcSrc,
|
|
RECT *prcClip,
|
|
HRGN hrgnInvalid,
|
|
HRGN hrgnUpdate,
|
|
LPRECT prcUpdate,
|
|
BOOL fLogUnits)
|
|
{
|
|
RECT rcVis;
|
|
RECT rcSrc;
|
|
RECT rcClip;
|
|
RECT rcUnclippedSrc;
|
|
RECT rcDst;
|
|
RECT rcUpdate;
|
|
RECT rcValid;
|
|
BOOL fSrcNotEmpty;
|
|
BOOL fHaveVisRgn;
|
|
POINT rgpt[2];
|
|
int dxLog;
|
|
int dyLog;
|
|
int wClip;
|
|
int wClipValid;
|
|
BOOL bMirroredDC=FALSE;
|
|
|
|
fHaveVisRgn = FALSE;
|
|
|
|
/*
|
|
* Enter a critical region to ensure that no one changes visrgns
|
|
* or update regions while we scroll bits around.
|
|
*/
|
|
GreLockDisplay(gpDispInfo->hDev);
|
|
|
|
if ((wClip = GreGetClipBox(hdc, &rcVis, TRUE)) == ERROR) {
|
|
|
|
ErrorExit:
|
|
|
|
GreUnlockDisplay(gpDispInfo->hDev);
|
|
return ERROR;
|
|
}
|
|
|
|
CopyRect(&rcSrc, (prcSrc) ? prcSrc : &rcVis);
|
|
if (prcClip) {
|
|
CopyRect(&rcClip, prcClip);
|
|
}
|
|
|
|
dxLog = dx;
|
|
dyLog = dy;
|
|
|
|
if (fLogUnits) {
|
|
|
|
/*
|
|
* Convert input parameters to device coordinates
|
|
*/
|
|
GreLPtoDP(hdc, (LPPOINT)&rcVis, 2);
|
|
GreLPtoDP(hdc, (LPPOINT)&rcSrc, 2);
|
|
|
|
//
|
|
// Since this is a mirrored DC, then the resulting
|
|
// device coord will be flowing from right to left
|
|
// (i.e. rc.right < rc.left) so they should be flipped.
|
|
// [samera]
|
|
//
|
|
if (GreGetLayout(hdc) & LAYOUT_RTL) {
|
|
int iTemp = rcVis.left;
|
|
rcVis.left = rcVis.right;
|
|
rcVis.right = iTemp;
|
|
|
|
iTemp = rcSrc.left;
|
|
rcSrc.left = rcSrc.right;
|
|
rcSrc.right = iTemp;
|
|
|
|
bMirroredDC = TRUE;
|
|
}
|
|
|
|
if (prcClip) {
|
|
GreLPtoDP(hdc, (LPPOINT)&rcClip, 2);
|
|
|
|
//
|
|
// Since this is a mirrored DC, then the resulting
|
|
// device coord will be flowing from right to left
|
|
// (i.e. rc.right < rc.left) so they should be flipped.
|
|
// [samera]
|
|
//
|
|
if (bMirroredDC) {
|
|
int iTemp = rcClip.left;
|
|
rcClip.left = rcClip.right;
|
|
rcClip.right = iTemp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The delta values must be treated as a vector from
|
|
* the point (0, 0) to (dx, dy). Scale it as such, then
|
|
* compute the difference. This handles flipped coordinate systems.
|
|
*/
|
|
rgpt[0].x = rgpt[0].y = 0;
|
|
rgpt[1].x = dx;
|
|
rgpt[1].y = dy;
|
|
|
|
GreLPtoDP(hdc, rgpt, 2);
|
|
|
|
dx = rgpt[1].x - rgpt[0].x;
|
|
dy = rgpt[1].y - rgpt[0].y;
|
|
}
|
|
|
|
switch (wClip) {
|
|
case NULLREGION:
|
|
|
|
NullExit:
|
|
|
|
if (hrgnUpdate && !SetEmptyRgn(hrgnUpdate))
|
|
goto ErrorExit;
|
|
|
|
if (prcUpdate) {
|
|
SetRectEmpty(prcUpdate);
|
|
}
|
|
|
|
GreUnlockDisplay(gpDispInfo->hDev);
|
|
return NULLREGION;
|
|
|
|
case COMPLEXREGION:
|
|
GetTrueClipRgn(hdc, ghrgnScrlVis);
|
|
fHaveVisRgn = TRUE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* First compute the source and destination rectangles.
|
|
*
|
|
* rcDst = Offset(rcSrc, dx, dy)
|
|
*/
|
|
rcDst.left = rcSrc.left + dx;
|
|
rcDst.right = rcSrc.right + dx;
|
|
rcDst.top = rcSrc.top + dy;
|
|
rcDst.bottom = rcSrc.bottom + dy;
|
|
|
|
/*
|
|
* If necessary, intersect with caller-supplied clip rect.
|
|
*/
|
|
if (prcClip) {
|
|
|
|
if ((wClip == SIMPLEREGION) &&
|
|
((hrgnInvalid == NULL) || (hrgnInvalid == HRGN_FULL))) {
|
|
|
|
/*
|
|
* Simple clip region: just a rect intersection
|
|
*/
|
|
if (!IntersectRect(&rcVis, &rcVis, &rcClip))
|
|
goto NullExit;
|
|
|
|
} else {
|
|
|
|
if (!fHaveVisRgn) {
|
|
|
|
if (GetTrueClipRgn(hdc, ghrgnScrlVis) == ERROR)
|
|
goto ErrorExit;
|
|
|
|
fHaveVisRgn = TRUE;
|
|
}
|
|
|
|
SetRectRgnIndirect(ghrgnScrl1, &rcClip);
|
|
wClip = IntersectRgn(ghrgnScrlVis, ghrgnScrl1, ghrgnScrlVis);
|
|
switch (wClip) {
|
|
case ERROR:
|
|
goto ErrorExit;
|
|
|
|
case NULLREGION:
|
|
goto NullExit;
|
|
|
|
case SIMPLEREGION:
|
|
|
|
/*
|
|
* If the clipped region is simple, we're back in fat
|
|
* rect city.
|
|
*/
|
|
GreGetRgnBox(ghrgnScrlVis, &rcVis);
|
|
break;
|
|
|
|
case COMPLEXREGION:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Time for basic scrolling area calculations:
|
|
*
|
|
* Dst = Offset(Src, dx, dy) & Vis
|
|
* Src = Src & Vis
|
|
* Valid = Offset(Src, dx, dy) & Dst
|
|
* Valid = Valid & Invalid & Offset(Invalid, dx, dy)
|
|
* Update = (Src | Dst) - Valid
|
|
*
|
|
* If the vis region is simple, then we know that the valid region
|
|
* will be rectangular.
|
|
*
|
|
* The rectangular calculation case can only deal with
|
|
* ghrgnInvalid == NULL or (HRGN)1: the region case is handled the hard way.
|
|
*/
|
|
if ((wClip == SIMPLEREGION) &&
|
|
((hrgnInvalid == NULL) || (hrgnInvalid == HRGN_FULL))) {
|
|
|
|
/*
|
|
* Save a copy of this for update rect calc optimization.
|
|
*/
|
|
CopyRect(&rcUnclippedSrc, &rcSrc);
|
|
|
|
/*
|
|
* Dst = Offset(Src, dx, dy) & Vis.
|
|
*/
|
|
IntersectRect(&rcDst, &rcDst, &rcVis);
|
|
|
|
/*
|
|
* Src = Src & Vis.
|
|
*/
|
|
fSrcNotEmpty = IntersectRect(&rcSrc, &rcSrc, &rcVis);
|
|
|
|
/*
|
|
* Valid = Offset(Src, dx, dy) & Dst.
|
|
*/
|
|
if (hrgnInvalid == HRGN_FULL) {
|
|
SetRectEmpty(&rcValid);
|
|
} else {
|
|
|
|
rcValid.left = rcSrc.left + dx;
|
|
rcValid.right = rcSrc.right + dx;
|
|
rcValid.top = rcSrc.top + dy;
|
|
rcValid.bottom = rcSrc.bottom + dy;
|
|
|
|
IntersectRect(&rcValid, &rcValid, &rcDst);
|
|
}
|
|
|
|
/*
|
|
* Now calculate the update area.
|
|
*
|
|
* There are two cases where the result will be a rectangle:
|
|
*
|
|
* 1) The source rectangle lies completely within the visrgn,
|
|
* and the source and destination don't overlap. In this
|
|
* case the update region is equal to the source rect.
|
|
*
|
|
* 2) The clipped source rectangle is empty, in which case
|
|
* the update region is equal to the clipped dest rect.
|
|
*
|
|
* 3) We're scrolling in one dimension only, and the source
|
|
* and destination DO overlap. In this case we can use
|
|
* UnionRect() and SubtractRect() to do the area arithmetic.
|
|
*/
|
|
if (!fSrcNotEmpty) {
|
|
|
|
/*
|
|
* Clipped source is empty. Update area is the clipped dest.
|
|
*/
|
|
CopyRect(&rcUpdate, &rcDst);
|
|
goto RectUpdate;
|
|
|
|
} else if (IntersectRect(&rcUpdate, &rcSrc, &rcDst)) {
|
|
|
|
/*
|
|
* They overlap. If we're scrolling in one dimension only
|
|
* then we can use rect arithmetic...
|
|
*/
|
|
if (dx == 0 || dy == 0) {
|
|
|
|
UnionRect(&rcUpdate, &rcSrc, &rcDst);
|
|
SubtractRect(&rcUpdate, &rcUpdate, &rcValid);
|
|
goto RectUpdate;
|
|
}
|
|
|
|
} else if (EqualRect(&rcSrc, &rcUnclippedSrc)) {
|
|
|
|
/*
|
|
* They don't overlap, and the source lies completely
|
|
* within the visible region. Update region is the source.
|
|
*/
|
|
CopyRect(&rcUpdate, &rcSrc);
|
|
RectUpdate:
|
|
if (prcUpdate) {
|
|
CopyRect(prcUpdate, &rcUpdate);
|
|
}
|
|
|
|
if (hrgnUpdate && !SetRectRgnIndirect(hrgnUpdate, &rcUpdate)) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
wClip = SIMPLEREGION;
|
|
if (rcUpdate.left >= rcUpdate.right ||
|
|
rcUpdate.top >= rcUpdate.bottom)
|
|
|
|
wClip = NULLREGION;
|
|
|
|
goto DoRectBlt;
|
|
}
|
|
|
|
/*
|
|
* The update region isn't rectangular. Need to do our
|
|
* area calculations with region calls. Skip all this
|
|
* if the caller doesn't care about the update region.
|
|
*
|
|
* If he wants a rectangle but no region, use ghrgnScrl2 as a temp.
|
|
*/
|
|
if (hrgnUpdate == NULL && prcUpdate) {
|
|
hrgnUpdate = ghrgnScrl2;
|
|
}
|
|
|
|
if (hrgnUpdate != NULL) {
|
|
|
|
/*
|
|
* hrgnUpdateCalc = (rcSrc | rcDst) - rcBltDst
|
|
*/
|
|
SetRectRgnIndirect(ghrgnScrl1, &rcSrc);
|
|
SetRectRgnIndirect(hrgnUpdate, &rcDst);
|
|
if (UnionRgn(hrgnUpdate, hrgnUpdate, ghrgnScrl1) == ERROR)
|
|
goto ErrorExit;
|
|
|
|
SetRectRgnIndirect(ghrgnScrl1, &rcValid);
|
|
wClip = SubtractRgn(hrgnUpdate, hrgnUpdate, ghrgnScrl1);
|
|
if (wClip == ERROR)
|
|
goto ErrorExit;
|
|
|
|
if (prcUpdate) {
|
|
GreGetRgnBox(hrgnUpdate, prcUpdate);
|
|
}
|
|
}
|
|
|
|
DoRectBlt:
|
|
|
|
/*
|
|
* If the valid rectangle's not empty, then copy those bits...
|
|
*/
|
|
if (rcValid.left < rcValid.right && rcValid.top < rcValid.bottom) {
|
|
|
|
/*
|
|
* If the DC is in a funny map mode, then be sure to map from
|
|
* device to logical coordinates for BLT call...
|
|
*/
|
|
if (fLogUnits)
|
|
GreDPtoLP(hdc, (LPPOINT)&rcValid, 2);
|
|
|
|
GreBitBlt(hdc,
|
|
rcValid.left,
|
|
rcValid.top,
|
|
rcValid.right - rcValid.left,
|
|
rcValid.bottom - rcValid.top,
|
|
hdc,
|
|
rcValid.left - dxLog,
|
|
rcValid.top - dyLog,
|
|
SRCCOPY,
|
|
0);
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Get the true visrgn if we haven't already.
|
|
*/
|
|
if (!fHaveVisRgn) {
|
|
|
|
if (GetTrueClipRgn(hdc, ghrgnScrlVis) == ERROR)
|
|
goto ErrorExit;
|
|
|
|
fHaveVisRgn = TRUE;
|
|
}
|
|
|
|
/*
|
|
* The visrgn is not empty. Need to do all our calculations
|
|
* with regions.
|
|
*
|
|
* hrgnSrc = hrgnSrc & ghrgnScrlVis
|
|
*/
|
|
SetRectRgnIndirect(ghrgnScrlSrc, &rcSrc);
|
|
if (IntersectRgn(ghrgnScrlSrc, ghrgnScrlSrc, ghrgnScrlVis) == ERROR)
|
|
goto ErrorExit;
|
|
|
|
/*
|
|
* hrgnDst = hrgnDst & ghrgnScrlVis
|
|
*/
|
|
SetRectRgnIndirect(ghrgnScrlDst, &rcDst);
|
|
if (IntersectRgn(ghrgnScrlDst, ghrgnScrlDst, ghrgnScrlVis) == ERROR)
|
|
goto ErrorExit;
|
|
|
|
/*
|
|
* Now compute the valid region:
|
|
*
|
|
* Valid = Offset(Src, dx, dy) & Dst.
|
|
* Valid = Valid & Invalid & Offset(Invalid, dx, dy)
|
|
*
|
|
* If hrgnInvalid is (HRGN)1, then the valid area is empty.
|
|
*/
|
|
wClipValid = NULLREGION;
|
|
if (hrgnInvalid != HRGN_FULL) {
|
|
|
|
/*
|
|
* Valid = Offset(Src, dx, dy) & Dst
|
|
*/
|
|
if (CopyRgn(ghrgnScrlValid, ghrgnScrlSrc) == ERROR)
|
|
goto ErrorExit;
|
|
|
|
GreOffsetRgn(ghrgnScrlValid, dx, dy);
|
|
wClipValid = IntersectRgn(ghrgnScrlValid,
|
|
ghrgnScrlValid,
|
|
ghrgnScrlDst);
|
|
|
|
/*
|
|
* Valid = Valid - Invalid - Offset(Invalid, dx, dy)
|
|
* We need bother only if hrgnInvalid is a real region.
|
|
*/
|
|
if (hrgnInvalid > HRGN_FULL) {
|
|
|
|
if (wClipValid != ERROR && wClipValid != NULLREGION) {
|
|
POINT pt;
|
|
|
|
GetDCOrgOnScreen(hdc, &pt);
|
|
|
|
/*
|
|
* hrgnInvalid is in screen coordinates: map to dc coords
|
|
*/
|
|
CopyRgn(ghrgnScrl2, hrgnInvalid);
|
|
GreOffsetRgn(ghrgnScrl2, -pt.x, -pt.y);
|
|
|
|
wClipValid = SubtractRgn(ghrgnScrlValid,
|
|
ghrgnScrlValid,
|
|
ghrgnScrl2);
|
|
}
|
|
|
|
if (wClipValid != ERROR && wClipValid != NULLREGION) {
|
|
GreOffsetRgn(ghrgnScrl2, dx, dy);
|
|
|
|
wClipValid = SubtractRgn(ghrgnScrlValid,
|
|
ghrgnScrlValid,
|
|
ghrgnScrl2);
|
|
}
|
|
}
|
|
|
|
if (wClipValid == ERROR)
|
|
goto ErrorExit;
|
|
}
|
|
|
|
/*
|
|
* If he wants a rectangle but no region, use ghrgnScrl2 as a temp.
|
|
*/
|
|
if (hrgnUpdate == NULL && prcUpdate) {
|
|
hrgnUpdate = ghrgnScrl2;
|
|
}
|
|
|
|
if (hrgnUpdate != NULL) {
|
|
|
|
/*
|
|
* Update = (Src | Dst) - Valid.
|
|
*/
|
|
wClip = UnionRgn(hrgnUpdate, ghrgnScrlDst, ghrgnScrlSrc);
|
|
if (wClip == ERROR)
|
|
goto ErrorExit;
|
|
|
|
if (wClipValid != NULLREGION) {
|
|
wClip = SubtractRgn(hrgnUpdate, hrgnUpdate, ghrgnScrlValid);
|
|
}
|
|
|
|
if (prcUpdate) {
|
|
GreGetRgnBox(hrgnUpdate, prcUpdate);
|
|
}
|
|
}
|
|
|
|
if (wClipValid != NULLREGION) {
|
|
|
|
#ifdef LATER
|
|
|
|
/*
|
|
* don't use the visrgn here
|
|
*/
|
|
HRGN hrgnSaveVis = CreateEmptyRgn();
|
|
if (hrgnSaveVis != NULL) {
|
|
|
|
BOOL fClipped;
|
|
|
|
fClipped = (GreGetRandomRgn(hdc, hrgnSaveVis, 1) == 1);
|
|
GreExtSelectClipRgn(hdc, ghrgnScrlValid, RGN_COPY);
|
|
|
|
/*
|
|
* If the DC is in a funny map mode, then be sure to
|
|
* map from device to logical coordinates for BLT call...
|
|
*/
|
|
if (fLogUnits)
|
|
GreDPtoLP(hdc, (LPPOINT)&rcDst, 2);
|
|
|
|
/*
|
|
* Gdi can take along time to process this call if
|
|
* it's a printer DC
|
|
*/
|
|
GreBitBlt(hdc,
|
|
rcDst.left,
|
|
rcDst.top,
|
|
rcDst.right - rcDst.left,
|
|
rcDst.bottom - rcDst.top,
|
|
hdc,
|
|
rcDst.left - dxLog,
|
|
rcDst.top - dyLog,
|
|
SRCCOPY,
|
|
0);
|
|
|
|
GreExtSelectClipRgn(hdc,
|
|
(fClipped ? hrgnSaveVis : NULL),
|
|
RGN_COPY);
|
|
|
|
GreDeleteObject(hrgnSaveVis);
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
* Visrgn is expected in DC surface coordinates: offset
|
|
* as appropriate.
|
|
*/
|
|
POINT pt;
|
|
GreGetDCOrg(hdc, &pt);
|
|
|
|
GreOffsetRgn(ghrgnScrlValid, pt.x, pt.y);
|
|
|
|
/*
|
|
* Select in the temporary vis rgn, saving the old
|
|
*/
|
|
|
|
GreSelectVisRgn(hdc, ghrgnScrlValid, SVR_SWAP);
|
|
|
|
/*
|
|
* If the DC is in a funny map mode, then be sure to map from
|
|
* device to logical coordinates for BLT call...
|
|
*/
|
|
if (fLogUnits)
|
|
GreDPtoLP(hdc, (LPPOINT)&rcDst, 2);
|
|
|
|
/*
|
|
* Gdi can take along time to process this call if it's
|
|
* a printer DC.
|
|
*/
|
|
GreBitBlt(hdc,
|
|
rcDst.left,
|
|
rcDst.top,
|
|
rcDst.right - rcDst.left,
|
|
rcDst.bottom - rcDst.top,
|
|
hdc,
|
|
rcDst.left - dxLog,
|
|
rcDst.top - dyLog,
|
|
SRCCOPY,
|
|
0);
|
|
|
|
/*
|
|
* Restore the old vis rgn, leaving ghrgnScrlValid with
|
|
* a valid rgn
|
|
*/
|
|
GreSelectVisRgn(hdc, ghrgnScrlValid, SVR_SWAP);
|
|
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If necessary, convert the resultant update rect back
|
|
* to logical coordinates.
|
|
*/
|
|
if (fLogUnits && prcUpdate) {
|
|
GreDPtoLP(hdc, (LPPOINT)prcUpdate, 2);
|
|
}
|
|
|
|
GreUnlockDisplay(gpDispInfo->hDev);
|
|
|
|
return wClip;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _ScrollDC (API)
|
|
*
|
|
*
|
|
* History:
|
|
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
BOOL _ScrollDC(
|
|
HDC hdc,
|
|
int dx,
|
|
int dy,
|
|
LPRECT prcSrc,
|
|
LPRECT prcClip,
|
|
HRGN hrgnUpdate,
|
|
LPRECT prcUpdate)
|
|
{
|
|
RECT rcSrc;
|
|
RECT rcSpb;
|
|
PWND pwnd;
|
|
HRGN hrgnInvalid;
|
|
BOOL fRet;
|
|
|
|
/*
|
|
* ScrollDC does not scroll update region. Under WinNT, an app calling
|
|
* GetUpdateRgn() then ScrollDC() then InvalidateRgn() will not get
|
|
* any new update region that happened between the Get and Scroll. Under
|
|
* Win3.1, that was not a problem because no other app ran during this
|
|
* time. So pass hrgnInvalid - this will affect the hrgnUpdate and
|
|
* prcUpdate values being returned from ScrollDC with the update region.
|
|
*/
|
|
hrgnInvalid = NULL;
|
|
if ((pwnd = FastWindowFromDC(hdc)) != NULL) {
|
|
|
|
hrgnInvalid = pwnd->hrgnUpdate;
|
|
|
|
if (hrgnInvalid == HRGN_FULL) {
|
|
|
|
/*
|
|
* This is a fix for winhell, a performance testing app
|
|
* written by some guy working for a windows magazine.
|
|
* this app scrolls it's window while it is completely
|
|
* invalid. We normaly won't scroll invalid bits but
|
|
* but we make the exception here
|
|
*/
|
|
hrgnInvalid = NULL;
|
|
}
|
|
}
|
|
|
|
fRet = InternalScrollDC(hdc,
|
|
dx,
|
|
dy,
|
|
prcSrc,
|
|
prcClip,
|
|
hrgnInvalid,
|
|
hrgnUpdate,
|
|
prcUpdate,
|
|
TRUE) != ERROR;
|
|
|
|
/*
|
|
* InternalScrollDC() only scrolls those areas inside the visible region.
|
|
* This means it does no operations on parts of the window if the window
|
|
* isn't visible. This means SPBs don't get properly invalidated. This
|
|
* could be seen by starting a dir, then moving another window with the
|
|
* mouse (and keeping the mouse down until the dir finished). The
|
|
* screen is remembered with an SPB, and the dir window doesn't get
|
|
* properly invalidated because of this.
|
|
*/
|
|
if (pwnd != NULL && AnySpbs()) {
|
|
|
|
if (prcSrc) {
|
|
|
|
rcSrc = *prcSrc;
|
|
OffsetRect(&rcSrc, pwnd->rcClient.left, pwnd->rcClient.top);
|
|
|
|
rcSpb = rcSrc;
|
|
OffsetRect(&rcSpb, dx, dy);
|
|
UnionRect(&rcSpb, &rcSpb, &rcSrc);
|
|
|
|
} else {
|
|
rcSpb = pwnd->rcClient;
|
|
}
|
|
|
|
SpbCheckRect(pwnd, &rcSpb, 0);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ScrollWindowEx (API)
|
|
*
|
|
*
|
|
* History:
|
|
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
int xxxScrollWindowEx(
|
|
PWND pwnd,
|
|
int dx,
|
|
int dy,
|
|
RECT *prcScroll,
|
|
RECT *prcClip,
|
|
HRGN hrgnUpdate,
|
|
LPRECT prcUpdate,
|
|
DWORD flags)
|
|
{
|
|
INT code;
|
|
HDC hdc;
|
|
int dxDev;
|
|
int dyDev;
|
|
RECT rcSrcDev;
|
|
RECT rcSpb, rcSrc;
|
|
DWORD flagsDCX;
|
|
BOOL fHideCaret;
|
|
BOOL fRcScroll = (prcScroll != NULL);
|
|
BOOL fInvisible = FALSE;
|
|
PCARET pcaret;
|
|
POINT pt;
|
|
TL tlpwndChild;
|
|
HRGN hrgnInvalid;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
CheckLock(pwnd);
|
|
UserAssert(IsWinEventNotifyDeferredOK());
|
|
|
|
|
|
if (pwnd == NULL)
|
|
pwnd = ptiCurrent->rpdesk->pDeskInfo->spwnd; // pwndDesktop
|
|
|
|
if (TestWF(pwnd, WEFLAYOUTRTL)) {
|
|
dx = -dx;
|
|
|
|
MirrorRegion(pwnd, hrgnUpdate, TRUE);
|
|
|
|
if(prcScroll) {
|
|
MirrorClientRect(pwnd, prcScroll);
|
|
}
|
|
|
|
if (prcClip) {
|
|
MirrorClientRect(pwnd, prcClip);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If nothing's moving, nothing to do.
|
|
*/
|
|
if ((dx | dy) == 0 ) {
|
|
|
|
goto DoNothing;
|
|
|
|
} else if (!IsVisible(pwnd)) {
|
|
|
|
/* We want to offset our children if we're not minimized. IsVisible()
|
|
* will return FALSE if we're minimized, invisible, or the child of
|
|
* a minimized/invisible ancestore.
|
|
*/
|
|
if (!TestWF(pwnd, WFMINIMIZED) &&
|
|
(flags & SW_SCROLLCHILDREN) &&
|
|
!fRcScroll) {
|
|
|
|
fInvisible = TRUE;
|
|
flags &= ~SW_INVALIDATE;
|
|
}
|
|
|
|
DoNothing:
|
|
|
|
if (hrgnUpdate) {
|
|
SetEmptyRgn(hrgnUpdate);
|
|
}
|
|
|
|
if (prcUpdate) {
|
|
SetRectEmpty(prcUpdate);
|
|
}
|
|
|
|
if (!fInvisible)
|
|
return NULLREGION;
|
|
}
|
|
|
|
/*
|
|
* Hide the caret.
|
|
*/
|
|
fHideCaret = FALSE;
|
|
|
|
if (!fInvisible) {
|
|
pcaret = &ptiCurrent->pq->caret;
|
|
if (pcaret->spwnd != NULL && _IsDescendant(pcaret->spwnd, pwnd)) {
|
|
fHideCaret = TRUE;
|
|
zzzInternalHideCaret();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If scrollwindow, and window is clipchildren, use a cache entry.
|
|
* Otherwise, always use a
|
|
*
|
|
* Determine what kind of DC we'll be needing. If the DCX_CACHE bit
|
|
* isn't set, it means that we'll be operating in logical coordinates.
|
|
*/
|
|
if (flags & SW_SCROLLWINDOW) {
|
|
|
|
/*
|
|
* ScrollWindow() call: use the cache if not OWNDC or CLASSDC.
|
|
*/
|
|
flagsDCX = DCX_USESTYLE;
|
|
if (!TestCF(pwnd, CFOWNDC) && !TestCF(pwnd, CFCLASSDC))
|
|
flagsDCX |= DCX_CACHE;
|
|
|
|
/*
|
|
* If SW_SCROLLCHILDREN (i.e., lprcScroll == NULL) and CLIPCHILDREN,
|
|
* then use the cache and don't clip children.
|
|
* This is screwy, but 3.0 backward compatible.
|
|
*/
|
|
if ((flags & SW_SCROLLCHILDREN) && TestWF(pwnd, WFCLIPCHILDREN))
|
|
flagsDCX |= DCX_NOCLIPCHILDREN | DCX_CACHE;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* ScrollWindowEx() call: always use the cache
|
|
*/
|
|
flagsDCX = DCX_USESTYLE | DCX_CACHE;
|
|
|
|
/*
|
|
* if SW_SCROLLCHILDREN, always use noclipchildren.
|
|
*/
|
|
if (flags & SW_SCROLLCHILDREN)
|
|
flagsDCX |= DCX_NOCLIPCHILDREN;
|
|
}
|
|
|
|
flagsDCX |= DCX_NOMIRROR;
|
|
hdc = _GetDCEx(pwnd, NULL, flagsDCX);
|
|
|
|
if (flags & SW_INVALIDATE) {
|
|
|
|
/*
|
|
* Get device origin while DC is valid, for later offsetting
|
|
*/
|
|
GetDCOrgOnScreen(hdc, &pt);
|
|
|
|
/*
|
|
* If the user didn't give us a region to use, use ghrgnSW.
|
|
*/
|
|
if (hrgnUpdate == NULL)
|
|
hrgnUpdate = ghrgnSW;
|
|
}
|
|
|
|
/*
|
|
* The DC will be in some logical coordinate system if OWNDC or CLASSDC.
|
|
*/
|
|
if (!fRcScroll) {
|
|
prcScroll = &rcSrc;
|
|
|
|
/*
|
|
* IMPORTANT:
|
|
* We have to use CopyOffsetRect() here because GetClientRect() gives
|
|
* unreliable results for minimized windows. 3.1 dudes get told that
|
|
* their client is non-empty, for compatibility reasons.
|
|
*/
|
|
GetRect(pwnd, &rcSrc, GRECT_CLIENT | GRECT_CLIENTCOORDS);
|
|
|
|
/*
|
|
* If the DC might be a screwy one, then map the
|
|
* rect to logical units.
|
|
*/
|
|
if (!(flagsDCX & DCX_CACHE))
|
|
GreDPtoLP(hdc, (LPPOINT)&rcSrc, 2);
|
|
}
|
|
|
|
/*
|
|
* If the DC is in logical coordinates, map *prcScroll and dx, dy
|
|
* to device units for use later.
|
|
*/
|
|
dxDev = dx;
|
|
dyDev = dy;
|
|
rcSrcDev = *prcScroll;
|
|
|
|
if (!(flagsDCX & DCX_CACHE)) {
|
|
|
|
POINT rgpt[2];
|
|
|
|
GreLPtoDP(hdc, (POINT FAR*)&rcSrcDev, 2);
|
|
|
|
/*
|
|
* The delta values must be treated as a vector from
|
|
* the point (0, 0) to (dx, dy). Scale it as such, then
|
|
* compute the difference. This handles flipped coordinate systems.
|
|
*/
|
|
rgpt[0].x = rgpt[0].y = 0;
|
|
rgpt[1].x = dx;
|
|
rgpt[1].y = dy;
|
|
|
|
GreLPtoDP(hdc, rgpt, 2);
|
|
|
|
dxDev = rgpt[1].x - rgpt[0].x;
|
|
dyDev = rgpt[1].y - rgpt[0].y;
|
|
}
|
|
|
|
if (fInvisible)
|
|
code = NULLREGION;
|
|
else {
|
|
hrgnInvalid = pwnd->hrgnUpdate;
|
|
if ((flags & SW_SCROLLWINDOW) && !TestWF(pwnd, WFWIN31COMPAT)) {
|
|
/*
|
|
* 3.0 Backward compatibility hack:
|
|
* The following incorrect code is what 3.0 used to do, and
|
|
* there are apps such as Finale and Scrapbook+ that have worked
|
|
* around this bug in ways that don't work with the "correct" code.
|
|
*/
|
|
if (pwnd->hrgnUpdate > HRGN_FULL) {
|
|
RECT rc;
|
|
|
|
GreGetRgnBox(pwnd->hrgnUpdate, &rc);
|
|
OffsetRect(&rc,
|
|
dxDev - pwnd->rcClient.left,
|
|
dyDev - pwnd->rcClient.top);
|
|
|
|
xxxRedrawWindow(pwnd,
|
|
&rc, NULL,
|
|
RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
|
|
}
|
|
hrgnInvalid = NULL;
|
|
}
|
|
|
|
code = InternalScrollDC(hdc,
|
|
dx,
|
|
dy,
|
|
prcScroll,
|
|
prcClip,
|
|
hrgnInvalid,
|
|
hrgnUpdate,
|
|
prcUpdate,
|
|
!(flagsDCX & DCX_CACHE));
|
|
if (prcUpdate && TestWF(pwnd, WEFLAYOUTRTL)) {
|
|
MirrorClientRect(pwnd, prcUpdate);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Release the hdc we used.
|
|
*/
|
|
_ReleaseDC(hdc);
|
|
|
|
/*
|
|
* Check the union of the src and dst rectangle against any SPBs.
|
|
* We do this because the window
|
|
* might be completely obscured by some window with an SPB, but
|
|
* since we're completely covered no BitBlt call will be made
|
|
* to accumulate bounds in that area.
|
|
*/
|
|
if (!fInvisible && AnySpbs()) {
|
|
|
|
if (fRcScroll) {
|
|
if (pwnd == PWNDDESKTOP(pwnd)) {
|
|
rcSrc = rcSrcDev;
|
|
} else {
|
|
CopyOffsetRect(
|
|
&rcSrc,
|
|
&rcSrcDev,
|
|
pwnd->rcClient.left,
|
|
pwnd->rcClient.top);
|
|
}
|
|
|
|
rcSpb = rcSrc;
|
|
OffsetRect(&rcSpb, dxDev, dyDev);
|
|
UnionRect(&rcSpb, &rcSpb, &rcSrc);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Use the entire client area.
|
|
*/
|
|
rcSpb = pwnd->rcClient;
|
|
}
|
|
|
|
SpbCheckRect(pwnd, &rcSpb, 0);
|
|
}
|
|
|
|
/*
|
|
* If this guy wants to scroll his children, go at it. Only scroll those
|
|
* children intersecting prcScroll. Then invalidate any vis rgns
|
|
* calculated for these child windows.
|
|
*/
|
|
if (flags & SW_SCROLLCHILDREN) {
|
|
|
|
RECT rc;
|
|
|
|
/*
|
|
* If this window has the caret then offset it if:
|
|
* a) The whole window is scrolling
|
|
* b) The rectangle scrolled contains the caret rectangle
|
|
*/
|
|
if (!fInvisible && (pwnd == pcaret->spwnd)) {
|
|
|
|
if (fRcScroll)
|
|
SetRect(&rc,
|
|
pcaret->x,
|
|
pcaret->y,
|
|
pcaret->x + pcaret->cx,
|
|
pcaret->y + pcaret->cy);
|
|
|
|
if (!fRcScroll || IntersectRect(&rc, &rc, &rcSrcDev)) {
|
|
pcaret->x += dxDev;
|
|
pcaret->y += dyDev;
|
|
}
|
|
}
|
|
|
|
if (fRcScroll) {
|
|
|
|
/*
|
|
* Create a copy of prcScroll and map to absolute coordinates...
|
|
*/
|
|
if (pwnd == PWNDDESKTOP(pwnd)) {
|
|
CopyRect(&rc, &rcSrcDev);
|
|
} else {
|
|
CopyOffsetRect(
|
|
&rc,
|
|
&rcSrcDev,
|
|
pwnd->rcClient.left,
|
|
pwnd->rcClient.top);
|
|
}
|
|
}
|
|
|
|
if (pwnd->spwndChild) {
|
|
|
|
OffsetChildren(pwnd,
|
|
dxDev,
|
|
dyDev,
|
|
(fRcScroll ? (LPRECT)&rc : NULL));
|
|
|
|
/*
|
|
* If we're clipchildren, then shuffling our children
|
|
* will affect our client visrgn (but not our window visrgn).
|
|
* Otherwise, only our children's
|
|
* visrgns were affected by the scroll.
|
|
* No need to DeferWinEventNotify() judging by xxxInternalInvalidate() below
|
|
*/
|
|
zzzInvalidateDCCache(pwnd,
|
|
TestWF(pwnd, WFCLIPCHILDREN) ?
|
|
IDC_CLIENTONLY : IDC_CHILDRENONLY);
|
|
|
|
}
|
|
}
|
|
|
|
if (flags & SW_INVALIDATE) {
|
|
|
|
/*
|
|
* If the caller supplied a region, invalidate using a copy,
|
|
* because InternalInvalidate may trash the passed-in region.
|
|
*/
|
|
if (hrgnUpdate != ghrgnSW)
|
|
CopyRgn(ghrgnSW, hrgnUpdate);
|
|
|
|
/*
|
|
* Make ghrgnSW screen-relative before invalidation...
|
|
*/
|
|
GreOffsetRgn(ghrgnSW, pt.x, pt.y);
|
|
|
|
xxxInternalInvalidate(
|
|
pwnd,
|
|
ghrgnSW,
|
|
(flags & SW_ERASE) ?
|
|
(RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE) :
|
|
(RDW_INVALIDATE | RDW_ALLCHILDREN));
|
|
}
|
|
|
|
/*
|
|
* Send child move messages if needed.
|
|
*/
|
|
if (flags & SW_SCROLLCHILDREN) {
|
|
|
|
PWND pwndChild;
|
|
RECT rc;
|
|
RECT rcScrolledChildren;
|
|
|
|
/*
|
|
* NOTE: the following code will send MOVE messages
|
|
* to windows that didn't move but were in the source rectangle.
|
|
* This is not a big deal, and definitely not worth fixing.
|
|
*/
|
|
if (fRcScroll) {
|
|
if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
CopyOffsetRect(&rcScrolledChildren, &rcSrcDev, dxDev, dyDev);
|
|
} else {
|
|
CopyOffsetRect(
|
|
&rcScrolledChildren,
|
|
&rcSrcDev,
|
|
dxDev + pwnd->spwndParent->rcClient.left,
|
|
dyDev + pwnd->spwndParent->rcClient.top);
|
|
}
|
|
}
|
|
|
|
ThreadLockNever(&tlpwndChild);
|
|
pwndChild = pwnd->spwndChild;
|
|
while (pwndChild != NULL) {
|
|
|
|
if ( !fRcScroll ||
|
|
IntersectRect(&rc, &rcScrolledChildren, &pwndChild->rcWindow)) {
|
|
|
|
/*
|
|
* NOTE: Win 3.0 and below passed wParam == TRUE here.
|
|
* This was not documented or used, so it was changed
|
|
* to be consistent with the documentation.
|
|
*/
|
|
ThreadLockExchangeAlways(pwndChild, &tlpwndChild);
|
|
xxxSendMessage(
|
|
pwndChild,
|
|
WM_MOVE,
|
|
0,
|
|
(pwnd == PWNDDESKTOP(pwnd)) ?
|
|
MAKELONG(pwndChild->rcClient.left, pwndChild->rcClient.top) :
|
|
MAKELONG(pwndChild->rcClient.left - pwnd->rcClient.left,
|
|
pwndChild->rcClient.top - pwnd->rcClient.top));
|
|
}
|
|
|
|
pwndChild = pwndChild->spwndNext;
|
|
}
|
|
|
|
ThreadUnlock(&tlpwndChild);
|
|
}
|
|
|
|
if (fHideCaret) {
|
|
|
|
/*
|
|
* Show the caret again.
|
|
*/
|
|
zzzInternalShowCaret();
|
|
}
|
|
|
|
/*
|
|
* Return the region code.
|
|
*/
|
|
return code;
|
|
}
|
|
|