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.
2054 lines
62 KiB
2054 lines
62 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: minmax.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Window Minimize/Maximize Routines
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
/*
|
|
* How long we want animation to last, in milliseconds
|
|
*/
|
|
#define CMS_ANIMATION 250
|
|
#define DX_GAP (SYSMET(CXMINSPACING) - SYSMET(CXMINIMIZED))
|
|
#define DY_GAP (SYSMET(CYMINSPACING) - SYSMET(CYMINIMIZED))
|
|
|
|
/***************************************************************************\
|
|
* xxxInitSendValidateMinMaxInfo()
|
|
*
|
|
* Routine which initializes the minmax array, sends WM_GETMINMAXINFO to
|
|
* the caller, and validates the results.
|
|
*
|
|
* Returns FALSE is the window went away in the middle.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
void
|
|
xxxInitSendValidateMinMaxInfo(PWND pwnd, LPMINMAXINFO lpmmi)
|
|
{
|
|
PTHREADINFO ptiCurrent;
|
|
PMONITOR pMonitorReal;
|
|
PMONITOR pMonitorPrimary;
|
|
TL tlpMonitorReal;
|
|
TL tlpMonitorPrimary;
|
|
CHECKPOINT * pcp;
|
|
RECT rcParent;
|
|
int cBorders;
|
|
int xMin, yMin;
|
|
BOOL bTopLevel;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* FILL IN THE MINMAXINFO WE THINK IS APPROPRIATE
|
|
*/
|
|
|
|
/*
|
|
* Minimized Size
|
|
*/
|
|
lpmmi->ptReserved.x = SYSMET(CXMINIMIZED);
|
|
lpmmi->ptReserved.y = SYSMET(CYMINIMIZED);
|
|
|
|
/*
|
|
* Maximized Position and Size
|
|
* Figure out where the window would be maximized within its parent.
|
|
*/
|
|
pMonitorPrimary = GetPrimaryMonitor();
|
|
|
|
/*
|
|
* [msadek], #31003
|
|
* Cache window parent status in case some code reparents
|
|
* the window during WM_GETMINMAXINFO
|
|
*/
|
|
if (bTopLevel = (pwnd->spwndParent == PWNDDESKTOP(pwnd))) {
|
|
/* What monitor is the window really going to maximize to? */
|
|
pMonitorReal = _MonitorFromWindow(pwnd, MONITOR_DEFAULTTOPRIMARY);
|
|
|
|
/* Send dimensions based on the primary only. */
|
|
rcParent = pMonitorPrimary->rcMonitor;
|
|
} else {
|
|
pMonitorReal = NULL;
|
|
_GetClientRect(pwnd->spwndParent, &rcParent);
|
|
}
|
|
|
|
cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE);
|
|
|
|
InflateRect(&rcParent,
|
|
cBorders * SYSMET(CXBORDER),
|
|
cBorders * SYSMET(CYBORDER));
|
|
|
|
rcParent.right -= rcParent.left;
|
|
rcParent.bottom -= rcParent.top;
|
|
|
|
/* rcParent.right, bottom are width and height now. */
|
|
lpmmi->ptMaxSize.x = rcParent.right;
|
|
lpmmi->ptMaxSize.y = rcParent.bottom;
|
|
|
|
pcp = (CHECKPOINT *)_GetProp(pwnd, PROP_CHECKPOINT, PROPF_INTERNAL);
|
|
if (pcp && pcp->fMaxInitialized) {
|
|
/*
|
|
* Note: For top level windows, we will fix this point up after
|
|
* the fact if it has gotten out of date because the size border
|
|
* changed.
|
|
*/
|
|
lpmmi->ptMaxPosition = pcp->ptMax;
|
|
} else {
|
|
lpmmi->ptMaxPosition = *((LPPOINT)&rcParent.left);
|
|
}
|
|
|
|
/*
|
|
* Normal minimum tracking size
|
|
* Only enforce min tracking size for windows with captions
|
|
*/
|
|
xMin = cBorders*SYSMET(CXEDGE);
|
|
yMin = cBorders*SYSMET(CYEDGE);
|
|
|
|
if (TestWF(pwnd, WFCAPTION) && !TestWF(pwnd, WEFTOOLWINDOW)) {
|
|
lpmmi->ptMinTrackSize.x = SYSMET(CXMINTRACK);
|
|
lpmmi->ptMinTrackSize.y = SYSMET(CYMINTRACK);
|
|
} else {
|
|
lpmmi->ptMinTrackSize.x = max(SYSMET(CXEDGE), xMin);
|
|
lpmmi->ptMinTrackSize.y = max(SYSMET(CYEDGE), yMin);
|
|
}
|
|
|
|
/*
|
|
* Normal maximum tracking size
|
|
*/
|
|
lpmmi->ptMaxTrackSize.x = SYSMET(CXMAXTRACK);
|
|
lpmmi->ptMaxTrackSize.y = SYSMET(CYMAXTRACK);
|
|
|
|
/*
|
|
* SEND THE WM_GETMINMAXINFO MESSAGE
|
|
*/
|
|
|
|
ThreadLockWithPti(ptiCurrent, pMonitorReal, &tlpMonitorReal);
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pMonitorPrimary, &tlpMonitorPrimary);
|
|
xxxSendMessage(pwnd, WM_GETMINMAXINFO, 0, (LPARAM)lpmmi);
|
|
|
|
/*
|
|
* VALIDATE THE MINMAXINFO
|
|
*/
|
|
|
|
/*
|
|
* Minimized Size (this is read only)
|
|
*/
|
|
lpmmi->ptReserved.x = SYSMET(CXMINIMIZED);
|
|
lpmmi->ptReserved.y = SYSMET(CYMINIMIZED);
|
|
|
|
/*
|
|
* Maximized Postion and Size (only for top level windows)
|
|
*/
|
|
if (bTopLevel) {
|
|
LPRECT lprcRealMax;
|
|
|
|
GetMonitorMaxArea(pwnd, pMonitorReal, &lprcRealMax);
|
|
|
|
/*
|
|
* Is the window a TRUE maximized dude, or somebody like the DOS box
|
|
* who can maximize but not take up the entire screen?
|
|
*
|
|
* Is the window really maximizeable?
|
|
*/
|
|
if ((lpmmi->ptMaxSize.x >= (pMonitorPrimary->rcMonitor.right - pMonitorPrimary->rcMonitor.left)) &&
|
|
(lpmmi->ptMaxSize.y >= (pMonitorPrimary->rcMonitor.bottom - pMonitorPrimary->rcMonitor.top))) {
|
|
|
|
SetWF(pwnd, WFREALLYMAXIMIZABLE);
|
|
|
|
/*
|
|
* Need to reload the checkpoint here, since it might have gotten
|
|
* blown away while we were in the xxxSendMessage call above.
|
|
*/
|
|
pcp = (CHECKPOINT *)_GetProp(pwnd, PROP_CHECKPOINT, PROPF_INTERNAL);
|
|
|
|
if ( pcp &&
|
|
pcp->fMaxInitialized &&
|
|
TestWF(pwnd, WFSIZEBOX) &&
|
|
(lpmmi->ptMaxPosition.x != rcParent.left) &&
|
|
(pcp->ptMax.x == lpmmi->ptMaxPosition.x)) {
|
|
|
|
/*
|
|
* If this window has a weird maximize point that doesn't jibe
|
|
* with what we'd expect and it has a checkpoint, fix up the
|
|
* checkpoint. It means that somebody's WINDOWPLACEMENT
|
|
* got out of date when the size border changed dimensions.
|
|
*/
|
|
pcp->fMaxInitialized = FALSE;
|
|
|
|
lpmmi->ptMaxPosition.y += (rcParent.left - lpmmi->ptMaxPosition.x);
|
|
lpmmi->ptMaxPosition.x = rcParent.left;
|
|
}
|
|
|
|
/*
|
|
* Transfer the maximum size over to the monitor we are REALLY
|
|
* moving to. And fix up guys going fullscreen. A whole bunch
|
|
* of Consumer titles + Word '95 and XL '95 move their caption
|
|
* above the top of the monitor when going fullscreen. Detect
|
|
* these guys now, and let them take up the monitor.
|
|
*/
|
|
if ( lpmmi->ptMaxPosition.y + SYSMET(CYCAPTION) <=
|
|
pMonitorPrimary->rcMonitor.top
|
|
&&
|
|
lpmmi->ptMaxPosition.y + lpmmi->ptMaxSize.y >=
|
|
pMonitorPrimary->rcMonitor.bottom) {
|
|
|
|
lprcRealMax = &pMonitorReal->rcMonitor;
|
|
}
|
|
|
|
/*
|
|
* Compensate for the difference between the primary monitor
|
|
* and the monitor we are actually on.
|
|
*/
|
|
lpmmi->ptMaxSize.x = lpmmi->ptMaxSize.x -
|
|
(pMonitorPrimary->rcMonitor.right - pMonitorPrimary->rcMonitor.left) +
|
|
(lprcRealMax->right - lprcRealMax->left);
|
|
|
|
lpmmi->ptMaxSize.y = lpmmi->ptMaxSize.y -
|
|
(pMonitorPrimary->rcMonitor.bottom - pMonitorPrimary->rcMonitor.top) +
|
|
(lprcRealMax->bottom - lprcRealMax->top);
|
|
} else {
|
|
ClrWF(pwnd, WFREALLYMAXIMIZABLE);
|
|
}
|
|
|
|
/*
|
|
* Now transfer the max position over to the monitor we are REALLY
|
|
* moving to.
|
|
*/
|
|
lpmmi->ptMaxPosition.x += lprcRealMax->left;
|
|
lpmmi->ptMaxPosition.y += lprcRealMax->top;
|
|
}
|
|
|
|
ThreadUnlock(&tlpMonitorPrimary);
|
|
ThreadUnlock(&tlpMonitorReal);
|
|
|
|
/*
|
|
* Normal minimum tracking size.
|
|
*/
|
|
|
|
/*
|
|
* WFCAPTION == WFBORDER | WFDLGFRAME; So, when we want to test for the
|
|
* presence of CAPTION, we must test for both the bits. Otherwise we
|
|
* might mistake WFBORDER or WFDLGFRAME to be a CAPTION.
|
|
*
|
|
*
|
|
* We must not allow a window to be sized smaller than the border
|
|
* thickness -- SANKAR -- 06/12/91 --
|
|
*/
|
|
if (TestWF(pwnd, WFCPRESENT)) {
|
|
|
|
/*
|
|
* NOTE THAT IF YOU CHANGE THE SPACING OF STUFF IN THE CAPTION,
|
|
* YOU NEED TO KEEP THE FOLLOWING IN SSYNC:
|
|
* (1) Default CXMINTRACK, CYMINTRACK in inctlpan.c
|
|
* (2) The default minimum right below
|
|
* (3) Hit testing
|
|
*
|
|
* The minimum size should be space for:
|
|
* * The borders
|
|
* * The buttons
|
|
* * Margins
|
|
* * 4 chars of text
|
|
* * Caption icon
|
|
*/
|
|
yMin = SYSMET(CYMINTRACK);
|
|
|
|
/*
|
|
* Min track size is determined by the number of buttons in
|
|
* the caption.
|
|
*/
|
|
if (TestWF(pwnd, WEFTOOLWINDOW)) {
|
|
|
|
/*
|
|
* Add in space for close button.
|
|
*/
|
|
if (TestWF(pwnd, WFSYSMENU))
|
|
xMin += SYSMET(CXSMSIZE);
|
|
|
|
/*
|
|
* DON'T add in space for 2 characters--breaks
|
|
* MFC toolbar stuff. They want to make vertical undocked
|
|
* toolbars narrower than what that would produce.
|
|
*/
|
|
xMin += (2 * SYSMET(CXEDGE));
|
|
|
|
} else {
|
|
|
|
if (TestWF(pwnd, WFSYSMENU)) {
|
|
|
|
/*
|
|
* Add in space for min/max/close buttons. Otherwise,
|
|
* if it's a contexthelp window, then add in space
|
|
* for help/close buttons.
|
|
*/
|
|
if (TestWF(pwnd, (WFMINBOX | WFMAXBOX)))
|
|
xMin += 3 * SYSMET(CXSIZE);
|
|
else if (TestWF(pwnd, WEFCONTEXTHELP))
|
|
xMin += 2 * SYSMET(CXSIZE);
|
|
|
|
|
|
/*
|
|
* Add in space for system menu icon.
|
|
*/
|
|
if (_HasCaptionIcon(pwnd))
|
|
xMin += SYSMET(CYSIZE);
|
|
}
|
|
|
|
/*
|
|
* Add in space for 4 characters and margins.
|
|
*/
|
|
xMin += 4 * gcxCaptionFontChar + 2 * SYSMET(CXEDGE);
|
|
}
|
|
}
|
|
|
|
lpmmi->ptMinTrackSize.x = max(lpmmi->ptMinTrackSize.x, xMin);
|
|
lpmmi->ptMinTrackSize.y = max(lpmmi->ptMinTrackSize.y, yMin);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* ParkIcon
|
|
*
|
|
* Called when minimizing a window. This parks the minwnd in the position
|
|
* given in the checkpoint or calculates a new position for it.
|
|
*
|
|
* LauraBu 10/15/92
|
|
* We now let the user specify two things that affect parking and arranging:
|
|
* (1) The corner to start arranging from
|
|
* (2) The direction to move in first
|
|
* MCostea 11/13/98 #246397
|
|
* Add sanity check for the number of tries. If the metrics are messed up
|
|
* and pwnd has a lot of siblings, the for-ever loop would make us timeout
|
|
*
|
|
\***************************************************************************/
|
|
|
|
VOID ParkIcon(
|
|
PWND pwnd,
|
|
PCHECKPOINT pcp)
|
|
{
|
|
RECT rcTest;
|
|
RECT rcT;
|
|
UINT xIconPositions;
|
|
UINT xIconT;
|
|
PWND pwndTest;
|
|
PWND pwndParent;
|
|
int xOrg;
|
|
int yOrg;
|
|
int dx;
|
|
int dy;
|
|
int dxSlot;
|
|
int dySlot;
|
|
int iteration;
|
|
BOOL fHorizontal;
|
|
PCHECKPOINT pncp;
|
|
|
|
/*
|
|
* Put these into local vars immediately. The compiler is too dumb to
|
|
* know that we're using a constant offset into a constant address, and
|
|
* thus a resulting constant address.
|
|
*/
|
|
dxSlot = SYSMET(CXMINSPACING);
|
|
dySlot = SYSMET(CYMINSPACING);
|
|
|
|
if (IsTrayWindow(pwnd)) {
|
|
|
|
pcp->fMinInitialized = TRUE;
|
|
pcp->ptMin.x = WHERE_NOONE_CAN_SEE_ME;
|
|
pcp->ptMin.y = WHERE_NOONE_CAN_SEE_ME;
|
|
|
|
return;
|
|
}
|
|
|
|
/* We need to adjust the client rectangle for scrollbars, just like we
|
|
* do in ArrangeIconicWindows(). If one thing is clear, it is that
|
|
* parking and arranging must follow the same principles. This is to
|
|
* avoid the user arranging some windows, creating a new one, and parking
|
|
* it in a place not consistent with the arrangement of the others.
|
|
*/
|
|
pwndParent = pwnd->spwndParent;
|
|
GetRealClientRect(pwndParent, &rcT, GRC_SCROLLS, NULL);
|
|
|
|
/*
|
|
* Get gravity & move vars. We want gaps to start on the sides that
|
|
* we begin arranging from.
|
|
*
|
|
* Horizontal gravity
|
|
*/
|
|
if (SYSMET(ARRANGE) & ARW_STARTRIGHT) {
|
|
|
|
/*
|
|
* Starting on right side
|
|
*/
|
|
rcTest.left = xOrg = rcT.right - dxSlot;
|
|
dx = -dxSlot;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Starting on left
|
|
*/
|
|
rcTest.left = xOrg = rcT.left + DX_GAP;
|
|
dx = dxSlot;
|
|
}
|
|
|
|
/*
|
|
* Vertical gravity
|
|
*/
|
|
if (SYSMET(ARRANGE) & ARW_STARTTOP) {
|
|
|
|
/*
|
|
* Starting on top side
|
|
*/
|
|
rcTest.top = yOrg = rcT.top + DY_GAP;
|
|
dy = dySlot;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Starting on bottom
|
|
*/
|
|
rcTest.top = yOrg = rcT.bottom - dySlot;
|
|
dy = -dySlot;
|
|
}
|
|
|
|
/*
|
|
* Get arrangement direction. Note that ARW_HORIZONTAL is 0, so we
|
|
* can't test for it.
|
|
*/
|
|
fHorizontal = ((SYSMET(ARRANGE) & ARW_DOWN) ? FALSE : TRUE);
|
|
|
|
if (fHorizontal)
|
|
xIconPositions = xIconT = max(1, (rcT.right / dxSlot));
|
|
else
|
|
xIconPositions = xIconT = max(1, (rcT.bottom / dySlot));
|
|
|
|
/*
|
|
* BOGUS
|
|
* LauraBu 10/15/92
|
|
* What happens if the parent is scrolled over horizontally or
|
|
* vertically? Just like when you drop an object...
|
|
*/
|
|
iteration = 0;
|
|
while (iteration < 5000) {
|
|
|
|
/*
|
|
* Make a rectangle representing this position, in screen coords
|
|
*/
|
|
rcTest.right = rcTest.left + dxSlot;
|
|
rcTest.bottom = rcTest.top + dySlot;
|
|
|
|
/*
|
|
* Look for intersections with existing iconic windows
|
|
*/
|
|
for (pwndTest = pwndParent->spwndChild; pwndTest; pwndTest = pwndTest->spwndNext) {
|
|
|
|
if (!TestWF(pwndTest, WFVISIBLE))
|
|
continue;
|
|
|
|
if (pwndTest == pwnd)
|
|
continue;
|
|
|
|
if (!TestWF(pwndTest, WFMINIMIZED)) {
|
|
|
|
/*
|
|
* This is a non-minimized window. See if it has a checkpoint
|
|
* and find out where it would be if it were minimized. We
|
|
* will try not to park an icon in this spot.
|
|
*/
|
|
pncp = (PCHECKPOINT)_GetProp(pwndTest,
|
|
PROP_CHECKPOINT,
|
|
PROPF_INTERNAL);
|
|
|
|
if (!pncp || !pncp->fDragged || !pncp->fMinInitialized)
|
|
continue;
|
|
|
|
/*
|
|
* Get parent coordinates of minimized window pos.
|
|
*/
|
|
rcT.right = rcT.left = pncp->ptMin.x;
|
|
rcT.right += dxSlot;
|
|
rcT.bottom = rcT.top = pncp->ptMin.y;
|
|
rcT.bottom += dySlot;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Get parent coordinates of currently minimized window
|
|
*/
|
|
GetRect(pwndTest, &rcT, GRECT_WINDOW | GRECT_PARENTCOORDS);
|
|
}
|
|
|
|
iteration++;
|
|
/*
|
|
* Get out of loop if they overlap
|
|
*/
|
|
if (IntersectRect(&rcT, &rcT, &rcTest))
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Found a position that doesn't overlap, so get out of search loop
|
|
*/
|
|
if (!pwndTest)
|
|
break;
|
|
|
|
/*
|
|
* Else setup to process the next position
|
|
*/
|
|
if (--xIconT == 0) {
|
|
|
|
/*
|
|
* Setup next pass
|
|
*/
|
|
xIconT = xIconPositions;
|
|
|
|
if (fHorizontal) {
|
|
rcTest.left = xOrg;
|
|
rcTest.top += dy;
|
|
} else {
|
|
rcTest.left += dx;
|
|
rcTest.top = yOrg;
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Same pass.
|
|
*/
|
|
if (fHorizontal)
|
|
rcTest.left += dx;
|
|
else
|
|
rcTest.top += dy;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Note that rcTest is in parent coordinates already.
|
|
*/
|
|
pcp->fMinInitialized = TRUE;
|
|
pcp->ptMin.x = rcTest.left;
|
|
pcp->ptMin.y = rcTest.top;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxAnimateCaption
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
ULONG_PTR SaveScreen(PWND pwnd, ULONG iMode, ULONG_PTR iSave, int x, int y, int cx, int cy)
|
|
{
|
|
RECT rc;
|
|
|
|
/*
|
|
* x and y are in the DC coordinates, make the screen in the
|
|
* (meta hdev) coordinates for the call to Gre/driver.
|
|
*/
|
|
rc.left = x + pwnd->rcWindow.left;
|
|
rc.right = x + cx;
|
|
rc.top = y + pwnd->rcWindow.top;
|
|
rc.bottom = y + cy;
|
|
|
|
if (IntersectRect(&rc, &rc, &gpDispInfo->rcScreen)) {
|
|
return GreSaveScreenBits(gpDispInfo->hDev, iMode, iSave, (RECTL*)&rc);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
VOID xxxAnimateCaption(
|
|
PWND pwnd,
|
|
HDC hdc,
|
|
LPRECT lprcStart,
|
|
LPRECT lprcEnd)
|
|
{
|
|
DWORD dwTimeStart;
|
|
DWORD iTimeElapsed;
|
|
int iLeftStart;
|
|
int iTopStart;
|
|
int cxStart;
|
|
int dLeft;
|
|
int dTop;
|
|
int dcx;
|
|
int iLeft;
|
|
int iTop;
|
|
int cx;
|
|
int iLeftNew;
|
|
int iTopNew;
|
|
int cxNew;
|
|
int cBorders;
|
|
HBITMAP hbmpOld;
|
|
RECT rc;
|
|
int cy;
|
|
HDC hdcMem;
|
|
ULONG_PTR uSave;
|
|
PWND pwndOrg;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
if ((pwndOrg = _WindowFromDC(hdc)) == NULL) {
|
|
RIPMSG0(RIP_WARNING, "SaveScreen: invalid DC passed in");
|
|
return;
|
|
}
|
|
|
|
cy = SYSMET(CYCAPTION) - 1;
|
|
|
|
/*
|
|
* kurtp: 29-Jan-1997
|
|
*
|
|
* We don't do anything when animating the caption,
|
|
* because we couldn't get the desired effect at the
|
|
* client. If we do use it then the
|
|
* cache gets a bunch of bitmaps (size: 2xCaption by CXScreen)
|
|
* that are never re-used. This slows down clients
|
|
* because the GreBitBlts always generate new bitmaps
|
|
* and the cache is displaced by the new bitmaps (yuk!).
|
|
*/
|
|
|
|
if (IsRemoteConnection() || SYSMETBOOL2(SM_REMOTECONTROL))
|
|
return;
|
|
|
|
if ((hdcMem = GreCreateCompatibleDC(ghdcMem)) == NULL)
|
|
return;
|
|
|
|
/*
|
|
* If the caption strip doesn't exist, then attempt to recreate it. This
|
|
* might be necessary if the user does a mode-switch during low memory
|
|
* and is not able to recreate the surface. When the memory becomes
|
|
* available, we'll attempt to recreate it here.
|
|
*/
|
|
if (ghbmCaption == NULL) {
|
|
ghbmCaption = CreateCaptionStrip();
|
|
}
|
|
|
|
hbmpOld = GreSelectBitmap(hdcMem, ghbmCaption);
|
|
|
|
/*
|
|
* initialize start values
|
|
*/
|
|
iTopStart = lprcStart->top;
|
|
iLeftStart = lprcStart->left;
|
|
cxStart = lprcStart->right - iLeftStart;
|
|
|
|
/*
|
|
* initialize delta values to the destination dimensions
|
|
*/
|
|
dLeft = lprcEnd->left;
|
|
dTop = lprcEnd->top;
|
|
dcx = lprcEnd->right - dLeft;
|
|
|
|
/*
|
|
* adjust for window borders as appropriate
|
|
*/
|
|
cBorders = GetWindowBorders(pwnd->style,
|
|
pwnd->ExStyle,
|
|
TRUE,
|
|
FALSE);
|
|
|
|
if ((lprcStart->bottom - iTopStart) > SYSMET(CYCAPTION)) {
|
|
|
|
iLeftStart += cBorders;
|
|
iTopStart += cBorders;
|
|
cxStart -= 2*cBorders;
|
|
}
|
|
|
|
if ((lprcEnd->bottom - dTop) > SYSMET(CYCAPTION)) {
|
|
|
|
dLeft += cBorders;
|
|
dTop += cBorders;
|
|
dcx -= 2*cBorders;
|
|
}
|
|
|
|
/*
|
|
* initialize step values
|
|
*/
|
|
iLeft = iLeftStart;
|
|
iTop = iTopStart;
|
|
cx = cxStart;
|
|
|
|
/*
|
|
* initialize off screen bitmap with caption drawing and first saved rect
|
|
*/
|
|
rc.left = 0;
|
|
rc.top = cy;
|
|
rc.right = max(cxStart, dcx);
|
|
rc.bottom = cy * 2;
|
|
|
|
xxxDrawCaptionTemp(pwnd,
|
|
hdcMem,
|
|
&rc,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
DC_ACTIVE | DC_ICON | DC_TEXT |
|
|
(TestALPHA(GRADIENTCAPTIONS) ? DC_GRADIENT : 0));
|
|
|
|
if ((uSave = SaveScreen(pwndOrg, SS_SAVE, 0,iLeft, iTop, cx, cy)) == 0) {
|
|
if (!GreBitBlt(hdcMem,
|
|
0,
|
|
0,
|
|
cx,
|
|
cy,
|
|
hdc,
|
|
iLeft,
|
|
iTop,
|
|
SRCCOPY,
|
|
0)) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* compute delta values by subtracting source dimensions
|
|
*/
|
|
dLeft -= iLeftStart;
|
|
dTop -= iTopStart;
|
|
dcx -= cxStart;
|
|
|
|
/*
|
|
* blt and time first caption on screen
|
|
* WARNING: If you use *lpSystemTickCount here,
|
|
* the compiler may not generate code to do a DWORD fetch;
|
|
*/
|
|
dwTimeStart = NtGetTickCount();
|
|
GreBitBlt(hdc,
|
|
iLeft,
|
|
iTop,
|
|
cx,
|
|
cy,
|
|
hdcMem,
|
|
0,
|
|
cy,
|
|
SRCCOPY,
|
|
0);
|
|
|
|
iTimeElapsed = (NtGetTickCount() - dwTimeStart);
|
|
|
|
while (LOWORD(iTimeElapsed) <= CMS_ANIMATION) {
|
|
|
|
iLeftNew = iLeftStart + MultDiv(dLeft, LOWORD(iTimeElapsed), CMS_ANIMATION);
|
|
iTopNew = iTopStart + MultDiv(dTop, LOWORD(iTimeElapsed), CMS_ANIMATION);
|
|
cxNew = cxStart + MultDiv(dcx, LOWORD(iTimeElapsed), CMS_ANIMATION);
|
|
|
|
/*
|
|
* Delay before next frame
|
|
*/
|
|
UserSleep(1);
|
|
|
|
/*
|
|
* restore saved rect
|
|
*/
|
|
if (uSave != 0) {
|
|
SaveScreen(pwndOrg, SS_RESTORE, uSave, iLeft, iTop, cx, cy);
|
|
} else {
|
|
GreBitBlt(hdc,
|
|
iLeft,
|
|
iTop,
|
|
cx,
|
|
cy,
|
|
hdcMem,
|
|
0,
|
|
0,
|
|
SRCCOPY,
|
|
0);
|
|
}
|
|
|
|
iLeft = iLeftNew;
|
|
iTop = iTopNew;
|
|
cx = cxNew;
|
|
|
|
/*
|
|
* save new rect offscreen and then draw over it onscreen.
|
|
*/
|
|
if (uSave != 0) {
|
|
uSave = SaveScreen(pwndOrg, SS_SAVE, 0, iLeft, iTop, cx, cy);
|
|
} else {
|
|
GreBitBlt(hdcMem,
|
|
0,
|
|
0,
|
|
cx,
|
|
cy,
|
|
hdc,
|
|
iLeft,
|
|
iTop,
|
|
SRCCOPY,
|
|
0);
|
|
}
|
|
GreBitBlt(hdc,
|
|
iLeft,
|
|
iTop,
|
|
cx,
|
|
cy,
|
|
hdcMem,
|
|
0,
|
|
cy,
|
|
SRCCOPY,
|
|
0);
|
|
|
|
/*
|
|
* update elapsed time
|
|
* WARNING: If you use *lpSystemTickCount here,
|
|
* the compiler may not generate code to do a DWORD fetch;
|
|
*/
|
|
iTimeElapsed = (NtGetTickCount() - dwTimeStart);
|
|
}
|
|
|
|
/*
|
|
* restore saved rect
|
|
*/
|
|
if (uSave != 0) {
|
|
SaveScreen(pwndOrg, SS_RESTORE, uSave, iLeft, iTop, cx, cy);
|
|
} else {
|
|
GreBitBlt(hdc,
|
|
iLeft,
|
|
iTop,
|
|
cx,
|
|
cy,
|
|
hdcMem,
|
|
0,
|
|
0,
|
|
SRCCOPY,
|
|
0);
|
|
}
|
|
|
|
Cleanup:
|
|
GreSelectBitmap(hdcMem, hbmpOld);
|
|
GreDeleteDC(hdcMem);
|
|
}
|
|
|
|
#if 0 // DISABLE OLD ANIMATION FOR M7
|
|
/***************************************************************************\
|
|
* DrawWireFrame
|
|
*
|
|
* Draws wire frame trapezoid
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
VOID DrawWireFrame(
|
|
HDC hdc,
|
|
LPRECT prcFront,
|
|
LPRECT prcBack)
|
|
{
|
|
RECT rcFront;
|
|
RECT rcBack;
|
|
RECT rcT;
|
|
HRGN hrgnSave;
|
|
BOOL fClip;
|
|
|
|
/*
|
|
* Save these locally
|
|
*/
|
|
CopyRect(&rcFront, prcFront);
|
|
CopyRect(&rcBack, prcBack);
|
|
|
|
/*
|
|
* Front face
|
|
*/
|
|
GreMoveTo(hdc, rcFront.left, rcFront.top);
|
|
GreLineTo(hdc, rcFront.left, rcFront.bottom);
|
|
GreLineTo(hdc, rcFront.right, rcFront.bottom);
|
|
GreLineTo(hdc, rcFront.right, rcFront.top);
|
|
GreLineTo(hdc, rcFront.left, rcFront.top);
|
|
|
|
/*
|
|
* Exclude front face from clipping area, only if back face isn't
|
|
* entirely within interior. We need variable because SaveClipRgn()
|
|
* can return NULL.
|
|
*/
|
|
fClip = (EqualRect(&rcFront, &rcBack) ||
|
|
!IntersectRect(&rcT, &rcFront, &rcBack) ||
|
|
!EqualRect(&rcT, &rcBack));
|
|
|
|
if (fClip) {
|
|
|
|
hrgnSave = GreSaveClipRgn(hdc);
|
|
|
|
GreExcludeClipRect(hdc,
|
|
rcFront.left,
|
|
rcFront.top,
|
|
rcFront.right,
|
|
rcFront.bottom);
|
|
}
|
|
|
|
/*
|
|
* Edges
|
|
*/
|
|
GreMoveTo(hdc, rcBack.left, rcBack.top);
|
|
LineTo(hdc, rcFront.left, rcFront.top);
|
|
|
|
GreMoveTo(hdc, rcBack.right, rcBack.top);
|
|
GreLineTo(hdc, rcFront.right, rcFront.top);
|
|
|
|
GreMoveTo(hdc, rcBack.right, rcBack.bottom);
|
|
GreLineTo(hdc, rcFront.right, rcFront.bottom);
|
|
|
|
GreMoveTo(hdc, rcBack.left, rcBack.bottom);
|
|
GreLineTo(hdc, rcFront.left, rcFront.bottom);
|
|
|
|
/*
|
|
* Back face
|
|
*/
|
|
MoveTo(hdc, rcBack.left, rcBack.top);
|
|
LineTo(hdc, rcBack.left, rcBack.bottom);
|
|
LineTo(hdc, rcBack.right, rcBack.bottom);
|
|
LineTo(hdc, rcBack.right, rcBack.top);
|
|
LineTo(hdc, rcBack.left, rcBack.top);
|
|
|
|
if (fClip)
|
|
GreRestoreClipRgn(hdc, hrgnSave);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* AnimateFrame
|
|
*
|
|
* Draws wire frame 3D trapezoid
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
VOID AnimateFrame(
|
|
HDC hdc,
|
|
LPRECT prcStart,
|
|
LPRECT prcEnd,
|
|
BOOL fGrowing)
|
|
{
|
|
RECT rcBack;
|
|
RECT rcFront;
|
|
RECT rcT;
|
|
HPEN hpen;
|
|
int nMode;
|
|
int iTrans;
|
|
int nTrans;
|
|
DWORD dwTimeStart;
|
|
DWORD dwTimeCur;
|
|
|
|
/*
|
|
* Get pen for drawing lines
|
|
*/
|
|
hpen = GreSelectPen(hdc, GetStockObject(WHITE_PEN));
|
|
nMode = GreSetROP2(hdc, R2_XORPEN);
|
|
|
|
/*
|
|
* Save these locally
|
|
*/
|
|
if (fGrowing) {
|
|
|
|
CopyRect(&rcBack, prcStart);
|
|
CopyRect(&rcFront, prcStart);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Initial is trapezoid entire way from small to big. We're going
|
|
* to shrink it from the front face.
|
|
*/
|
|
CopyRect(&rcFront, prcStart);
|
|
CopyRect(&rcBack, prcEnd);
|
|
}
|
|
|
|
/*
|
|
* Offset left & top edges of rects, due to way that lines work.
|
|
*/
|
|
rcFront.left -= 1;
|
|
rcFront.top -= 1;
|
|
rcBack.left -= 1;
|
|
rcBack.top -= 1;
|
|
|
|
/*
|
|
* Get tick count. We'll draw then check how much time elapsed. From
|
|
* that we can calculate how many more transitions to draw. For the first
|
|
* We basically want whole animation to last 3/4 of a second, or 750
|
|
* milliseconds.
|
|
*
|
|
* WARNING: If you use *lpSystemTickCount here,
|
|
* the compiler may not generate code to do a DWORD fetch;
|
|
*/
|
|
dwTimeStart = GetSystemMsecCount();
|
|
|
|
DrawWireFrame(hdc, &rcFront, &rcBack);
|
|
|
|
/*
|
|
* WARNING: If you use *lpSystemTickCount here,
|
|
* the compiler may not generate code to do a DWORD fetch;
|
|
*/
|
|
dwTimeCur = GetSystemMsecCount();
|
|
|
|
/*
|
|
* Get rough estimate for how much time it took.
|
|
*/
|
|
if (dwTimeCur == dwTimeStart)
|
|
nTrans = CMS_ANIMATION / 55;
|
|
else
|
|
nTrans = CMS_ANIMATION / ((int)(dwTimeCur - dwTimeStart));
|
|
|
|
iTrans = 1;
|
|
while (iTrans <= nTrans) {
|
|
|
|
/*
|
|
* Grow the trapezoid out or shrink it in. Fortunately, prcStart
|
|
* and prcEnd are already set up for us.
|
|
*/
|
|
rcT.left = prcStart->left +
|
|
MultDiv(prcEnd->left - prcStart->left, iTrans, nTrans);
|
|
rcT.top = prcStart->top +
|
|
MultDiv(prcEnd->top - prcStart->top, iTrans, nTrans);
|
|
rcT.right = prcStart->right +
|
|
MultDiv(prcEnd->right - prcStart->right, iTrans, nTrans);
|
|
rcT.bottom = prcStart->bottom +
|
|
MultDiv(prcEnd->bottom - prcStart->bottom, iTrans, nTrans);
|
|
|
|
/*
|
|
* Undraw old and draw new
|
|
*/
|
|
DrawWireFrame(hdc, &rcFront, &rcBack);
|
|
CopyRect(&rcFront, &rcT);
|
|
DrawWireFrame(hdc, &rcFront, &rcBack);
|
|
|
|
/*
|
|
* Check the time. How many more transitions left?
|
|
* iTrans / nTrans AS (dwTimeCur-dwTimeStart) / 750
|
|
*
|
|
* WARNING: If you use *lpSystemTickCount here,
|
|
* the compiler may not generate code to do a DWORD fetch;
|
|
*/
|
|
dwTimeCur = GetSystemMsecCount();
|
|
iTrans = MultDiv(nTrans,
|
|
(int)(dwTimeCur - dwTimeStart),
|
|
CMS_ANIMATION);
|
|
}
|
|
|
|
/*
|
|
* Undraw wire frame
|
|
*/
|
|
DrawWireFrame(hdc, &rcFront, &rcBack);
|
|
|
|
/*
|
|
* Clean up
|
|
*/
|
|
GreSetROP2(hdc, nMode);
|
|
hpen = GreSelectPen(hdc, hpen);
|
|
}
|
|
#endif // END DISABLE OLD ANIMATION FOR M7
|
|
|
|
/***************************************************************************\
|
|
* xxxDrawAnimatedRects
|
|
*
|
|
* General routine, like PlaySoundEvent(), that calls other routines for
|
|
* various animation effects. Currently used for changing state from/to
|
|
* minimized.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxDrawAnimatedRects(
|
|
PWND pwndClip,
|
|
int idAnimation,
|
|
LPRECT lprcStart,
|
|
LPRECT lprcEnd)
|
|
{
|
|
HDC hdc;
|
|
POINT rgPt[4];
|
|
RECT rcClip;
|
|
HRGN hrgn;
|
|
PWND pwndAnimate = NULL;
|
|
int iPt;
|
|
|
|
CheckLock(pwndClip);
|
|
|
|
/*
|
|
* Get rects into variables
|
|
*/
|
|
CopyRect((LPRECT)&rgPt[0], lprcStart);
|
|
CopyRect((LPRECT)&rgPt[2], lprcEnd);
|
|
|
|
/*
|
|
* DISABLE OLD ANIMATION FOR M7
|
|
*/
|
|
if (idAnimation != IDANI_CAPTION)
|
|
return TRUE;
|
|
|
|
pwndAnimate = pwndClip;
|
|
if (!pwndAnimate || pwndAnimate == PWNDDESKTOP(pwndAnimate))
|
|
return FALSE;
|
|
|
|
pwndClip = pwndClip->spwndParent;
|
|
if (!pwndClip) {
|
|
RIPMSG0(RIP_WARNING, "xxxDrawAnimatedRects: pwndClip->spwndParent is NULL");
|
|
} else if (pwndClip == PWNDDESKTOP(pwndClip)) {
|
|
pwndClip = NULL;
|
|
}
|
|
|
|
/*
|
|
* NOTE:
|
|
* We do NOT need to do LockWindowUpdate(). We never yield within this
|
|
* function! Anything that was invalid will stay invalid, etc. So our
|
|
* XOR drawing won't leave remnants around.
|
|
*
|
|
* WIN32NT may need to take display critical section or do LWU().
|
|
*
|
|
* Get clipping area
|
|
* Neat feature:
|
|
* NULL window means whole screen, don't clip out children
|
|
* hwndDesktop means working area, don't clip out children
|
|
*/
|
|
if (pwndClip == NULL) {
|
|
pwndClip = _GetDesktopWindow();
|
|
CopyRect(&rcClip, &pwndClip->rcClient);
|
|
if ((hrgn = GreCreateRectRgnIndirect(&rcClip)) == NULL) {
|
|
hrgn = HRGN_FULL;
|
|
}
|
|
|
|
/*
|
|
* Get drawing DC
|
|
*/
|
|
hdc = _GetDCEx(pwndClip,
|
|
hrgn,
|
|
DCX_WINDOW |
|
|
DCX_CACHE |
|
|
DCX_INTERSECTRGN |
|
|
DCX_LOCKWINDOWUPDATE);
|
|
} else {
|
|
|
|
hdc = _GetDCEx(pwndClip,
|
|
HRGN_FULL,
|
|
DCX_WINDOW | DCX_USESTYLE | DCX_INTERSECTRGN);
|
|
|
|
/*
|
|
* We now have a window DC. We need to convert client coords
|
|
* to window coords.
|
|
*/
|
|
for (iPt = 0; iPt < 4; iPt++) {
|
|
|
|
rgPt[iPt].x += (pwndClip->rcClient.left - pwndClip->rcWindow.left);
|
|
rgPt[iPt].y += (pwndClip->rcClient.top - pwndClip->rcWindow.top);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get drawing DC:
|
|
* Unclipped if desktop, clipped otherwise.
|
|
* Note that ReleaseDC() will free the region if needed.
|
|
*/
|
|
if (idAnimation == IDANI_CAPTION) {
|
|
CheckLock(pwndAnimate);
|
|
xxxAnimateCaption(pwndAnimate, hdc, (LPRECT)&rgPt[0], (LPRECT)&rgPt[2]);
|
|
}
|
|
|
|
/*
|
|
* DISABLE OLD ANIMATION FOR M7
|
|
*/
|
|
#if 0
|
|
else {
|
|
AnimateFrame(hdc,
|
|
(LPRECT)&rgPt[0],
|
|
(LPRECT)&rgPt[2],
|
|
(idAnimation == IDANI_OPEN));
|
|
}
|
|
#endif
|
|
/*
|
|
* END DISABLE OLD ANIMATION FOR M7
|
|
*/
|
|
|
|
/*
|
|
* Clean up
|
|
*/
|
|
_ReleaseDC(hdc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* CalcMinZOrder
|
|
*
|
|
*
|
|
* Compute the Z-order of a window to be minimized.
|
|
*
|
|
* The strategy is to find the bottom-most sibling of pwndMinimize that
|
|
* shares the same owner, and insert ourself behind that. We must also
|
|
* take into account that a TOPMOST window should stay among other TOPMOST,
|
|
* and vice versa.
|
|
*
|
|
* We must make sure never to insert after a bottom-most window.
|
|
*
|
|
* This code works for child windows too, since they don't have owners
|
|
* and never have WEFTOPMOST set.
|
|
*
|
|
* If NULL is returned, the window shouldn't be Z-ordered.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
PWND CalcMinZOrder(
|
|
PWND pwndMinimize)
|
|
{
|
|
BYTE bTopmost;
|
|
PWND pwndAfter;
|
|
PWND pwnd;
|
|
|
|
bTopmost = TestWF(pwndMinimize, WEFTOPMOST);
|
|
pwndAfter = NULL;
|
|
|
|
for (pwnd = pwndMinimize->spwndNext; pwnd && !TestWF(pwnd, WFBOTTOMMOST); pwnd = pwnd->spwndNext) {
|
|
|
|
/*
|
|
* If we've enumerated a window that isn't the same topmost-wise
|
|
* as pwndMinimize, we've gone as far as we can.
|
|
*/
|
|
if (TestWF(pwnd, WEFTOPMOST) != bTopmost)
|
|
break;
|
|
|
|
if (pwnd->spwndOwner == pwndMinimize->spwndOwner)
|
|
pwndAfter = pwnd;
|
|
}
|
|
|
|
return pwndAfter;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxActivateOnMinimize
|
|
*
|
|
* Activate the previously active window, provided that window still exists
|
|
* and is a NORMAL window (not bottomost, minimized, disabled, or invisible).
|
|
* If it's not NORMAL, then activate the first non WS_EX_TOPMOST window
|
|
* that's normal. Return TRUE when no activation is needed or the activation
|
|
* has been done in this function. Return FALSE if failed to find a window
|
|
* to activate.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxActivateOnMinimize(PWND pwnd)
|
|
{
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
PWND pwndStart, pwndFirstTool, pwndT;
|
|
BOOL fTryTopmost = TRUE;
|
|
BOOL fPrevCheck = (ptiCurrent->pq->spwndActivePrev != NULL);
|
|
TL tlpwndT;
|
|
|
|
/*
|
|
* We should always have a last-topmost window.
|
|
*/
|
|
pwndStart = GetLastTopMostWindow();
|
|
if (pwndStart) {
|
|
pwndStart = pwndStart->spwndNext;
|
|
} else {
|
|
pwndStart = pwnd->spwndParent->spwndChild;
|
|
}
|
|
|
|
UserAssert(HIBYTE(WFMINIMIZED) == HIBYTE(WFVISIBLE));
|
|
UserAssert(HIBYTE(WFVISIBLE) == HIBYTE(WFDISABLED));
|
|
|
|
SearchAgain:
|
|
|
|
pwndT = (fPrevCheck ? ptiCurrent->pq->spwndActivePrev : pwndStart);
|
|
pwndFirstTool = NULL;
|
|
|
|
/*
|
|
* TryThisWindow must be outside the beginning of the for loop such
|
|
* that pwndT is checked for NULL before being dereferenced.
|
|
*/
|
|
TryThisWindow:
|
|
|
|
for ( ; pwndT ; pwndT = pwndT->spwndNext) {
|
|
/*
|
|
* Use the first nonminimized, visible, nondisabled, and
|
|
* nonbottommost window
|
|
*/
|
|
if (!HMIsMarkDestroy(pwndT) &&
|
|
!TestWF(pwndT, WEFNOACTIVATE) &&
|
|
(TestWF(pwndT, WFVISIBLE | WFDISABLED) == LOBYTE(WFVISIBLE)) &&
|
|
(!TestWF(pwndT, WFMINIMIZED) || GetFullScreen(pwndT) == FULLSCREEN)) {
|
|
|
|
if (TestWF(pwndT, WEFTOOLWINDOW)) {
|
|
if (!pwndFirstTool) {
|
|
pwndFirstTool = pwndT;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fPrevCheck) {
|
|
fPrevCheck = FALSE;
|
|
pwndT = pwndStart;
|
|
goto TryThisWindow;
|
|
}
|
|
}
|
|
|
|
if (!pwndT) {
|
|
|
|
if (fTryTopmost) {
|
|
|
|
fTryTopmost = FALSE;
|
|
if (pwndStart != NULL) {
|
|
pwndStart = pwndStart->spwndParent->spwndChild;
|
|
} else {
|
|
PWND pwndDesktop = _GetDesktopWindow();
|
|
pwndStart = (pwndDesktop != NULL) ? pwndDesktop->spwndChild : NULL;
|
|
}
|
|
goto SearchAgain;
|
|
}
|
|
|
|
pwndT = pwndFirstTool;
|
|
}
|
|
|
|
if (pwndT) {
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwndT, &tlpwndT);
|
|
xxxSetForegroundWindow(pwndT, FALSE);
|
|
ThreadUnlock(&tlpwndT);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxMinMaximize
|
|
*
|
|
* cmd = SW_MINIMIZE, SW_SHOWMINNOACTIVE, SW_SHOWMINIZED,
|
|
* SW_SHOWMAXIMIZED, SW_SHOWNOACTIVE, SW_NORMAL
|
|
*
|
|
* If MINMAX_KEEPHIDDEN is set in dwFlags, keep it hidden, otherwise show it.
|
|
* This is always cleared, except in the case we call it from
|
|
* createwindow(), where the wnd is iconic, but hidden. we
|
|
* need to call this func, to set it up correctly so that when
|
|
* the app shows the wnd, it is displayed correctly.
|
|
*
|
|
* When changing state, we always add on SWP_STATECHANGE. This lets
|
|
* SetWindowPos() know to always send WM_WINDOWPOSCHANGING/CHANGED messages
|
|
* even if the new size is the same as the old size. This is because
|
|
* apps watch the WM_SIZE wParam field to see when they are changing state.
|
|
* If SWP doesn't send WM_WINDOWPOSCHANGED, then they won't get a WM_SIZE
|
|
* message at all.
|
|
*
|
|
* Furthermore, when changing state to/from maximized, if we are really
|
|
* maximizing and are in multiple monitor mode, we want to set the window's
|
|
* region so that it can't draw outside of the monitor. Otherwise, it
|
|
* will spill over onto another. The borders are really annoying.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
PWND xxxMinMaximize(
|
|
PWND pwnd,
|
|
UINT cmd,
|
|
DWORD dwFlags)
|
|
{
|
|
RECT rc;
|
|
RECT rcWindow;
|
|
RECT rcRestore;
|
|
BOOL fShow = FALSE;
|
|
BOOL fSetFocus = FALSE;
|
|
BOOL fShowOwned = FALSE;
|
|
BOOL fSendActivate = FALSE;
|
|
BOOL fMaxStateChanging = FALSE;
|
|
int idAnimation = 0;
|
|
BOOL fFlushPalette = FALSE;
|
|
UINT swpFlags = 0;
|
|
HWND hwndAfter = NULL;
|
|
PWND pwndT;
|
|
PCHECKPOINT pcp;
|
|
PTHREADINFO ptiCurrent;
|
|
TL tlpwndParent;
|
|
TL tlpwndT;
|
|
PSMWP psmwp;
|
|
BOOL fIsTrayWindowNow = FALSE;
|
|
NTSTATUS Status;
|
|
MINMAXINFO mmi;
|
|
UINT uEvent = 0;
|
|
PWND pwndParent = pwnd->spwndParent;
|
|
BOOL bMirroredParent=FALSE;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* Get window rect, in parent client coordinates.
|
|
*/
|
|
GetRect(pwnd, &rcWindow, GRECT_WINDOW | GRECT_PARENTCOORDS);
|
|
|
|
/*
|
|
* If this is NULL, we're out of memory, so punt now.
|
|
*/
|
|
pcp = CkptRestore(pwnd, &rcWindow);
|
|
if (!pcp)
|
|
goto Exit;
|
|
|
|
/*
|
|
* If this top-level window is placed in a mirrored desktop,
|
|
* its coordinates should be mirrored here so that xxxAnimateCaptions
|
|
* works properly, however we shouldn't change the actual screen coordinates
|
|
* of the window. This is why I do it after CkptRestore(...). [samera]
|
|
*/
|
|
if (TestWF(pwndParent,WEFLAYOUTRTL) &&
|
|
(!TestWF(pwnd,WFCHILD))) {
|
|
int iLeft = rcWindow.left;
|
|
rcWindow.left = pwndParent->rcWindow.right - rcWindow.right;
|
|
rcWindow.right = pwndParent->rcWindow.right - iLeft;
|
|
bMirroredParent = TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Save the previous restore size.
|
|
*/
|
|
CopyRect(&rcRestore, &pcp->rcNormal);
|
|
|
|
/*
|
|
* First ask the CBT hook if we can do this operation.
|
|
*/
|
|
if ( IsHooked(PtiCurrent(), WHF_CBT) &&
|
|
xxxCallHook(HCBT_MINMAX, (WPARAM)HWq(pwnd), (DWORD)cmd, WH_CBT)) {
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* If another MDI window is being maximized, and we want to restore this
|
|
* one to its previous state, we can't change the zorder or the
|
|
* activation. We'd mess things up that way. BTW, this SW_ value is
|
|
* internal.
|
|
*/
|
|
if (cmd == SW_MDIRESTORE) {
|
|
|
|
swpFlags |= SWP_NOZORDER | SWP_NOACTIVATE;
|
|
|
|
cmd = (pcp->fWasMinimizedBeforeMaximized ?
|
|
SW_SHOWMINIMIZED : SW_SHOWNORMAL);
|
|
}
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
switch (cmd) {
|
|
case SW_MINIMIZE: // Bottom of zorder, make top-level active
|
|
case SW_SHOWMINNOACTIVE: // Bottom of zorder, don't change activation
|
|
|
|
if (gpqForeground && gpqForeground->spwndActive)
|
|
swpFlags |= SWP_NOACTIVATE;
|
|
|
|
if ((pwndT = CalcMinZOrder(pwnd)) == NULL) {
|
|
swpFlags |= SWP_NOZORDER;
|
|
} else {
|
|
hwndAfter = PtoHq(pwndT);
|
|
}
|
|
|
|
|
|
/*
|
|
* FALL THRU
|
|
*/
|
|
|
|
case SW_SHOWMINIMIZED: // Top of zorder, make active
|
|
|
|
/*
|
|
* Force a show.
|
|
*/
|
|
fShow = TRUE;
|
|
|
|
/*
|
|
* If already minimized, then don't change the existing
|
|
* parking spot.
|
|
*/
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
|
|
/*
|
|
* If we're already minimized and we're properly visible
|
|
* or not visible, don't do anything
|
|
*/
|
|
if (TestWF(pwnd, WFVISIBLE))
|
|
return NULL;
|
|
|
|
swpFlags |= SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE;
|
|
|
|
goto Showit;
|
|
}
|
|
|
|
/*
|
|
* We're becoming minimized although we currently are not. So
|
|
* we want to draw the transition animation, and ALWAYS send
|
|
* sizing messages.
|
|
*/
|
|
idAnimation = IDANI_CLOSE;
|
|
|
|
if (!pcp->fDragged)
|
|
pcp->fMinInitialized = FALSE;
|
|
|
|
if (!pcp->fMinInitialized)
|
|
ParkIcon(pwnd, pcp);
|
|
|
|
rc.left = pcp->ptMin.x;
|
|
rc.top = pcp->ptMin.y;
|
|
rc.right = pcp->ptMin.x + SYSMET(CXMINIMIZED);
|
|
rc.bottom = pcp->ptMin.y + SYSMET(CYMINIMIZED);
|
|
|
|
xxxShowOwnedWindows(pwnd, SW_PARENTCLOSING, NULL);
|
|
|
|
pwndT = ptiCurrent->pq->spwndFocus;
|
|
|
|
while (pwndT) {
|
|
|
|
/*
|
|
* if we or any child has the focus, punt it away
|
|
*/
|
|
if (pwndT != pwnd) {
|
|
pwndT = pwndT->spwndParent;
|
|
continue;
|
|
}
|
|
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwndT, &tlpwndT);
|
|
|
|
if (TestwndChild(pwnd)) {
|
|
|
|
ThreadLockWithPti(ptiCurrent, pwnd->spwndParent, &tlpwndParent);
|
|
xxxSetFocus(pwnd->spwndParent);
|
|
ThreadUnlock(&tlpwndParent);
|
|
|
|
} else {
|
|
xxxSetFocus(NULL);
|
|
}
|
|
|
|
ThreadUnlock(&tlpwndT);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Save the maximized state so that we can restore the window maxed
|
|
*/
|
|
if (TestWF(pwnd, WFMAXIMIZED)) {
|
|
pcp->fWasMaximizedBeforeMinimized = TRUE;
|
|
fMaxStateChanging = TRUE;
|
|
} else{
|
|
pcp->fWasMaximizedBeforeMinimized = FALSE;
|
|
}
|
|
|
|
if (!TestWF(pwnd, WFWIN40COMPAT))
|
|
fIsTrayWindowNow = IsTrayWindow(pwnd);
|
|
|
|
/*
|
|
* Decrement the visible-windows count only if the
|
|
* window is visible. If the window is marked for
|
|
* destruction, we will not decrement for that as
|
|
* well. Let SetMinimize take care of this.
|
|
*/
|
|
SetMinimize(pwnd, SMIN_SET);
|
|
ClrWF(pwnd, WFMAXIMIZED);
|
|
|
|
uEvent = EVENT_SYSTEM_MINIMIZESTART;
|
|
|
|
if (!TestWF(pwnd, WFWIN40COMPAT))
|
|
fIsTrayWindowNow = (fIsTrayWindowNow != IsTrayWindow(pwnd));
|
|
|
|
/*
|
|
* The children of this window are now no longer visible.
|
|
* Ensure that they no longer have any update regions...
|
|
*/
|
|
for (pwndT = pwnd->spwndChild; pwndT; pwndT = pwndT->spwndNext)
|
|
ClrFTrueVis(pwndT);
|
|
|
|
/*
|
|
* B#2919
|
|
* Ensure that the client area gets recomputed, and make
|
|
* sure that no bits are copied when the size is changed. And
|
|
* make sure that WM_SIZE messages get sent, even if our client
|
|
* size is staying the same.
|
|
*/
|
|
swpFlags |= (SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_STATECHANGE);
|
|
|
|
/*
|
|
* We are going minimized, so we want to give palette focus to
|
|
* another app.
|
|
*/
|
|
if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
fFlushPalette = (BOOL)TestWF(pwnd, WFHASPALETTE);
|
|
}
|
|
|
|
break;
|
|
|
|
case SW_SHOWNOACTIVATE:
|
|
if (gpqForeground && gpqForeground->spwndActive)
|
|
swpFlags |= SWP_NOACTIVATE;
|
|
|
|
/*
|
|
* FALL THRU
|
|
*/
|
|
|
|
case SW_RESTORE:
|
|
|
|
/*
|
|
* If restoring a minimized window that was maximized before
|
|
* being minimized, go back to being maximized.
|
|
*/
|
|
if (TestWF(pwnd, WFMINIMIZED) && pcp->fWasMaximizedBeforeMinimized)
|
|
cmd = SW_SHOWMAXIMIZED;
|
|
else
|
|
cmd = SW_NORMAL;
|
|
|
|
/*
|
|
* FALL THRU
|
|
*/
|
|
|
|
case SW_NORMAL:
|
|
case SW_SHOWMAXIMIZED:
|
|
|
|
if (cmd == SW_SHOWMAXIMIZED) {
|
|
|
|
/*
|
|
* If already maximized and visible, we have nothing to do
|
|
* Otherwise, for the DOSbox, still set fMaxStateChanging
|
|
* to TRUE so we recalc the monitor region if need be.
|
|
* That way WinOldAp can change its "changing from maxed to
|
|
* maxed with new bigger font" code to work right.
|
|
*/
|
|
if (TestWF(pwnd, WFMAXIMIZED)) {
|
|
if (TestWF(pwnd, WFVISIBLE)) {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
/*
|
|
* We're changing from normal to maximized, so always
|
|
* send WM_SIZE.
|
|
*/
|
|
swpFlags |= SWP_STATECHANGE;
|
|
}
|
|
fMaxStateChanging = TRUE;
|
|
|
|
/*
|
|
* If calling from CreateWindow, don't let the thing become
|
|
* activated by the SWP call below. Acitvation will happen
|
|
* on the ShowWindow done by CreateWindow or the app.
|
|
*/
|
|
if (dwFlags & MINMAX_KEEPHIDDEN)
|
|
swpFlags |= SWP_NOACTIVATE;
|
|
|
|
/*
|
|
* This is for MDI's auto-restore behaviour (craigc)
|
|
*/
|
|
if (TestWF(pwnd, WFMINIMIZED))
|
|
pcp->fWasMinimizedBeforeMaximized = TRUE;
|
|
|
|
xxxInitSendValidateMinMaxInfo(pwnd, &mmi);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* We're changing state from non-normal to normal. Make
|
|
* sure WM_SIZE gets sents.
|
|
*/
|
|
UserAssert(HIBYTE(WFMINIMIZED) == HIBYTE(WFMAXIMIZED));
|
|
if (TestWF(pwnd, WFMINIMIZED | WFMAXIMIZED)) {
|
|
swpFlags |= SWP_STATECHANGE;
|
|
}
|
|
if (TestWF(pwnd, WFMAXIMIZED)) {
|
|
fMaxStateChanging = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If currently minimized, show windows' popups
|
|
*/
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
|
|
/*
|
|
* Send WM_QUERYOPEN to make sure this guy should unminimize
|
|
*/
|
|
if (!xxxSendMessage(pwnd, WM_QUERYOPEN, 0, 0L))
|
|
return NULL;
|
|
|
|
idAnimation = IDANI_OPEN;
|
|
fShowOwned = TRUE;
|
|
fSetFocus = TRUE;
|
|
|
|
/*
|
|
* JEFFBOG B#2868
|
|
* Condition added before setting fSendActivate prevents
|
|
* WM_ACTIVATE message from reaching a child window. Might
|
|
* be backwards compatibility problems if a pre 3.1 app
|
|
* relies on WM_ACTIVATE reaching a child.
|
|
*/
|
|
if (!TestWF(pwnd, WFCHILD))
|
|
fSendActivate = TRUE;
|
|
|
|
swpFlags |= SWP_NOCOPYBITS;
|
|
} else {
|
|
idAnimation = IDANI_CAPTION;
|
|
}
|
|
|
|
if (cmd == SW_SHOWMAXIMIZED) {
|
|
rc.left = mmi.ptMaxPosition.x;
|
|
rc.top = mmi.ptMaxPosition.y;
|
|
rc.right = rc.left + mmi.ptMaxSize.x;
|
|
rc.bottom = rc.top + mmi.ptMaxSize.y;
|
|
|
|
SetWF(pwnd, WFMAXIMIZED);
|
|
|
|
} else {
|
|
CopyRect(&rc, &rcRestore);
|
|
ClrWF(pwnd, WFMAXIMIZED);
|
|
}
|
|
|
|
/*
|
|
* We do this TestWF again since we left the critical section
|
|
* above and someone might have already 'un-minimized us'.
|
|
*/
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
|
|
if (!TestWF(pwnd, WFWIN40COMPAT))
|
|
fIsTrayWindowNow = IsTrayWindow(pwnd);
|
|
|
|
/*
|
|
* Mark it as minimized and adjust cVisWindows.
|
|
*/
|
|
SetMinimize(pwnd, SMIN_CLEAR);
|
|
|
|
uEvent = EVENT_SYSTEM_MINIMIZEEND;
|
|
|
|
/*
|
|
* if we're unminimizing a window that is now
|
|
* not seen in maximized/restore mode then remove him
|
|
* from the tray
|
|
*/
|
|
if (!TestWF(pwnd, WFWIN40COMPAT) &&
|
|
(fIsTrayWindowNow != IsTrayWindow(pwnd)) &&
|
|
FDoTray()) {
|
|
|
|
HWND hw = HWq(pwnd);
|
|
|
|
if (FCallHookTray()) {
|
|
xxxCallHook(HSHELL_WINDOWDESTROYED,
|
|
(WPARAM)hw,
|
|
(LPARAM)0,
|
|
WH_SHELL);
|
|
}
|
|
|
|
/*
|
|
* NT specific code. Post the window-destroyed message
|
|
* to the shell.
|
|
*/
|
|
if (FPostTray(pwnd->head.rpdesk))
|
|
PostShellHookMessages(HSHELL_WINDOWDESTROYED, (LPARAM)hw);
|
|
}
|
|
|
|
fIsTrayWindowNow = FALSE;
|
|
|
|
/*
|
|
* If we're un-minimizing a visible top-level window, cVisWindows
|
|
* was zero, and we're either activating a window or showing
|
|
* the currently active window, set ourselves into the
|
|
* foreground. If the window isn't currently visible
|
|
* then we can rely on SetWindowPos() to do the right
|
|
* thing for us.
|
|
*/
|
|
if (!TestwndChild(pwnd) &&
|
|
TestWF(pwnd, WFVISIBLE) &&
|
|
(GETPTI(pwnd)->cVisWindows == 1) &&
|
|
(GETPTI(pwnd)->pq != gpqForeground) &&
|
|
(!(swpFlags & SWP_NOACTIVATE)
|
|
|| (GETPTI(pwnd)->pq->spwndActive == pwnd))) {
|
|
|
|
xxxSetForegroundWindow2(pwnd, GETPTI(pwnd), SFW_STARTUP);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Ensure that client area gets recomputed, and that
|
|
* the frame gets redrawn to reflect the new state.
|
|
*/
|
|
swpFlags |= SWP_DRAWFRAME;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* For the iconic case, we need to also show the window because it
|
|
* might not be visible yet.
|
|
*/
|
|
|
|
Showit:
|
|
|
|
if (!(dwFlags & MINMAX_KEEPHIDDEN)) {
|
|
|
|
if (TestWF(pwnd, WFVISIBLE)) {
|
|
|
|
if (fShow)
|
|
swpFlags |= SWP_SHOWWINDOW;
|
|
|
|
/* if we're full screening a DOS BOX then don't draw
|
|
* the animation 'cause it looks bad.
|
|
* overloaded WFFULLSCREEN bit for MDI child windows --
|
|
* use it to indicate to not animate size change.
|
|
*/
|
|
if (IsVisible(pwnd) &&
|
|
(dwFlags & MINMAX_ANIMATE) &&
|
|
idAnimation &&
|
|
(!TestWF(pwnd, WFCHILD) || !TestWF(pwnd, WFNOANIMATE))) {
|
|
|
|
/*
|
|
* If this top-level window is placed in a mirrored desktop,
|
|
* its coordinates should be mirrored here so that xxxAnimateCaptions
|
|
* works properly, however we shouldn't change the actual screen coordinates
|
|
* of the window. This is why I do it here and restore it afterwards before
|
|
* doing the _DeferWindowPos(...). [samera]
|
|
*/
|
|
RECT rcT;
|
|
if (bMirroredParent) {
|
|
int iLeft = rc.left;
|
|
rcT = rc;
|
|
rc.left = pwndParent->rcWindow.right - rc.right;
|
|
rc.right = pwndParent->rcWindow.right - iLeft;
|
|
}
|
|
|
|
if ((idAnimation != IDANI_CAPTION) && IsTrayWindow(pwnd)) {
|
|
|
|
RECT rcMin;
|
|
|
|
SetRectEmpty(&rcMin);
|
|
#if 0 // Win95 call.
|
|
CallHook(HSHELL_GETMINRECT, (WPARAM)HW16(hwnd), (LPARAM)(LPRECT)&rcMin, WH_SHELL);
|
|
#else
|
|
xxxSendMinRectMessages(pwnd, &rcMin);
|
|
#endif
|
|
|
|
if (!IsRectEmpty(&rcMin)) {
|
|
|
|
if (idAnimation == IDANI_CLOSE) {
|
|
|
|
xxxDrawAnimatedRects(pwnd,
|
|
IDANI_CAPTION,
|
|
&rcWindow,
|
|
&rcMin);
|
|
|
|
} else {
|
|
|
|
xxxDrawAnimatedRects(pwnd,
|
|
IDANI_CAPTION,
|
|
&rcMin,
|
|
&rc);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
xxxDrawAnimatedRects(pwnd, IDANI_CAPTION, &rcWindow, &rc);
|
|
}
|
|
/*
|
|
* Restore the original rect, after doing the animation
|
|
*/
|
|
if (bMirroredParent) {
|
|
rc = rcT;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
swpFlags |= SWP_SHOWWINDOW;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* hack for VB - we add their window in when their minimizing.
|
|
*/
|
|
if (!TestWF(pwnd, WFWIN40COMPAT) && fIsTrayWindowNow && FDoTray()) {
|
|
|
|
HWND hw = HWq(pwnd);
|
|
|
|
if (FCallHookTray()) {
|
|
xxxCallHook(HSHELL_WINDOWCREATED,
|
|
(WPARAM)hw,
|
|
(LPARAM)0,
|
|
WH_SHELL);
|
|
}
|
|
|
|
/*
|
|
* NT specific code. Post the window-created message
|
|
* to the shell.
|
|
*/
|
|
if (FPostTray(pwnd->head.rpdesk))
|
|
PostShellHookMessages(HSHELL_WINDOWCREATED, (LPARAM)hw);
|
|
}
|
|
|
|
/*
|
|
* BACKWARD COMPATIBILITY HACK:
|
|
*
|
|
* Because SetWindowPos() won't honor sizing, moving and SWP_SHOWWINDOW
|
|
* at the same time in version 3.0 or below, we call DeferWindowPos()
|
|
* directly here.
|
|
*/
|
|
if (psmwp = InternalBeginDeferWindowPos(1)) {
|
|
|
|
psmwp = _DeferWindowPos(psmwp,
|
|
pwnd,
|
|
((hwndAfter != NULL) ? RevalidateHwnd(hwndAfter) : NULL),
|
|
rc.left, rc.top,
|
|
rc.right - rc.left,
|
|
rc.bottom - rc.top,
|
|
swpFlags);
|
|
|
|
if (psmwp) {
|
|
|
|
/*
|
|
* HACK FOR MULTIPLE MONITOR TRUE MAXIMIZATION CLIPPING
|
|
* On a multiple monitor system, we would like the
|
|
* borders not to spill over onto another monitor when a
|
|
* window 'really' maximizes. The only way to get this
|
|
* to work right is to set a rectangular region, namely
|
|
* a copy of the monitor region, on the window. We can
|
|
* only do this if the window isn't currently regional.
|
|
*
|
|
* Going to maximized: Add the monitor region
|
|
* Coming from maximized: Remove the monitor region
|
|
*/
|
|
if (fMaxStateChanging && gpDispInfo->cMonitors > 1) {
|
|
if ( TestWF(pwnd, WFMAXIMIZED) &&
|
|
pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
|
|
psmwp->acvr[0].hrgnClip = HRGN_MONITOR;
|
|
|
|
} else if (TestWF(pwnd, WFMAXFAKEREGIONAL)) {
|
|
UserAssert(pwnd->hrgnClip);
|
|
psmwp->acvr[0].hrgnClip = HRGN_FULL;
|
|
}
|
|
}
|
|
|
|
xxxEndDeferWindowPosEx(psmwp, FALSE);
|
|
}
|
|
}
|
|
|
|
if (uEvent) {
|
|
xxxWindowEvent(uEvent, pwnd, OBJID_WINDOW, 0, WEF_USEPWNDTHREAD);
|
|
}
|
|
|
|
/*
|
|
* COMPATIBILITY HACK:
|
|
* Borland's OBEX expects a WM_PAINT message when it starts running
|
|
* minimized and initializes all it's data during that message.
|
|
* So, we generate a bogus WM_PAINT message here.
|
|
* Also, Visionware's XServer can not handle getting a WM_PAINT msg, as it
|
|
* would always get a WM_PAINTICON msg in 3.1, so make sure the logic is here
|
|
* to generate the correct message.
|
|
*/
|
|
if((cmd == SW_SHOWMINIMIZED) &&
|
|
(!TestWF(pwnd, WFWIN40COMPAT)) &&
|
|
TestWF(pwnd, WFVISIBLE) &&
|
|
TestWF(pwnd, WFTOPLEVEL)) {
|
|
|
|
if (pwnd->pcls->spicn)
|
|
_PostMessage(pwnd, WM_PAINTICON, (WPARAM)TRUE, 0L);
|
|
else
|
|
_PostMessage(pwnd, WM_PAINT, 0, 0L);
|
|
}
|
|
|
|
if (fShowOwned)
|
|
xxxShowOwnedWindows(pwnd, SW_PARENTOPENING, NULL);
|
|
|
|
if ((cmd == SW_MINIMIZE) && (pwnd->spwndParent == PWNDDESKTOP(pwnd))) {
|
|
if (!xxxActivateOnMinimize(pwnd)) {
|
|
xxxActivateWindow(pwnd, AW_SKIP);
|
|
}
|
|
|
|
{
|
|
PEPROCESS p;
|
|
|
|
if (gptiForeground && ptiCurrent->ppi != gptiForeground->ppi && !(ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD)) {
|
|
|
|
p = PsGetThreadProcess(ptiCurrent->pEThread);
|
|
KeAttachProcess(PsGetProcessPcb(p));
|
|
Status = MmAdjustWorkingSetSize((SIZE_T)-1,
|
|
(SIZE_T)-1,
|
|
FALSE,
|
|
TRUE);
|
|
KeDetachProcess();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_ERROR, "Error adjusting working set, status = %x\n", Status);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If any app is starting, restore its right to foreground activate
|
|
* (activate and come on top of everything else) because we just
|
|
* minimized what we were working on.
|
|
*/
|
|
RestoreForegroundActivate();
|
|
}
|
|
|
|
/*
|
|
* If going from iconic, insure the focus is in the window.
|
|
*/
|
|
if (fSetFocus)
|
|
xxxSetFocus(pwnd);
|
|
|
|
/*
|
|
* This was added for 1.03 compatibility reasons. If apps watch
|
|
* WM_ACTIVATE to set their focus, sending this message will appear
|
|
* as if the window just got activated (like in 1.03). Before this
|
|
* was added, opening an iconic window never sent this message since
|
|
* it was already active (but HIWORD(lParam) != 0).
|
|
*/
|
|
if (fSendActivate)
|
|
xxxSendMessage(pwnd, WM_ACTIVATE, WA_ACTIVE, 0);
|
|
|
|
/*
|
|
* Flush the palette. We do this on a minimize of a palette app.
|
|
*/
|
|
if (fFlushPalette)
|
|
xxxFlushPalette(pwnd);
|
|
|
|
Exit:
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMinimizeHungWindow
|
|
*
|
|
* 10/31/96 vadimg created
|
|
\***************************************************************************/
|
|
|
|
void xxxMinimizeHungWindow(PWND pwnd)
|
|
{
|
|
RECT rcMin;
|
|
HRGN hrgnHung;
|
|
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* If the window is already minimized or not visible don't do anything.
|
|
*/
|
|
if (TestWF(pwnd, WFMINIMIZED) || !TestWF(pwnd, WFVISIBLE))
|
|
return;
|
|
|
|
/*
|
|
* Animate the caption to the minimized state.
|
|
*/
|
|
if (TEST_PUDF(PUDF_ANIMATE)) {
|
|
SetRectEmpty(&rcMin);
|
|
xxxSendMinRectMessages(pwnd, &rcMin);
|
|
if (!IsRectEmpty(&rcMin)) {
|
|
xxxDrawAnimatedRects(pwnd, IDANI_CAPTION, &pwnd->rcWindow, &rcMin);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reset the visible bit on the window itself and ownees. At the same
|
|
* time calculate how much needs to be repainted. We must invalidate
|
|
* the DC cache to make sure that the visible regions get recalculated.
|
|
*/
|
|
SetVisible(pwnd, SV_UNSET);
|
|
hrgnHung = GreCreateRectRgnIndirect(&pwnd->rcWindow);
|
|
xxxShowOwnedWindows(pwnd, SW_PARENTCLOSING, hrgnHung);
|
|
zzzInvalidateDCCache(pwnd, IDC_DEFAULT);
|
|
xxxRedrawWindow(NULL, NULL, hrgnHung, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
|
|
GreDeleteObject(hrgnHung);
|
|
|
|
/*
|
|
* Deal with activating some other window for top-level windows.
|
|
*/
|
|
if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
xxxActivateOnMinimize(pwnd);
|
|
}
|
|
PostEventMessage(GETPTI(pwnd), GETPTI(pwnd)->pq, QEVENT_HUNGTHREAD, pwnd, 0, 0, 0);
|
|
}
|