reactos/win32ss/user/ntuser/nonclient.c
Whindmar Saksit f3d03760e9
[NTUSER] Rewrite Window Snap handling (#5705)
Fixes many Window Snap related bugs and uses the WS_EX2_VERTICALLYMAXIMIZED* styles to remember which edge it is snapped to.

The most significant change is that GetWindowPlacement lies about the normal position when it is snapped, just like Windows.

CORE-19160 CORE-19165 CORE-19166
2024-08-11 20:21:58 +02:00

2175 lines
72 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Win32k subsystem
* PURPOSE: Miscellaneous User functions
* FILE: win32ss/user/ntuser/nonclient.c
* PROGRAMER:
*/
#include <win32k.h>
#include <windowsx.h>
DBG_DEFAULT_CHANNEL(UserDefwnd);
#define UserHasDlgFrameStyle(Style, ExStyle) \
(((ExStyle) & WS_EX_DLGMODALFRAME) || \
(((Style) & WS_DLGFRAME) && (!((Style) & WS_THICKFRAME))))
#define UserHasThickFrameStyle(Style, ExStyle) \
(((Style) & WS_THICKFRAME) && \
(!(((Style) & (WS_DLGFRAME | WS_BORDER)) == WS_DLGFRAME)))
#define UserHasThinFrameStyle(Style, ExStyle) \
(((Style) & WS_BORDER) || (!((Style) & (WS_CHILD | WS_POPUP))))
#define ON_LEFT_BORDER(hit) \
(((hit) == HTLEFT) || ((hit) == HTTOPLEFT) || ((hit) == HTBOTTOMLEFT))
#define ON_RIGHT_BORDER(hit) \
(((hit) == HTRIGHT) || ((hit) == HTTOPRIGHT) || ((hit) == HTBOTTOMRIGHT))
#define ON_TOP_BORDER(hit) \
(((hit) == HTTOP) || ((hit) == HTTOPLEFT) || ((hit) == HTTOPRIGHT))
#define ON_BOTTOM_BORDER(hit) \
(((hit) == HTBOTTOM) || ((hit) == HTBOTTOMLEFT) || ((hit) == HTBOTTOMRIGHT))
#define HASSIZEGRIP(Style, ExStyle, ParentStyle, WindowRect, ParentClientRect) \
((!(Style & WS_CHILD) && (Style & WS_THICKFRAME) && !(Style & WS_MAXIMIZE)) || \
((Style & WS_CHILD) && (ParentStyle & WS_THICKFRAME) && !(ParentStyle & WS_MAXIMIZE) && \
(WindowRect.right - WindowRect.left == ParentClientRect.right) && \
(WindowRect.bottom - WindowRect.top == ParentClientRect.bottom)))
VOID FASTCALL
UserDrawWindowFrame(HDC hdc,
RECTL *rect,
ULONG width,
ULONG height)
{
HBRUSH hbrush = NtGdiSelectBrush( hdc, gpsi->hbrGray );
NtGdiPatBlt( hdc, rect->left, rect->top, rect->right - rect->left - width, height, PATINVERT );
NtGdiPatBlt( hdc, rect->left, rect->top + height, width, rect->bottom - rect->top - height, PATINVERT );
NtGdiPatBlt( hdc, rect->left + width, rect->bottom, rect->right - rect->left - width, -(LONG)height, PATINVERT );
NtGdiPatBlt( hdc, rect->right, rect->top, -(LONG)width, rect->bottom - rect->top - height, PATINVERT );
NtGdiSelectBrush( hdc, hbrush );
}
VOID FASTCALL
UserDrawMovingFrame(HDC hdc,
RECTL *rect,
BOOL thickframe)
{
if (thickframe) UserDrawWindowFrame(hdc, rect, UserGetSystemMetrics(SM_CXFRAME), UserGetSystemMetrics(SM_CYFRAME));
else UserDrawWindowFrame(hdc, rect, 1, 1);
}
/***********************************************************************
* NC_GetInsideRect
*
* Get the 'inside' rectangle of a window, i.e. the whole window rectangle
* but without the borders (if any).
*/
void FASTCALL // Previously known as "UserGetInsideRectNC"
NC_GetInsideRect(PWND Wnd, RECT *rect)
{
ULONG Style;
ULONG ExStyle;
Style = Wnd->style;
ExStyle = Wnd->ExStyle;
rect->top = rect->left = 0;
rect->right = Wnd->rcWindow.right - Wnd->rcWindow.left;
rect->bottom = Wnd->rcWindow.bottom - Wnd->rcWindow.top;
if (Style & WS_ICONIC) return;
/* Remove frame from rectangle */
if (UserHasThickFrameStyle(Style, ExStyle))
{
RECTL_vInflateRect(rect, -UserGetSystemMetrics(SM_CXFRAME), -UserGetSystemMetrics(SM_CYFRAME));
}
else if (UserHasDlgFrameStyle(Style, ExStyle))
{
RECTL_vInflateRect(rect, -UserGetSystemMetrics(SM_CXDLGFRAME), -UserGetSystemMetrics(SM_CYDLGFRAME));
/* FIXME: this isn't in NC_AdjustRect? why not? */
if (ExStyle & WS_EX_DLGMODALFRAME)
RECTL_vInflateRect( rect, -1, 0 );
}
else if (UserHasThinFrameStyle(Style, ExStyle))
{
RECTL_vInflateRect(rect, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER));
}
/* We have additional border information if the window
* is a child (but not an MDI child) */
if ((Style & WS_CHILD) && !(ExStyle & WS_EX_MDICHILD))
{
if (ExStyle & WS_EX_CLIENTEDGE)
RECTL_vInflateRect (rect, -UserGetSystemMetrics(SM_CXEDGE), -UserGetSystemMetrics(SM_CYEDGE));
if (ExStyle & WS_EX_STATICEDGE)
RECTL_vInflateRect (rect, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER));
}
}
/***********************************************************************
* NC_GetSysPopupPos
*/
void FASTCALL
NC_GetSysPopupPos(PWND Wnd, RECT *Rect)
{
RECT WindowRect;
if ((Wnd->style & WS_MINIMIZE) != 0)
{
IntGetWindowRect(Wnd, Rect);
}
else
{
NC_GetInsideRect(Wnd, Rect);
IntGetWindowRect(Wnd, &WindowRect);
RECTL_vOffsetRect(Rect, WindowRect.left, WindowRect.top);
if (Wnd->style & WS_CHILD)
{
IntClientToScreen(IntGetParent(Wnd), (POINT *) Rect);
}
Rect->right = Rect->left + UserGetSystemMetrics(SM_CYCAPTION) - 1;
Rect->bottom = Rect->top + UserGetSystemMetrics(SM_CYCAPTION) - 1;
}
}
static UINT
GetSnapActivationPoint(PWND Wnd, POINT pt)
{
RECT wa;
UserSystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0); /* FIXME: MultiMon of PWND */
if (pt.x <= wa.left) return HTLEFT;
if (pt.x >= wa.right-1) return HTRIGHT;
if (pt.y <= wa.top) return HTTOP; /* Maximize */
return HTNOWHERE;
}
LONG FASTCALL
DefWndStartSizeMove(PWND Wnd, WPARAM wParam, POINT *capturePoint)
{
LONG hittest = 0;
POINT pt;
MSG msg;
RECT rectWindow;
ULONG Style = Wnd->style;
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
rectWindow = Wnd->rcWindow;
if ((wParam & 0xfff0) == SC_MOVE)
{
/* Move pointer at the center of the caption */
RECT rect = rectWindow;
/* Note: to be exactly centered we should take the different types
* of border into account, but it shouldn't make more than a few pixels
* of difference so let's not bother with that */
if (Style & WS_SYSMENU)
rect.left += UserGetSystemMetrics(SM_CXSIZE) + 1;
if (Style & WS_MINIMIZEBOX)
rect.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
if (Style & WS_MAXIMIZEBOX)
rect.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
pt.x = (rect.right + rect.left) / 2;
pt.y = rect.top + UserGetSystemMetrics(SM_CYSIZE)/2;
hittest = HTCAPTION;
*capturePoint = pt;
}
else /* SC_SIZE */
{
pt.x = pt.y = 0;
while (!hittest)
{
if (!co_IntGetPeekMessage(&msg, 0, 0, 0, PM_REMOVE, TRUE)) return 0;
if (IntCallMsgFilter( &msg, MSGF_SIZE )) continue;
switch(msg.message)
{
case WM_MOUSEMOVE:
//// Clamp the mouse position to the window rectangle when starting a window resize.
pt.x = min( max( msg.pt.x, rectWindow.left ), rectWindow.right - 1 );
pt.y = min( max( msg.pt.y, rectWindow.top ), rectWindow.bottom - 1 );
hittest = GetNCHitEx(Wnd, pt);
if ((hittest < HTLEFT) || (hittest > HTBOTTOMRIGHT)) hittest = 0;
break;
case WM_LBUTTONUP:
return 0;
case WM_KEYDOWN:
switch (msg.wParam)
{
case VK_UP:
hittest = HTTOP;
pt.x = (rectWindow.left+rectWindow.right)/2;
pt.y = rectWindow.top + UserGetSystemMetrics(SM_CYFRAME) / 2;
break;
case VK_DOWN:
hittest = HTBOTTOM;
pt.x = (rectWindow.left+rectWindow.right)/2;
pt.y = rectWindow.bottom - UserGetSystemMetrics(SM_CYFRAME) / 2;
break;
case VK_LEFT:
hittest = HTLEFT;
pt.x = rectWindow.left + UserGetSystemMetrics(SM_CXFRAME) / 2;
pt.y = (rectWindow.top+rectWindow.bottom)/2;
break;
case VK_RIGHT:
hittest = HTRIGHT;
pt.x = rectWindow.right - UserGetSystemMetrics(SM_CXFRAME) / 2;
pt.y = (rectWindow.top+rectWindow.bottom)/2;
break;
case VK_RETURN:
case VK_ESCAPE:
return 0;
}
break;
default:
IntTranslateKbdMessage( &msg, 0 );
pti->TIF_flags |= TIF_MOVESIZETRACKING;
IntDispatchMessage( &msg );
pti->TIF_flags |= TIF_MOVESIZETRACKING;
break;
}
}
*capturePoint = pt;
}
UserSetCursorPos(pt.x, pt.y, 0, 0, FALSE);
co_IntSendMessage(UserHMGetHandle(Wnd), WM_SETCURSOR, (WPARAM)UserHMGetHandle(Wnd), MAKELONG(hittest, WM_MOUSEMOVE));
return hittest;
}
//
// System Command Size and Move
//
// Perform SC_MOVE and SC_SIZE commands.
//
VOID FASTCALL
DefWndDoSizeMove(PWND pwnd, WORD wParam)
{
MSG msg;
RECT sizingRect, mouseRect, origRect, unmodRect, snapPreviewRect;
PRECT pFrameRect = &sizingRect;
HDC hdc;
LONG hittest = (LONG)(wParam & 0x0f);
PCURICON_OBJECT DragCursor = NULL, OldCursor = NULL;
POINT minTrack, maxTrack;
POINT capturePoint, pt;
ULONG Style, ExStyle;
UINT orgSnap = IntGetWindowSnapEdge(pwnd), snap = orgSnap;
BOOL thickframe;
BOOL iconic;
BOOL moved = FALSE;
BOOL DragFullWindows = FALSE;
PWND pWndParent = NULL;
WPARAM syscommand = (wParam & 0xfff0);
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
//PMONITOR mon = 0; Don't port sync from wine!!! This breaks explorer task bar sizing!!
// The task bar can grow in size and can not reduce due to the change
// in the work area.
Style = pwnd->style;
ExStyle = pwnd->ExStyle;
iconic = (Style & WS_MINIMIZE) != 0;
if (((Style & WS_MAXIMIZE) && syscommand != SC_MOVE) || !IntIsWindowVisible(pwnd)) return;
if ((Style & (WS_MAXIMIZE | WS_CHILD)) == WS_MAXIMIZE)
orgSnap = snap = HTTOP;
thickframe = UserHasThickFrameStyle(Style, ExStyle) && !iconic;
//
// Show window contents while dragging the window, get flag from registry data.
//
UserSystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &DragFullWindows, 0);
pt.x = pti->ptLast.x;
pt.y = pti->ptLast.y;
capturePoint = pt;
UserClipCursor( NULL );
TRACE("pwnd %p command %04lx, hittest %d, pos %d,%d\n",
pwnd, syscommand, hittest, pt.x, pt.y);
if (syscommand == SC_MOVE)
{
if (!hittest) hittest = DefWndStartSizeMove(pwnd, wParam, &capturePoint);
if (!hittest) return;
}
else /* SC_SIZE */
{
if (!thickframe) return;
if (hittest && (syscommand != SC_MOUSEMENU))
{
hittest += (HTLEFT - WMSZ_LEFT);
}
else
{
co_UserSetCapture(UserHMGetHandle(pwnd));
hittest = DefWndStartSizeMove(pwnd, wParam, &capturePoint);
if (!hittest)
{
IntReleaseCapture();
return;
}
}
}
/* Get min/max info */
co_WinPosGetMinMaxInfo(pwnd, NULL, NULL, &minTrack, &maxTrack);
sizingRect = pwnd->rcWindow;
origRect = sizingRect;
if (Style & WS_CHILD)
{
pWndParent = IntGetParent(pwnd);
IntGetClientRect( pWndParent, &mouseRect );
IntMapWindowPoints( pWndParent, 0, (LPPOINT)&mouseRect, 2 );
IntMapWindowPoints( 0, pWndParent, (LPPOINT)&sizingRect, 2 );
unmodRect = sizingRect;
}
else
{
if (!(ExStyle & WS_EX_TOPMOST))
{
UserSystemParametersInfo(SPI_GETWORKAREA, 0, &mouseRect, 0);
}
else
{
RECTL_vSetRect(&mouseRect, 0, 0, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN));
}
unmodRect = sizingRect;
}
if (ON_LEFT_BORDER(hittest))
{
mouseRect.left = max( mouseRect.left, sizingRect.right-maxTrack.x+capturePoint.x-sizingRect.left );
mouseRect.right = min( mouseRect.right, sizingRect.right-minTrack.x+capturePoint.x-sizingRect.left );
}
else if (ON_RIGHT_BORDER(hittest))
{
mouseRect.left = max( mouseRect.left, sizingRect.left+minTrack.x+capturePoint.x-sizingRect.right );
mouseRect.right = min( mouseRect.right, sizingRect.left+maxTrack.x+capturePoint.x-sizingRect.right );
}
if (ON_TOP_BORDER(hittest))
{
mouseRect.top = max( mouseRect.top, sizingRect.bottom-maxTrack.y+capturePoint.y-sizingRect.top );
mouseRect.bottom = min( mouseRect.bottom,sizingRect.bottom-minTrack.y+capturePoint.y-sizingRect.top);
}
else if (ON_BOTTOM_BORDER(hittest))
{
mouseRect.top = max( mouseRect.top, sizingRect.top+minTrack.y+capturePoint.y-sizingRect.bottom );
mouseRect.bottom = min( mouseRect.bottom, sizingRect.top+maxTrack.y+capturePoint.y-sizingRect.bottom );
}
hdc = UserGetDCEx( pWndParent, 0, DCX_CACHE );
if (iconic)
{
DragCursor = pwnd->pcls->spicn;
if (DragCursor)
{
UserReferenceObject(DragCursor);
}
else
{
HCURSOR CursorHandle = (HCURSOR)co_IntSendMessage( UserHMGetHandle(pwnd), WM_QUERYDRAGICON, 0, 0 );
if (CursorHandle)
{
DragCursor = UserGetCurIconObject(CursorHandle);
}
else
{
iconic = FALSE;
}
}
}
/* repaint the window before moving it around */
co_UserRedrawWindow( pwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
IntNotifyWinEvent( EVENT_SYSTEM_MOVESIZESTART, pwnd, OBJID_WINDOW, CHILDID_SELF, 0);
co_IntSendMessage( UserHMGetHandle(pwnd), WM_ENTERSIZEMOVE, 0, 0 );
MsqSetStateWindow(pti, MSQ_STATE_MOVESIZE, UserHMGetHandle(pwnd));
if (IntGetCapture() != UserHMGetHandle(pwnd)) co_UserSetCapture( UserHMGetHandle(pwnd) );
pwnd->head.pti->TIF_flags |= TIF_MOVESIZETRACKING;
for(;;)
{
int dx = 0, dy = 0;
if (!co_IntGetPeekMessage(&msg, 0, 0, 0, PM_REMOVE, TRUE)) break;
if (IntCallMsgFilter( &msg, MSGF_SIZE )) continue;
if (msg.message == WM_KEYDOWN && (msg.wParam == VK_RETURN || msg.wParam == VK_ESCAPE))
break; // Exit on Return or Esc
if (!g_bWindowSnapEnabled && (msg.message == WM_LBUTTONUP ||
(msg.message == WM_MOUSEMOVE && (msg.wParam & MK_LBUTTON) == 0)))
{ // If no WindowSnapEnabled: Exit on button-up immediately
break;
}
else if (g_bWindowSnapEnabled && (msg.message == WM_LBUTTONUP ||
(msg.message == WM_MOUSEMOVE && (msg.wParam & MK_LBUTTON) == 0)))
{ // If WindowSnapEnabled: Decide whether to snap before exiting
if (hittest == HTCAPTION && thickframe && /* Check for snapping if was moved by caption */
IntIsSnapAllowedForWindow(pwnd) && (ExStyle & WS_EX_MDICHILD) == 0)
{
BOOLEAN wasSnap = IntIsWindowSnapped(pwnd); /* Need the live snap state, not orgSnap nor maximized state */
UINT snapTo = iconic ? HTNOWHERE : GetSnapActivationPoint(pwnd, pt);
if (snapTo)
{
if (DragFullWindows)
{
co_IntSnapWindow(pwnd, snapTo);
if (!wasSnap)
pwnd->InternalPos.NormalRect = origRect;
}
snap = snapTo;
}
}
break;
}
if ((msg.message != WM_KEYDOWN) && (msg.message != WM_MOUSEMOVE))
{
IntTranslateKbdMessage( &msg , 0 );
IntDispatchMessage( &msg );
continue; /* We are not interested in other messages */
}
pt = msg.pt;
if (msg.message == WM_KEYDOWN) switch(msg.wParam)
{
case VK_UP: pt.y -= 8; break;
case VK_DOWN: pt.y += 8; break;
case VK_LEFT: pt.x -= 8; break;
case VK_RIGHT: pt.x += 8; break;
}
pt.x = max( pt.x, mouseRect.left );
pt.x = min( pt.x, mouseRect.right - 1 );
pt.y = max( pt.y, mouseRect.top );
pt.y = min( pt.y, mouseRect.bottom - 1 );
dx = pt.x - capturePoint.x;
dy = pt.y - capturePoint.y;
if (dx || dy)
{
if (!moved)
{
moved = TRUE;
if (iconic) /* ok, no system popup tracking */
{
OldCursor = UserSetCursor(DragCursor, FALSE);
UserShowCursor(TRUE);
}
else if (!DragFullWindows)
UserDrawMovingFrame(hdc, &sizingRect, thickframe);
}
if (msg.message == WM_KEYDOWN)
{
UserSetCursorPos(pt.x, pt.y, 0, 0, FALSE);
}
else
{
RECT newRect = unmodRect;
if (!iconic && !DragFullWindows)
{
UserDrawMovingFrame(hdc, pFrameRect, thickframe);
pFrameRect = &sizingRect;
}
if (hittest == HTCAPTION)
{
/* Restore window size if it is snapped */
PRECT pr = &newRect;
LONG width, height, capcy, snapTo;
if (snap && syscommand == SC_MOVE && !iconic &&
!RECTL_bIsEmptyRect(&pwnd->InternalPos.NormalRect))
{
*pr = pwnd->InternalPos.NormalRect;
origRect = *pr; /* Save normal size - is required when window unsnapped from one side and snapped to another holding mouse down */
/* Try to position the center of the caption where the mouse is horizontally */
capcy = UserGetSystemMetrics((ExStyle & WS_EX_TOPMOST) ? SM_CYSMCAPTION : SM_CYCAPTION); /* No border, close enough */
width = pr->right - pr->left;
height = pr->bottom - pr->top;
pr->left = pt.x - width / 2;
pr->right = pr->left + width;
pr->top = mouseRect.top;
pr->bottom = pr->top + height;
if (pr->left < mouseRect.left)
{
pr->left = mouseRect.left;
pr->right = pr->left + width;
}
if ((pwnd->ExStyle & WS_EX_LAYOUTRTL) && pr->right > mouseRect.right)
{
pr->left = mouseRect.right - width;
pr->right = pr->left + width;
}
UserSetCursorPos(pt.x, pr->top + capcy / 2, 0, 0, FALSE);
snap = FALSE;
dx = dy = 0; /* Don't offset this move */
if (DragFullWindows)
{
IntSetStyle(pwnd, 0, WS_MAXIMIZE);
IntSetSnapEdge(pwnd, HTNOWHERE);
/* Have to move and size it now because we don't want SWP_NOSIZE */
co_WinPosSetWindowPos(pwnd, HWND_TOP, pr->left, pr->top, width, height, SWP_NOACTIVATE);
}
}
else if (!snap && syscommand == SC_MOVE && !iconic)
{
if ((snapTo = GetSnapActivationPoint(pwnd, pt)) != 0)
{
co_IntCalculateSnapPosition(pwnd, snapTo, &snapPreviewRect);
if (DragFullWindows)
{
/* TODO: Show preview of snap */
}
else
{
pFrameRect = &snapPreviewRect;
UserDrawMovingFrame(hdc, pFrameRect, thickframe);
continue;
}
}
}
/* regular window moving */
RECTL_vOffsetRect(&newRect, dx, dy);
}
if (ON_LEFT_BORDER(hittest)) newRect.left += dx;
else if (ON_RIGHT_BORDER(hittest)) newRect.right += dx;
if (ON_TOP_BORDER(hittest)) newRect.top += dy;
else if (ON_BOTTOM_BORDER(hittest)) newRect.bottom += dy;
capturePoint = pt;
//
// Save the new position to the unmodified rectangle. This allows explorer task bar
// sizing. Explorer will forces back the position unless a certain amount of sizing
// has occurred.
//
unmodRect = newRect;
/* Determine the hit location */
if (syscommand == SC_SIZE)
{
WPARAM wpSizingHit = 0;
if (hittest >= HTLEFT && hittest <= HTBOTTOMRIGHT)
wpSizingHit = WMSZ_LEFT + (hittest - HTLEFT);
co_IntSendMessage( UserHMGetHandle(pwnd), WM_SIZING, wpSizingHit, (LPARAM)&newRect );
}
else
co_IntSendMessage( UserHMGetHandle(pwnd), WM_MOVING, 0, (LPARAM)&newRect );
if (!iconic)
{
if (!DragFullWindows)
UserDrawMovingFrame( hdc, &newRect, thickframe );
else
{ // Moving the whole window now!
HRGN hrgnNew;
HRGN hrgnOrig = GreCreateRectRgnIndirect(&pwnd->rcWindow);
if (pwnd->hrgnClip != NULL)
NtGdiCombineRgn(hrgnOrig, hrgnOrig, pwnd->hrgnClip, RGN_AND);
//// This causes the mdi child window to jump up when it is moved.
//IntMapWindowPoints( 0, pWndParent, (POINT *)&rect, 2 );
co_WinPosSetWindowPos(pwnd,
NULL,
newRect.left,
newRect.top,
newRect.right - newRect.left,
newRect.bottom - newRect.top,
SWP_NOACTIVATE | ((hittest == HTCAPTION) ? SWP_NOSIZE : 0));
hrgnNew = GreCreateRectRgnIndirect(&pwnd->rcWindow);
if (pwnd->hrgnClip != NULL)
NtGdiCombineRgn(hrgnNew, hrgnNew, pwnd->hrgnClip, RGN_AND);
if (hrgnNew)
{
if (hrgnOrig)
NtGdiCombineRgn(hrgnOrig, hrgnOrig, hrgnNew, RGN_DIFF);
}
else
{
if (hrgnOrig)
{
GreDeleteObject(hrgnOrig);
hrgnOrig = 0;
}
}
// Update all the windows after the move or size, including this window.
UpdateThreadWindows(UserGetDesktopWindow()->spwndChild, pti, hrgnOrig);
if (hrgnOrig) GreDeleteObject(hrgnOrig);
if (hrgnNew) GreDeleteObject(hrgnNew);
}
}
sizingRect = newRect;
}
}
}
pwnd->head.pti->TIF_flags &= ~TIF_MOVESIZETRACKING;
IntReleaseCapture();
if ( iconic )
{
if ( moved ) /* restore cursors, show icon title later on */
{
UserShowCursor( FALSE );
OldCursor = UserSetCursor(OldCursor, FALSE);
}
/* It could be that the cursor was already changed while we were proceeding,
* so we must unreference whatever cursor was current at the time we restored the old one.
* Maybe it is DragCursor, but maybe it is another one and DragCursor got already freed.
*/
if (OldCursor) UserDereferenceObject(OldCursor);
}
else
{
UINT eraseFinalFrame = moved && !DragFullWindows;
if (eraseFinalFrame)
UserDrawMovingFrame(hdc, pFrameRect, thickframe); // Undo the XOR drawing
}
UserReleaseDC(NULL, hdc, FALSE);
//// This causes the mdi child window to jump up when it is moved.
//if (pWndParent) IntMapWindowPoints( 0, pWndParent, (POINT *)&sizingRect, 2 );
if (co_HOOK_CallHooks(WH_CBT, HCBT_MOVESIZE, (WPARAM)UserHMGetHandle(pwnd), (LPARAM)&sizingRect))
{
ERR("DoSizeMove : WH_CBT Call Hook return!\n");
moved = FALSE;
}
IntNotifyWinEvent( EVENT_SYSTEM_MOVESIZEEND, pwnd, OBJID_WINDOW, CHILDID_SELF, 0);
MsqSetStateWindow(pti, MSQ_STATE_MOVESIZE, NULL);
co_IntSendMessage( UserHMGetHandle(pwnd), WM_EXITSIZEMOVE, 0, 0 );
//// wine mdi hack
co_IntSendMessage( UserHMGetHandle(pwnd), WM_SETVISIBLE, !!(pwnd->style & WS_MINIMIZE), 0L);
////
/* window moved or resized */
if (moved)
{
BOOL forceSizing = !iconic && hittest == HTCAPTION && (!!orgSnap != !!snap);
UINT swp = (!forceSizing && hittest == HTCAPTION) ? SWP_NOSIZE : 0;
/* if the moving/resizing isn't canceled call SetWindowPos
* with the new position or the new size of the window
*/
if (!((msg.message == WM_KEYDOWN) && (msg.wParam == VK_ESCAPE)) )
{
/* NOTE: SWP_NOACTIVATE prevents document window activation in Word 6 */
if (!DragFullWindows || iconic)
{
if (snap)
{
co_IntSnapWindow(pwnd, snap);
}
else
{
if (orgSnap && !snap)
{
IntSetStyle(pwnd, 0, WS_MAXIMIZE);
IntSetSnapInfo(pwnd, HTNOWHERE, NULL);
}
co_WinPosSetWindowPos(pwnd, HWND_TOP, sizingRect.left, sizingRect.top,
sizingRect.right - sizingRect.left,
sizingRect.bottom - sizingRect.top, swp);
}
}
}
else
{
/* restore previous size/position */
if (orgSnap)
{
co_IntSnapWindow(pwnd, orgSnap);
}
else if (DragFullWindows)
{
co_WinPosSetWindowPos(pwnd, HWND_TOP, origRect.left, origRect.top,
origRect.right - origRect.left,
origRect.bottom - origRect.top, swp);
}
}
}
if (IntIsWindow(UserHMGetHandle(pwnd)))
{
/* Single click brings up the system menu when iconized */
if (iconic && !moved && (Style & WS_SYSMENU))
{
co_IntSendMessage(UserHMGetHandle(pwnd), WM_SYSCOMMAND, SC_MOUSEMENU + HTSYSMENU, MAKELONG(pt.x, pt.y));
}
}
}
PCURICON_OBJECT FASTCALL NC_IconForWindow( PWND pWnd )
{
PCURICON_OBJECT pIcon = NULL;
HICON hIcon;
hIcon = UserGetProp(pWnd, gpsi->atomIconSmProp, TRUE);
if (!hIcon) hIcon = UserGetProp(pWnd, gpsi->atomIconProp, TRUE);
if (!hIcon && pWnd->pcls->spicnSm)
return pWnd->pcls->spicnSm;
if (!hIcon && pWnd->pcls->spicn)
return pWnd->pcls->spicn;
// WARNING: Wine code has this test completely wrong. The following is how
// Windows behaves for windows having the WS_EX_DLGMODALFRAME style set:
// it does not use the default icon! And it does not check for DS_MODALFRAME.
if (!hIcon && !(pWnd->ExStyle & WS_EX_DLGMODALFRAME))
{
hIcon = gpsi->hIconSmWindows; // Both are IDI_WINLOGO Small
if (!hIcon) hIcon = gpsi->hIconWindows; // Reg size.
}
if (hIcon)
{
pIcon = (PCURICON_OBJECT)UserGetObjectNoErr(gHandleTable,
hIcon,
TYPE_CURSOR);
}
return pIcon;
}
BOOL
UserDrawSysMenuButton(PWND pWnd, HDC hDC, LPRECT Rect, BOOL Down)
{
PCURICON_OBJECT WindowIcon;
BOOL Ret = FALSE;
if ((WindowIcon = NC_IconForWindow(pWnd)))
{
UserReferenceObject(WindowIcon);
Ret = UserDrawIconEx(hDC,
Rect->left + 2,
Rect->top + 2,
WindowIcon,
UserGetSystemMetrics(SM_CXSMICON),
UserGetSystemMetrics(SM_CYSMICON),
0, NULL, DI_NORMAL);
UserDereferenceObject(WindowIcon);
}
return Ret;
}
BOOL
IntIsScrollBarVisible(PWND pWnd, INT hBar)
{
SCROLLBARINFO sbi;
sbi.cbSize = sizeof(SCROLLBARINFO);
if(!co_IntGetScrollBarInfo(pWnd, hBar, &sbi))
return FALSE;
return !(sbi.rgstate[0] & STATE_SYSTEM_OFFSCREEN);
}
/*
* FIXME:
* - Cache bitmaps, then just bitblt instead of calling DFC() (and
* wasting precious CPU cycles) every time
* - Center the buttons vertically in the rect
*/
VOID
UserDrawCaptionButton(PWND pWnd, LPRECT Rect, DWORD Style, DWORD ExStyle, HDC hDC, BOOL bDown, ULONG Type)
{
RECT TempRect;
if (!(Style & WS_SYSMENU))
{
return;
}
TempRect = *Rect;
switch (Type)
{
case DFCS_CAPTIONMIN:
{
if (ExStyle & WS_EX_TOOLWINDOW)
return; /* ToolWindows don't have min/max buttons */
if (Style & WS_SYSMENU)
TempRect.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
if (Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
TempRect.right -= UserGetSystemMetrics(SM_CXSIZE) - 2;
TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXSIZE) + 1;
TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSIZE) - 2;
TempRect.top += 2;
TempRect.right -= 1;
DrawFrameControl(hDC, &TempRect, DFC_CAPTION,
((Style & WS_MINIMIZE) ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMIN) |
(bDown ? DFCS_PUSHED : 0) |
((Style & WS_MINIMIZEBOX) ? 0 : DFCS_INACTIVE));
break;
}
case DFCS_CAPTIONMAX:
{
if (ExStyle & WS_EX_TOOLWINDOW)
return; /* ToolWindows don't have min/max buttons */
if (Style & WS_SYSMENU)
TempRect.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXSIZE) + 1;
TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSIZE) - 2;
TempRect.top += 2;
TempRect.right -= 1;
DrawFrameControl(hDC, &TempRect, DFC_CAPTION,
((Style & WS_MAXIMIZE) ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX) |
(bDown ? DFCS_PUSHED : 0) |
((Style & WS_MAXIMIZEBOX) ? 0 : DFCS_INACTIVE));
break;
}
case DFCS_CAPTIONCLOSE:
{
PMENU pSysMenu = IntGetSystemMenu(pWnd, FALSE);
UINT MenuState = IntGetMenuState(pSysMenu ? UserHMGetHandle(pSysMenu) : NULL, SC_CLOSE, MF_BYCOMMAND); /* in case of error MenuState==0xFFFFFFFF */
/* A tool window has a smaller Close button */
if (ExStyle & WS_EX_TOOLWINDOW)
{
TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXSMSIZE);
TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSMSIZE) - 2;
}
else
{
TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXSIZE);
TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSIZE) - 2;
}
TempRect.top += 2;
TempRect.right -= 2;
DrawFrameControl(hDC, &TempRect, DFC_CAPTION,
(DFCS_CAPTIONCLOSE | (bDown ? DFCS_PUSHED : 0) |
((!(MenuState & (MF_GRAYED|MF_DISABLED)) && !(pWnd->pcls->style & CS_NOCLOSE)) ? 0 : DFCS_INACTIVE)));
break;
}
}
}
VOID
UserDrawCaptionButtonWnd(PWND pWnd, HDC hDC, BOOL bDown, ULONG Type)
{
RECT WindowRect;
SIZE WindowBorder;
IntGetWindowRect(pWnd, &WindowRect);
WindowRect.right -= WindowRect.left;
WindowRect.bottom -= WindowRect.top;
WindowRect.left = WindowRect.top = 0;
UserGetWindowBorders(pWnd->style, pWnd->ExStyle, &WindowBorder, FALSE);
RECTL_vInflateRect(&WindowRect, -WindowBorder.cx, -WindowBorder.cy);
UserDrawCaptionButton(pWnd, &WindowRect, pWnd->style, pWnd->ExStyle, hDC, bDown, Type);
}
VOID
NC_DrawFrame( HDC hDC, RECT *CurrentRect, BOOL Active, DWORD Style, DWORD ExStyle)
{
/* Firstly the "thick" frame */
if ((Style & WS_THICKFRAME) && !(Style & WS_MINIMIZE))
{
LONG Width =
(UserGetSystemMetrics(SM_CXFRAME) - UserGetSystemMetrics(SM_CXDLGFRAME)) *
UserGetSystemMetrics(SM_CXBORDER);
LONG Height =
(UserGetSystemMetrics(SM_CYFRAME) - UserGetSystemMetrics(SM_CYDLGFRAME)) *
UserGetSystemMetrics(SM_CYBORDER);
NtGdiSelectBrush(hDC, IntGetSysColorBrush(Active ? COLOR_ACTIVEBORDER : COLOR_INACTIVEBORDER));
/* Draw frame */
NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->top, CurrentRect->right - CurrentRect->left, Height, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->top, Width, CurrentRect->bottom - CurrentRect->top, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->bottom, CurrentRect->right - CurrentRect->left, -Height, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect->right, CurrentRect->top, -Width, CurrentRect->bottom - CurrentRect->top, PATCOPY);
RECTL_vInflateRect(CurrentRect, -Width, -Height);
}
/* Now the other bit of the frame */
if (Style & (WS_DLGFRAME | WS_BORDER) || ExStyle & WS_EX_DLGMODALFRAME)
{
LONG Width = UserGetSystemMetrics(SM_CXBORDER);
LONG Height = UserGetSystemMetrics(SM_CYBORDER);
NtGdiSelectBrush(hDC, IntGetSysColorBrush(
(ExStyle & (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE)) ? COLOR_3DFACE :
(ExStyle & WS_EX_STATICEDGE) ? COLOR_WINDOWFRAME :
(Style & (WS_DLGFRAME | WS_THICKFRAME)) ? COLOR_3DFACE :
COLOR_WINDOWFRAME));
/* Draw frame */
NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->top, CurrentRect->right - CurrentRect->left, Height, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->top, Width, CurrentRect->bottom - CurrentRect->top, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->bottom, CurrentRect->right - CurrentRect->left, -Height, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect->right, CurrentRect->top, -Width, CurrentRect->bottom - CurrentRect->top, PATCOPY);
RECTL_vInflateRect(CurrentRect, -Width, -Height);
}
}
VOID UserDrawCaptionBar(
PWND pWnd,
HDC hDC,
INT Flags)
{
DWORD Style, ExStyle;
RECT WindowRect, CurrentRect, TempRect;
HPEN PreviousPen;
BOOL Gradient = FALSE;
PCURICON_OBJECT pIcon = NULL;
if (!(Flags & DC_NOVISIBLE) && !IntIsWindowVisible(pWnd)) return;
TRACE("UserDrawCaptionBar: pWnd %p, hDc %p, Flags 0x%x.\n", pWnd, hDC, Flags);
Style = pWnd->style;
ExStyle = pWnd->ExStyle;
IntGetWindowRect(pWnd, &WindowRect);
CurrentRect.top = CurrentRect.left = 0;
CurrentRect.right = WindowRect.right - WindowRect.left;
CurrentRect.bottom = WindowRect.bottom - WindowRect.top;
/* Draw outer edge */
if (UserHasWindowEdge(Style, ExStyle))
{
DrawEdge(hDC, &CurrentRect, EDGE_RAISED, BF_RECT | BF_ADJUST);
}
else if (ExStyle & WS_EX_STATICEDGE)
{
#if 0
DrawEdge(hDC, &CurrentRect, BDR_SUNKENINNER, BF_RECT | BF_ADJUST | BF_FLAT);
#else
NtGdiSelectBrush(hDC, IntGetSysColorBrush(COLOR_BTNSHADOW));
NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, 1, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY);
NtGdiSelectBrush(hDC, IntGetSysColorBrush(COLOR_BTNHIGHLIGHT));
NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, 1, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY);
RECTL_vInflateRect(&CurrentRect, -1, -1);
#endif
}
if (Flags & DC_FRAME) NC_DrawFrame(hDC, &CurrentRect, (Flags & DC_ACTIVE), Style, ExStyle);
/* Draw caption */
if ((Style & WS_CAPTION) == WS_CAPTION)
{
TempRect = CurrentRect;
Flags |= DC_TEXT|DC_BUTTONS; // Icon will be checked if not already set.
if (UserSystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &Gradient, 0) && Gradient)
{
Flags |= DC_GRADIENT;
}
if (ExStyle & WS_EX_TOOLWINDOW)
{
Flags |= DC_SMALLCAP;
TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSMCAPTION) - 1;
CurrentRect.top += UserGetSystemMetrics(SM_CYSMCAPTION);
}
else
{
TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYCAPTION) - 1;
CurrentRect.top += UserGetSystemMetrics(SM_CYCAPTION);
}
if (!(Flags & DC_ICON) &&
!(Flags & DC_SMALLCAP) &&
(Style & WS_SYSMENU) &&
!(ExStyle & WS_EX_TOOLWINDOW) )
{
pIcon = NC_IconForWindow(pWnd); // Force redraw of caption with icon if DC_ICON not flaged....
}
UserDrawCaption(pWnd, hDC, &TempRect, NULL, pIcon ? UserHMGetHandle(pIcon) : NULL, NULL, Flags);
/* Draw buttons */
if (Style & WS_SYSMENU)
{
UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONCLOSE);
if ((Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(ExStyle & WS_EX_TOOLWINDOW))
{
UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMIN);
UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMAX);
}
}
if (!(Style & WS_MINIMIZE))
{
/* Line under caption */
PreviousPen = NtGdiSelectPen(hDC, NtGdiGetStockObject(DC_PEN));
IntSetDCPenColor( hDC, IntGetSysColor(((ExStyle & (WS_EX_STATICEDGE|WS_EX_CLIENTEDGE|WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE) ?
COLOR_WINDOWFRAME : COLOR_3DFACE));
GreMoveTo(hDC, TempRect.left, TempRect.bottom, NULL);
NtGdiLineTo(hDC, TempRect.right, TempRect.bottom);
NtGdiSelectPen(hDC, PreviousPen);
}
}
if (!(Style & WS_MINIMIZE))
{
/* Draw menu bar */
if (pWnd->state & WNDS_HASMENU && pWnd->IDMenu) // Should be pWnd->spmenu
{
PMENU menu;
if ((menu = UserGetMenuObject(UlongToHandle(pWnd->IDMenu)))) // FIXME! Use pWnd->spmenu,
{
TempRect = CurrentRect;
TempRect.bottom = TempRect.top + menu->cyMenu; // Should be pWnd->spmenu->cyMenu;
CurrentRect.top += MENU_DrawMenuBar(hDC, &TempRect, pWnd, FALSE);
}
}
if (ExStyle & WS_EX_CLIENTEDGE)
{
DrawEdge(hDC, &CurrentRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
}
}
}
// Note from Wine:
/* MSDN docs are pretty idiotic here, they say app CAN use clipRgn in
the call to GetDCEx implying that it is allowed not to use it either.
However, the suggested GetDCEx( , DCX_WINDOW | DCX_INTERSECTRGN)
will cause clipRgn to be deleted after ReleaseDC().
Now, how is the "system" supposed to tell what happened?
*/
/*
* FIXME:
* - Drawing of WS_BORDER after scrollbars
* - Correct drawing of size-box
*/
LRESULT
NC_DoNCPaint(PWND pWnd, HDC hDC, INT Flags)
{
DWORD Style, ExStyle;
PWND Parent;
RECT WindowRect, CurrentRect, TempRect;
BOOL Active = FALSE;
if (!IntIsWindowVisible(pWnd) ||
(pWnd->state & WNDS_NONCPAINT && !(pWnd->state & WNDS_FORCEMENUDRAW)) ||
IntEqualRect(&pWnd->rcWindow, &pWnd->rcClient) )
return 0;
Style = pWnd->style;
TRACE("DefWndNCPaint: pWnd %p, hDc %p, Active %s.\n", pWnd, hDC, Flags & DC_ACTIVE ? "TRUE" : "FALSE");
Parent = IntGetParent(pWnd);
ExStyle = pWnd->ExStyle;
if (Flags == -1) // NC paint mode.
{
if (ExStyle & WS_EX_MDICHILD)
{
Active = IntIsChildWindow(gpqForeground->spwndActive, pWnd);
if (Active)
Active = (UserHMGetHandle(pWnd) == (HWND)co_IntSendMessage(UserHMGetHandle(Parent), WM_MDIGETACTIVE, 0, 0));
}
else
{
Active = (gpqForeground == pWnd->head.pti->MessageQueue);
}
Flags = DC_NC; // Redraw everything!
}
else
Flags |= DC_NC;
IntGetWindowRect(pWnd, &WindowRect);
CurrentRect.top = CurrentRect.left = 0;
CurrentRect.right = WindowRect.right - WindowRect.left;
CurrentRect.bottom = WindowRect.bottom - WindowRect.top;
/* Draw outer edge */
if (UserHasWindowEdge(pWnd->style, pWnd->ExStyle))
{
DrawEdge(hDC, &CurrentRect, EDGE_RAISED, BF_RECT | BF_ADJUST);
}
else if (pWnd->ExStyle & WS_EX_STATICEDGE)
{
#if 0
DrawEdge(hDC, &CurrentRect, BDR_SUNKENINNER, BF_RECT | BF_ADJUST | BF_FLAT);
#else
NtGdiSelectBrush(hDC, IntGetSysColorBrush(COLOR_BTNSHADOW));
NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, 1, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY);
NtGdiSelectBrush(hDC, IntGetSysColorBrush(COLOR_BTNHIGHLIGHT));
NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, 1, PATCOPY);
NtGdiPatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY);
RECTL_vInflateRect(&CurrentRect, -1, -1);
#endif
}
if (Flags & DC_FRAME) NC_DrawFrame(hDC, &CurrentRect, Active ? Active : (Flags & DC_ACTIVE), Style, ExStyle);
/* Draw caption */
if ((Style & WS_CAPTION) == WS_CAPTION)
{
HPEN PreviousPen;
BOOL Gradient = FALSE;
if (Flags & DC_REDRAWHUNGWND)
{
Flags &= ~DC_REDRAWHUNGWND;
Flags |= DC_NOSENDMSG;
}
if (UserSystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &Gradient, 0) && Gradient)
{
Flags |= DC_GRADIENT;
}
if (Active)
{
if (pWnd->state & WNDS_ACTIVEFRAME)
Flags |= DC_ACTIVE;
else
{
ERR("Wnd is active and not set active!\n");
}
}
TempRect = CurrentRect;
if (ExStyle & WS_EX_TOOLWINDOW)
{
Flags |= DC_SMALLCAP;
TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSMCAPTION) - 1;
CurrentRect.top += UserGetSystemMetrics(SM_CYSMCAPTION);
}
else
{
TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYCAPTION) - 1;
CurrentRect.top += UserGetSystemMetrics(SM_CYCAPTION);
}
UserDrawCaption(pWnd, hDC, &TempRect, NULL, NULL, NULL, Flags);
/* Draw buttons */
if (Style & WS_SYSMENU)
{
UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONCLOSE);
if ((Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(ExStyle & WS_EX_TOOLWINDOW))
{
UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMIN);
UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMAX);
}
}
if (!(Style & WS_MINIMIZE))
{
/* Line under caption */
PreviousPen = NtGdiSelectPen(hDC, NtGdiGetStockObject(DC_PEN));
IntSetDCPenColor( hDC, IntGetSysColor(
((ExStyle & (WS_EX_STATICEDGE | WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE) ?
COLOR_WINDOWFRAME : COLOR_3DFACE));
GreMoveTo(hDC, TempRect.left, TempRect.bottom, NULL);
NtGdiLineTo(hDC, TempRect.right, TempRect.bottom);
NtGdiSelectPen(hDC, PreviousPen);
}
}
if (!(Style & WS_MINIMIZE))
{
/* Draw menu bar */
if (pWnd->state & WNDS_HASMENU && pWnd->IDMenu) // Should be pWnd->spmenu
{
if (!(Flags & DC_NOSENDMSG))
{
PMENU menu;
// Fix crash in test_menu_locked_by_window, should use pWnd->spmenu....
if ((menu = UserGetMenuObject(UlongToHandle(pWnd->IDMenu)))) // FIXME! Use pWnd->spmenu,
{
TempRect = CurrentRect;
TempRect.bottom = TempRect.top + menu->cyMenu; // Should be pWnd->spmenu->cyMenu;
CurrentRect.top += MENU_DrawMenuBar(hDC, &TempRect, pWnd, FALSE);
}
}
}
if (ExStyle & WS_EX_CLIENTEDGE)
{
DrawEdge(hDC, &CurrentRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
}
/* Draw the scrollbars */
if ((Style & WS_VSCROLL) && (Style & WS_HSCROLL) &&
IntIsScrollBarVisible(pWnd, OBJID_VSCROLL) && IntIsScrollBarVisible(pWnd, OBJID_HSCROLL))
{
RECT ParentClientRect;
TempRect = CurrentRect;
if (ExStyle & WS_EX_LEFTSCROLLBAR)
TempRect.right = TempRect.left + UserGetSystemMetrics(SM_CXVSCROLL);
else
TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXVSCROLL);
TempRect.top = TempRect.bottom - UserGetSystemMetrics(SM_CYHSCROLL);
FillRect(hDC, &TempRect, IntGetSysColorBrush(COLOR_BTNFACE));
if (Parent)
{
IntGetClientRect(Parent, &ParentClientRect);
if (HASSIZEGRIP(Style, ExStyle, Parent->style, WindowRect, ParentClientRect))
{
DrawFrameControl(hDC, &TempRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
}
}
IntDrawScrollBar(pWnd, hDC, SB_VERT);
IntDrawScrollBar(pWnd, hDC, SB_HORZ);
}
else
{
if (Style & WS_VSCROLL && IntIsScrollBarVisible(pWnd, OBJID_VSCROLL))
{
IntDrawScrollBar(pWnd, hDC, SB_VERT);
}
else if (Style & WS_HSCROLL && IntIsScrollBarVisible(pWnd, OBJID_HSCROLL))
{
IntDrawScrollBar(pWnd, hDC, SB_HORZ);
}
}
}
return 0; // For WM_NCPAINT message, return 0.
}
/* Win: xxxCalcClientRect */
LRESULT NC_HandleNCCalcSize( PWND Wnd, WPARAM wparam, RECTL *Rect, BOOL Suspended )
{
LRESULT Result = 0;
SIZE WindowBorders;
RECT OrigRect;
LONG Style = Wnd->style;
LONG exStyle = Wnd->ExStyle;
if (Rect == NULL)
{
return Result;
}
OrigRect = *Rect;
Wnd->state &= ~WNDS_HASCAPTION;
if (wparam)
{
if (Wnd->pcls->style & CS_VREDRAW)
{
Result |= WVR_VREDRAW;
}
if (Wnd->pcls->style & CS_HREDRAW)
{
Result |= WVR_HREDRAW;
}
Result |= WVR_VALIDRECTS;
}
if (!(Wnd->style & WS_MINIMIZE))
{
if (UserHasWindowEdge(Wnd->style, Wnd->ExStyle))
{
UserGetWindowBorders(Wnd->style, Wnd->ExStyle, &WindowBorders, FALSE);
RECTL_vInflateRect(Rect, -WindowBorders.cx, -WindowBorders.cy);
}
else if ((Wnd->ExStyle & WS_EX_STATICEDGE) || (Wnd->style & WS_BORDER))
{
RECTL_vInflateRect(Rect, -1, -1);
}
if ((Wnd->style & WS_CAPTION) == WS_CAPTION)
{
Wnd->state |= WNDS_HASCAPTION;
if (Wnd->ExStyle & WS_EX_TOOLWINDOW)
Rect->top += UserGetSystemMetrics(SM_CYSMCAPTION);
else
Rect->top += UserGetSystemMetrics(SM_CYCAPTION);
}
if (HAS_MENU(Wnd, Style))
{
HDC hDC = UserGetDCEx(Wnd, 0, DCX_USESTYLE | DCX_WINDOW);
Wnd->state |= WNDS_HASMENU;
if (hDC)
{
RECT CliRect = *Rect;
CliRect.bottom -= OrigRect.top;
CliRect.right -= OrigRect.left;
CliRect.left -= OrigRect.left;
CliRect.top -= OrigRect.top;
if (!Suspended) Rect->top += MENU_DrawMenuBar(hDC, &CliRect, Wnd, TRUE);
UserReleaseDC(Wnd, hDC, FALSE);
}
}
if (Wnd->ExStyle & WS_EX_CLIENTEDGE)
{
RECTL_vInflateRect(Rect, -2 * UserGetSystemMetrics(SM_CXBORDER), -2 * UserGetSystemMetrics(SM_CYBORDER));
}
if (Style & WS_VSCROLL)
{
if (Rect->right - Rect->left >= UserGetSystemMetrics(SM_CXVSCROLL))
{
Wnd->state |= WNDS_HASVERTICALSCROOLLBAR;
/* rectangle is in screen coords when wparam is false */
if (!wparam && (exStyle & WS_EX_LAYOUTRTL)) exStyle ^= WS_EX_LEFTSCROLLBAR;
if((exStyle & WS_EX_LEFTSCROLLBAR) != 0)
Rect->left += UserGetSystemMetrics(SM_CXVSCROLL);
else
Rect->right -= UserGetSystemMetrics(SM_CXVSCROLL);
}
}
if (Style & WS_HSCROLL)
{
if( Rect->bottom - Rect->top > UserGetSystemMetrics(SM_CYHSCROLL))
{
Wnd->state |= WNDS_HASHORIZONTALSCROLLBAR;
Rect->bottom -= UserGetSystemMetrics(SM_CYHSCROLL);
}
}
if (Rect->top > Rect->bottom)
Rect->bottom = Rect->top;
if (Rect->left > Rect->right)
Rect->right = Rect->left;
}
else
{
Rect->right = Rect->left;
Rect->bottom = Rect->top;
}
return Result;
}
static
INT NC_DoNCActive(PWND Wnd)
{
INT Ret = 0;
if ( IntGetSysColor(COLOR_CAPTIONTEXT) != IntGetSysColor(COLOR_INACTIVECAPTIONTEXT) ||
IntGetSysColor(COLOR_ACTIVECAPTION) != IntGetSysColor(COLOR_INACTIVECAPTION) )
Ret = DC_CAPTION;
if (!(Wnd->style & WS_MINIMIZED) && UserHasThickFrameStyle(Wnd->style, Wnd->ExStyle))
{
//if (IntGetSysColor(COLOR_ACTIVEBORDER) != IntGetSysColor(COLOR_INACTIVEBORDER)) // Why are these the same?
{
Ret = DC_FRAME;
}
}
return Ret;
}
LRESULT NC_HandleNCActivate( PWND Wnd, WPARAM wParam, LPARAM lParam )
{
INT Flags;
/* Lotus Notes draws menu descriptions in the caption of its main
* window. When it wants to restore original "system" view, it just
* sends WM_NCACTIVATE message to itself. Any optimizations here in
* attempt to minimize redrawings lead to a not restored caption.
*/
if (wParam & DC_ACTIVE)
{
Wnd->state |= WNDS_ACTIVEFRAME|WNDS_HASCAPTION;
wParam = DC_CAPTION|DC_ACTIVE;
}
else
{
Wnd->state &= ~WNDS_ACTIVEFRAME;
wParam = DC_CAPTION;
}
if ((Wnd->state & WNDS_NONCPAINT) || !(Wnd->style & WS_VISIBLE))
return TRUE;
/* This isn't documented but is reproducible in at least XP SP2 and
* Outlook 2007 depends on it
*/
// MSDN:
// If this parameter is set to -1, DefWindowProc does not repaint the
// nonclient area to reflect the state change.
if ( lParam != -1 &&
( Flags = NC_DoNCActive(Wnd)) != 0 )
{
HDC hDC;
HRGN hRgnTemp = NULL, hRgn = (HRGN)lParam;
if (GreIsHandleValid(hRgn))
{
hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
if (NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY) == ERROR)
{
GreDeleteObject(hRgnTemp);
hRgnTemp = NULL;
}
}
if ((hDC = UserGetDCEx(Wnd, hRgnTemp, DCX_WINDOW|DCX_USESTYLE)))
{
NC_DoNCPaint(Wnd, hDC, wParam | Flags); // Redraw MENUs.
UserReleaseDC(Wnd, hDC, FALSE);
}
else
GreDeleteObject(hRgnTemp);
}
return TRUE;
}
VOID
NC_DoButton(PWND pWnd, WPARAM wParam, LPARAM lParam)
{
MSG Msg;
HDC WindowDC;
BOOL Pressed = TRUE, OldState;
WPARAM SCMsg;
PMENU SysMenu;
ULONG ButtonType;
DWORD Style;
UINT MenuState;
Style = pWnd->style;
switch (wParam)
{
case HTCLOSE:
SysMenu = IntGetSystemMenu(pWnd, FALSE);
MenuState = IntGetMenuState(SysMenu ? UserHMGetHandle(SysMenu) : NULL, SC_CLOSE, MF_BYCOMMAND); /* in case of error MenuState==0xFFFFFFFF */
if (!(Style & WS_SYSMENU) || (MenuState & (MF_GRAYED|MF_DISABLED)) || (pWnd->pcls->style & CS_NOCLOSE))
return;
ButtonType = DFCS_CAPTIONCLOSE;
SCMsg = SC_CLOSE;
break;
case HTMINBUTTON:
if (!(Style & WS_MINIMIZEBOX))
return;
ButtonType = DFCS_CAPTIONMIN;
SCMsg = ((Style & WS_MINIMIZE) ? SC_RESTORE : SC_MINIMIZE);
break;
case HTMAXBUTTON:
if (!(Style & WS_MAXIMIZEBOX))
return;
ButtonType = DFCS_CAPTIONMAX;
SCMsg = ((Style & WS_MAXIMIZE) ? SC_RESTORE : SC_MAXIMIZE);
break;
default:
ASSERT(FALSE);
return;
}
/*
* FIXME: Not sure where to do this, but we must flush the pending
* window updates when someone clicks on the close button and at
* the same time the window is overlapped with another one. This
* looks like a good place for now...
*/
co_IntUpdateWindows(pWnd, RDW_ALLCHILDREN, FALSE);
WindowDC = UserGetWindowDC(pWnd);
UserDrawCaptionButtonWnd(pWnd, WindowDC, TRUE, ButtonType);
co_UserSetCapture(UserHMGetHandle(pWnd));
for (;;)
{
if (!co_IntGetPeekMessage(&Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE, TRUE)) break;
if (IntCallMsgFilter( &Msg, MSGF_MAX )) continue;
if (Msg.message == WM_LBUTTONUP)
break;
if (Msg.message != WM_MOUSEMOVE)
continue;
OldState = Pressed;
Pressed = (GetNCHitEx(pWnd, Msg.pt) == wParam);
if (Pressed != OldState)
UserDrawCaptionButtonWnd(pWnd, WindowDC, Pressed, ButtonType);
}
if (Pressed)
UserDrawCaptionButtonWnd(pWnd, WindowDC, FALSE, ButtonType);
IntReleaseCapture();
UserReleaseDC(pWnd, WindowDC, FALSE);
if (Pressed)
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SCMsg, SCMsg == SC_CLOSE ? lParam : MAKELONG(Msg.pt.x,Msg.pt.y));
}
LRESULT
NC_HandleNCLButtonDown(PWND pWnd, WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case HTCAPTION:
{
PWND TopWnd = pWnd, parent;
while(1)
{
if ((TopWnd->style & (WS_POPUP|WS_CHILD)) != WS_CHILD)
break;
parent = UserGetAncestor( TopWnd, GA_PARENT );
if (!parent || UserIsDesktopWindow(parent)) break;
TopWnd = parent;
}
if ( (pWnd && (pWnd->ExStyle & WS_EX_NOACTIVATE)) ||
co_IntSetForegroundWindowMouse(TopWnd) ||
//NtUserCallHwndLock(hTopWnd, HWNDLOCK_ROUTINE_SETFOREGROUNDWINDOWMOUSE) ||
UserGetActiveWindow() == UserHMGetHandle(TopWnd))
{
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, lParam);
}
break;
}
case HTSYSMENU:
{
LONG style = pWnd->style;
if (style & WS_SYSMENU)
{
if(!(style & WS_MINIMIZE) )
{
RECT rect;
HDC hDC = UserGetWindowDC(pWnd);
NC_GetInsideRect(pWnd, &rect);
UserDrawSysMenuButton(pWnd, hDC, &rect, TRUE);
UserReleaseDC( pWnd, hDC, FALSE );
}
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_MOUSEMENU + HTSYSMENU, lParam);
}
break;
}
case HTMENU:
{
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_MOUSEMENU + HTMENU, lParam);
break;
}
case HTHSCROLL:
{
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_HSCROLL + HTHSCROLL, lParam);
break;
}
case HTVSCROLL:
{
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_VSCROLL + HTVSCROLL, lParam);
break;
}
case HTMINBUTTON:
case HTMAXBUTTON:
case HTCLOSE:
{
NC_DoButton(pWnd, wParam, lParam);
break;
}
case HTLEFT:
case HTRIGHT:
case HTTOP:
case HTBOTTOM:
case HTTOPLEFT:
case HTTOPRIGHT:
case HTBOTTOMLEFT:
case HTBOTTOMRIGHT:
{
/* Old comment:
* "make sure hittest fits into 0xf and doesn't overlap with HTSYSMENU"
* This was previously done by setting wParam=SC_SIZE + wParam - 2
*/
/* But that is not what WinNT does. Instead it sends this. This
* is easy to differentiate from HTSYSMENU, because HTSYSMENU adds
* SC_MOUSEMENU into wParam.
*/
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_SIZE + wParam - (HTLEFT - WMSZ_LEFT), lParam);
break;
}
case HTBORDER:
break;
}
return(0);
}
LRESULT
NC_HandleNCLButtonDblClk(PWND pWnd, WPARAM wParam, LPARAM lParam)
{
ULONG Style;
Style = pWnd->style;
switch(wParam)
{
case HTCAPTION:
{
/* Maximize/Restore the window */
if((Style & WS_CAPTION) == WS_CAPTION && (Style & WS_MAXIMIZEBOX))
{
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, ((Style & (WS_MINIMIZE | WS_MAXIMIZE)) ? SC_RESTORE : SC_MAXIMIZE), 0);
}
break;
}
case HTSYSMENU:
{
PMENU SysMenu = IntGetSystemMenu(pWnd, FALSE);
UINT state = IntGetMenuState(SysMenu ? UserHMGetHandle(SysMenu) : NULL, SC_CLOSE, MF_BYCOMMAND);
/* If the close item of the sysmenu is disabled or not present do nothing */
if ((state & (MF_DISABLED | MF_GRAYED)) || (state == 0xFFFFFFFF))
break;
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_CLOSE, lParam);
break;
}
case HTTOP:
case HTBOTTOM:
{
RECT sizingRect = pWnd->rcWindow, mouseRect;
if (pWnd->ExStyle & WS_EX_MDICHILD)
break;
UserSystemParametersInfo(SPI_GETWORKAREA, 0, &mouseRect, 0);
co_WinPosSetWindowPos(pWnd,
NULL,
sizingRect.left,
mouseRect.top,
sizingRect.right - sizingRect.left,
mouseRect.bottom - mouseRect.top,
0);
break;
}
default:
return NC_HandleNCLButtonDown(pWnd, wParam, lParam);
}
return(0);
}
/***********************************************************************
* NC_HandleNCRButtonDown
*
* Handle a WM_NCRBUTTONDOWN message. Called from DefWindowProc().
*/
LRESULT NC_HandleNCRButtonDown(PWND pwnd, WPARAM wParam, LPARAM lParam)
{
MSG msg;
INT hittest = wParam;
switch (hittest)
{
case HTCAPTION:
case HTSYSMENU:
if (!IntGetSystemMenu( pwnd, FALSE )) break;
co_UserSetCapture( UserHMGetHandle(pwnd) );
for (;;)
{
if (!co_IntGetPeekMessage(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE, TRUE)) break;
if (IntCallMsgFilter( &msg, MSGF_MAX )) continue;
if (msg.message == WM_RBUTTONUP)
{
hittest = GetNCHitEx( pwnd, msg.pt );
break;
}
if (UserHMGetHandle(pwnd) != IntGetCapture()) return 0;
}
IntReleaseCapture();
if (hittest == HTCAPTION || hittest == HTSYSMENU || hittest == HTHSCROLL || hittest == HTVSCROLL)
{
TRACE("Msg pt %x and Msg.lParam %x and lParam %x\n",MAKELONG(msg.pt.x,msg.pt.y),msg.lParam,lParam);
co_IntSendMessage( UserHMGetHandle(pwnd), WM_CONTEXTMENU, (WPARAM)UserHMGetHandle(pwnd), MAKELONG(msg.pt.x,msg.pt.y));
}
break;
}
return 0;
}
#if 0 // Old version, kept there for reference, which is also used
// almost unmodified in uxtheme.dll (in nonclient.c)
/*
* FIXME:
* - Check the scrollbar handling
*/
LRESULT
DefWndNCHitTest(HWND hWnd, POINT Point)
{
RECT WindowRect, ClientRect, OrigWndRect;
POINT ClientPoint;
SIZE WindowBorders;
DWORD Style = GetWindowLongPtrW(hWnd, GWL_STYLE);
DWORD ExStyle = GetWindowLongPtrW(hWnd, GWL_EXSTYLE);
GetWindowRect(hWnd, &WindowRect);
if (!PtInRect(&WindowRect, Point))
{
return HTNOWHERE;
}
OrigWndRect = WindowRect;
if (UserHasWindowEdge(Style, ExStyle))
{
LONG XSize, YSize;
UserGetWindowBorders(Style, ExStyle, &WindowBorders, FALSE);
InflateRect(&WindowRect, -WindowBorders.cx, -WindowBorders.cy);
XSize = GetSystemMetrics(SM_CXSIZE) * GetSystemMetrics(SM_CXBORDER);
YSize = GetSystemMetrics(SM_CYSIZE) * GetSystemMetrics(SM_CYBORDER);
if (!PtInRect(&WindowRect, Point))
{
BOOL ThickFrame;
ThickFrame = (Style & WS_THICKFRAME);
if (Point.y < WindowRect.top)
{
if(Style & WS_MINIMIZE)
return HTCAPTION;
if(!ThickFrame)
return HTBORDER;
if (Point.x < (WindowRect.left + XSize))
return HTTOPLEFT;
if (Point.x >= (WindowRect.right - XSize))
return HTTOPRIGHT;
return HTTOP;
}
if (Point.y >= WindowRect.bottom)
{
if(Style & WS_MINIMIZE)
return HTCAPTION;
if(!ThickFrame)
return HTBORDER;
if (Point.x < (WindowRect.left + XSize))
return HTBOTTOMLEFT;
if (Point.x >= (WindowRect.right - XSize))
return HTBOTTOMRIGHT;
return HTBOTTOM;
}
if (Point.x < WindowRect.left)
{
if(Style & WS_MINIMIZE)
return HTCAPTION;
if(!ThickFrame)
return HTBORDER;
if (Point.y < (WindowRect.top + YSize))
return HTTOPLEFT;
if (Point.y >= (WindowRect.bottom - YSize))
return HTBOTTOMLEFT;
return HTLEFT;
}
if (Point.x >= WindowRect.right)
{
if(Style & WS_MINIMIZE)
return HTCAPTION;
if(!ThickFrame)
return HTBORDER;
if (Point.y < (WindowRect.top + YSize))
return HTTOPRIGHT;
if (Point.y >= (WindowRect.bottom - YSize))
return HTBOTTOMRIGHT;
return HTRIGHT;
}
}
}
else
{
if (ExStyle & WS_EX_STATICEDGE)
InflateRect(&WindowRect,
-GetSystemMetrics(SM_CXBORDER),
-GetSystemMetrics(SM_CYBORDER));
if (!PtInRect(&WindowRect, Point))
return HTBORDER;
}
if ((Style & WS_CAPTION) == WS_CAPTION)
{
if (ExStyle & WS_EX_TOOLWINDOW)
WindowRect.top += GetSystemMetrics(SM_CYSMCAPTION);
else
WindowRect.top += GetSystemMetrics(SM_CYCAPTION);
if (!PtInRect(&WindowRect, Point))
{
if (Style & WS_SYSMENU)
{
if (ExStyle & WS_EX_TOOLWINDOW)
{
WindowRect.right -= GetSystemMetrics(SM_CXSMSIZE);
}
else
{
// if(!(ExStyle & WS_EX_DLGMODALFRAME))
// FIXME: The real test should check whether there is
// an icon for the system window, and if so, do the
// rect.left increase.
// See dll/win32/uxtheme/nonclient.c!DefWndNCHitTest
// and win32ss/user/ntuser/nonclient.c!GetNCHitEx which does
// the test better.
WindowRect.left += GetSystemMetrics(SM_CXSIZE);
WindowRect.right -= GetSystemMetrics(SM_CXSIZE);
}
}
if (Point.x < WindowRect.left)
return HTSYSMENU;
if (WindowRect.right <= Point.x)
return HTCLOSE;
if (Style & WS_MAXIMIZEBOX || Style & WS_MINIMIZEBOX)
WindowRect.right -= GetSystemMetrics(SM_CXSIZE);
if (Point.x >= WindowRect.right)
return HTMAXBUTTON;
if (Style & WS_MINIMIZEBOX)
WindowRect.right -= GetSystemMetrics(SM_CXSIZE);
if (Point.x >= WindowRect.right)
return HTMINBUTTON;
return HTCAPTION;
}
}
if(!(Style & WS_MINIMIZE))
{
ClientPoint = Point;
ScreenToClient(hWnd, &ClientPoint);
GetClientRect(hWnd, &ClientRect);
if (PtInRect(&ClientRect, ClientPoint))
{
return HTCLIENT;
}
if (GetMenu(hWnd) && !(Style & WS_CHILD))
{
if (Point.x > 0 && Point.x < WindowRect.right && ClientPoint.y < 0)
return HTMENU;
}
if (ExStyle & WS_EX_CLIENTEDGE)
{
InflateRect(&WindowRect, -2 * GetSystemMetrics(SM_CXBORDER),
-2 * GetSystemMetrics(SM_CYBORDER));
}
if ((Style & WS_VSCROLL) && (Style & WS_HSCROLL) &&
(WindowRect.bottom - WindowRect.top) > GetSystemMetrics(SM_CYHSCROLL))
{
RECT ParentRect, TempRect = WindowRect, TempRect2 = WindowRect;
HWND Parent = GetParent(hWnd);
TempRect.bottom -= GetSystemMetrics(SM_CYHSCROLL);
if ((ExStyle & WS_EX_LEFTSCROLLBAR) != 0)
TempRect.right = TempRect.left + GetSystemMetrics(SM_CXVSCROLL);
else
TempRect.left = TempRect.right - GetSystemMetrics(SM_CXVSCROLL);
if (PtInRect(&TempRect, Point))
return HTVSCROLL;
TempRect2.top = TempRect2.bottom - GetSystemMetrics(SM_CYHSCROLL);
if ((ExStyle & WS_EX_LEFTSCROLLBAR) != 0)
TempRect2.left += GetSystemMetrics(SM_CXVSCROLL);
else
TempRect2.right -= GetSystemMetrics(SM_CXVSCROLL);
if (PtInRect(&TempRect2, Point))
return HTHSCROLL;
TempRect.top = TempRect2.top;
TempRect.bottom = TempRect2.bottom;
if(Parent)
GetClientRect(Parent, &ParentRect);
if (PtInRect(&TempRect, Point) && HASSIZEGRIP(Style, ExStyle,
GetWindowLongPtrW(Parent, GWL_STYLE), OrigWndRect, ParentRect))
{
if ((ExStyle & WS_EX_LEFTSCROLLBAR) != 0)
return HTBOTTOMLEFT;
else
return HTBOTTOMRIGHT;
}
}
else
{
if (Style & WS_VSCROLL)
{
RECT TempRect = WindowRect;
if ((ExStyle & WS_EX_LEFTSCROLLBAR) != 0)
TempRect.right = TempRect.left + GetSystemMetrics(SM_CXVSCROLL);
else
TempRect.left = TempRect.right - GetSystemMetrics(SM_CXVSCROLL);
if (PtInRect(&TempRect, Point))
return HTVSCROLL;
} else
if (Style & WS_HSCROLL)
{
RECT TempRect = WindowRect;
TempRect.top = TempRect.bottom - GetSystemMetrics(SM_CYHSCROLL);
if (PtInRect(&TempRect, Point))
return HTHSCROLL;
}
}
}
return HTNOWHERE;
}
#endif
DWORD FASTCALL
GetNCHitEx(PWND pWnd, POINT pt)
{
RECT rcWindow, rcClient;
DWORD Style, ExStyle;
if (!pWnd) return HTNOWHERE;
if (UserIsDesktopWindow(pWnd))
{
rcClient.left = rcClient.top = rcWindow.left = rcWindow.top = 0;
rcWindow.right = UserGetSystemMetrics(SM_CXSCREEN);
rcWindow.bottom = UserGetSystemMetrics(SM_CYSCREEN);
rcClient.right = UserGetSystemMetrics(SM_CXSCREEN);
rcClient.bottom = UserGetSystemMetrics(SM_CYSCREEN);
}
else
{
rcClient = pWnd->rcClient;
rcWindow = pWnd->rcWindow;
}
if (!RECTL_bPointInRect(&rcWindow, pt.x, pt.y)) return HTNOWHERE;
Style = pWnd->style;
ExStyle = pWnd->ExStyle;
if (Style & WS_MINIMIZE) return HTCAPTION;
if (RECTL_bPointInRect( &rcClient, pt.x, pt.y )) return HTCLIENT;
/* Check borders */
if (HAS_THICKFRAME( Style, ExStyle ))
{
RECTL_vInflateRect(&rcWindow, -UserGetSystemMetrics(SM_CXFRAME), -UserGetSystemMetrics(SM_CYFRAME) );
if (!RECTL_bPointInRect(&rcWindow, pt.x, pt.y ))
{
/* Check top sizing border */
if (pt.y < rcWindow.top)
{
if (pt.x < rcWindow.left+UserGetSystemMetrics(SM_CXSIZE)) return HTTOPLEFT;
if (pt.x >= rcWindow.right-UserGetSystemMetrics(SM_CXSIZE)) return HTTOPRIGHT;
return HTTOP;
}
/* Check bottom sizing border */
if (pt.y >= rcWindow.bottom)
{
if (pt.x < rcWindow.left+UserGetSystemMetrics(SM_CXSIZE)) return HTBOTTOMLEFT;
if (pt.x >= rcWindow.right-UserGetSystemMetrics(SM_CXSIZE)) return HTBOTTOMRIGHT;
return HTBOTTOM;
}
/* Check left sizing border */
if (pt.x < rcWindow.left)
{
if (pt.y < rcWindow.top+UserGetSystemMetrics(SM_CYSIZE)) return HTTOPLEFT;
if (pt.y >= rcWindow.bottom-UserGetSystemMetrics(SM_CYSIZE)) return HTBOTTOMLEFT;
return HTLEFT;
}
/* Check right sizing border */
if (pt.x >= rcWindow.right)
{
if (pt.y < rcWindow.top+UserGetSystemMetrics(SM_CYSIZE)) return HTTOPRIGHT;
if (pt.y >= rcWindow.bottom-UserGetSystemMetrics(SM_CYSIZE)) return HTBOTTOMRIGHT;
return HTRIGHT;
}
}
}
else /* No thick frame */
{
if (HAS_DLGFRAME( Style, ExStyle ))
RECTL_vInflateRect(&rcWindow, -UserGetSystemMetrics(SM_CXDLGFRAME), -UserGetSystemMetrics(SM_CYDLGFRAME));
else if (HAS_THINFRAME( Style, ExStyle ))
RECTL_vInflateRect(&rcWindow, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER));
else if (HAS_CLIENTFRAME( Style, ExStyle ))
RECTL_vInflateRect(&rcWindow, -UserGetSystemMetrics(SM_CXEDGE), -UserGetSystemMetrics(SM_CYEDGE));
if (!RECTL_bPointInRect( &rcWindow, pt.x, pt.y )) return HTBORDER;
}
/* Check caption */
if ((Style & WS_CAPTION) == WS_CAPTION)
{
if (ExStyle & WS_EX_TOOLWINDOW)
rcWindow.top += UserGetSystemMetrics(SM_CYSMCAPTION) - 1;
else
rcWindow.top += UserGetSystemMetrics(SM_CYCAPTION) - 1;
if (!RECTL_bPointInRect( &rcWindow, pt.x, pt.y ))
{
BOOL min_or_max_box = (Style & WS_SYSMENU) && (Style & (WS_MINIMIZEBOX|WS_MAXIMIZEBOX));
if (ExStyle & WS_EX_LAYOUTRTL)
{
/* Check system menu */
if ((Style & WS_SYSMENU) && !(ExStyle & WS_EX_TOOLWINDOW) && NC_IconForWindow(pWnd))
{
rcWindow.right -= UserGetSystemMetrics(SM_CYCAPTION) - 1;
if (pt.x > rcWindow.right) return HTSYSMENU;
}
/* Check close button */
if (Style & WS_SYSMENU)
{
rcWindow.left += UserGetSystemMetrics(SM_CYCAPTION);
if (pt.x < rcWindow.left) return HTCLOSE;
}
/* Check maximize box */
/* In Win95 there is automatically a Maximize button when there is a minimize one */
if (min_or_max_box && !(ExStyle & WS_EX_TOOLWINDOW))
{
rcWindow.left += UserGetSystemMetrics(SM_CXSIZE);
if (pt.x < rcWindow.left) return HTMAXBUTTON;
}
/* Check minimize box */
if (min_or_max_box && !(ExStyle & WS_EX_TOOLWINDOW))
{
rcWindow.left += UserGetSystemMetrics(SM_CXSIZE);
if (pt.x < rcWindow.left) return HTMINBUTTON;
}
}
else
{
/* Check system menu */
if ((Style & WS_SYSMENU) && !(ExStyle & WS_EX_TOOLWINDOW) && NC_IconForWindow(pWnd))
{
rcWindow.left += UserGetSystemMetrics(SM_CYCAPTION) - 1;
if (pt.x < rcWindow.left) return HTSYSMENU;
}
/* Check close button */
if (Style & WS_SYSMENU)
{
rcWindow.right -= UserGetSystemMetrics(SM_CYCAPTION);
if (pt.x > rcWindow.right) return HTCLOSE;
}
/* Check maximize box */
/* In Win95 there is automatically a Maximize button when there is a minimize one */
if (min_or_max_box && !(ExStyle & WS_EX_TOOLWINDOW))
{
rcWindow.right -= UserGetSystemMetrics(SM_CXSIZE);
if (pt.x > rcWindow.right) return HTMAXBUTTON;
}
/* Check minimize box */
if (min_or_max_box && !(ExStyle & WS_EX_TOOLWINDOW))
{
rcWindow.right -= UserGetSystemMetrics(SM_CXSIZE);
if (pt.x > rcWindow.right) return HTMINBUTTON;
}
}
return HTCAPTION;
}
}
/* Check menu bar */
if (HAS_MENU( pWnd, Style ) && (pt.y < rcClient.top) &&
(pt.x >= rcClient.left) && (pt.x < rcClient.right))
return HTMENU;
/* Check vertical scroll bar */
if (ExStyle & WS_EX_LAYOUTRTL) ExStyle ^= WS_EX_LEFTSCROLLBAR;
if (Style & WS_VSCROLL)
{
if((ExStyle & WS_EX_LEFTSCROLLBAR) != 0)
rcClient.left -= UserGetSystemMetrics(SM_CXVSCROLL);
else
rcClient.right += UserGetSystemMetrics(SM_CXVSCROLL);
if (RECTL_bPointInRect( &rcClient, pt.x, pt.y )) return HTVSCROLL;
}
/* Check horizontal scroll bar */
if (Style & WS_HSCROLL)
{
rcClient.bottom += UserGetSystemMetrics(SM_CYHSCROLL);
if (RECTL_bPointInRect( &rcClient, pt.x, pt.y ))
{
/* Check size box */
if ((Style & WS_VSCROLL) &&
((((ExStyle & WS_EX_LEFTSCROLLBAR) != 0) && (pt.x <= rcClient.left + UserGetSystemMetrics(SM_CXVSCROLL))) ||
(((ExStyle & WS_EX_LEFTSCROLLBAR) == 0) && (pt.x >= rcClient.right - UserGetSystemMetrics(SM_CXVSCROLL)))))
return HTSIZE;
return HTHSCROLL;
}
}
/* Has to return HTNOWHERE if nothing was found
Could happen when a window has a customized non client area */
return HTNOWHERE;
}
/* EOF */