Leaked source code of windows server 2003
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

/****************************** 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;
}