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