1103 lines
34 KiB
C
1103 lines
34 KiB
C
/*
|
|
* Window position related functions.
|
|
*
|
|
* Copyright 1993, 1994, 1995 Alexandre Julliard
|
|
* 1995, 1996, 1999 Alex Korobka
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "user_private.h"
|
|
#include "winerror.h"
|
|
#include "controls.h"
|
|
#include "win.h"
|
|
#include "wine/server.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(win);
|
|
|
|
#define HAS_THICKFRAME(style) \
|
|
(((style) & WS_THICKFRAME) && \
|
|
!(((style) & (WS_DLGFRAME|WS_BORDER)) == WS_DLGFRAME))
|
|
|
|
#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 PLACE_MIN 0x0001
|
|
#define PLACE_MAX 0x0002
|
|
#define PLACE_RECT 0x0004
|
|
|
|
|
|
/***********************************************************************
|
|
* SwitchToThisWindow (USER32.@)
|
|
*/
|
|
void WINAPI SwitchToThisWindow( HWND hwnd, BOOL alt_tab )
|
|
{
|
|
if (IsIconic( hwnd )) NtUserShowWindow( hwnd, SW_RESTORE );
|
|
else BringWindowToTop( hwnd );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetWindowRect (USER32.@)
|
|
*/
|
|
BOOL WINAPI GetWindowRect( HWND hwnd, LPRECT rect )
|
|
{
|
|
BOOL ret = NtUserGetWindowRect( hwnd, rect );
|
|
if (ret) TRACE( "hwnd %p %s\n", hwnd, wine_dbgstr_rect(rect) );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetWindowRgn (USER32.@)
|
|
*/
|
|
int WINAPI GetWindowRgn( HWND hwnd, HRGN hrgn )
|
|
{
|
|
return NtUserGetWindowRgnEx( hwnd, hrgn, 0 );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetWindowRgnBox (USER32.@)
|
|
*/
|
|
int WINAPI GetWindowRgnBox( HWND hwnd, LPRECT prect )
|
|
{
|
|
int ret = ERROR;
|
|
HRGN hrgn;
|
|
|
|
if (!prect)
|
|
return ERROR;
|
|
|
|
if ((hrgn = CreateRectRgn(0, 0, 0, 0)))
|
|
{
|
|
if ((ret = GetWindowRgn( hwnd, hrgn )) != ERROR )
|
|
ret = GetRgnBox( hrgn, prect );
|
|
DeleteObject(hrgn);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetClientRect (USER32.@)
|
|
*/
|
|
BOOL WINAPI GetClientRect( HWND hwnd, LPRECT rect )
|
|
{
|
|
return NtUserGetClientRect( hwnd, rect );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* list_children_from_point
|
|
*
|
|
* Get the list of children that can contain point from the server.
|
|
* Point is in screen coordinates.
|
|
* Returned list must be freed by caller.
|
|
*/
|
|
static HWND *list_children_from_point( HWND hwnd, POINT pt )
|
|
{
|
|
HWND *list;
|
|
int i, size = 128;
|
|
|
|
for (;;)
|
|
{
|
|
int count = 0;
|
|
|
|
if (!(list = HeapAlloc( GetProcessHeap(), 0, size * sizeof(HWND) ))) break;
|
|
|
|
SERVER_START_REQ( get_window_children_from_point )
|
|
{
|
|
req->parent = wine_server_user_handle( hwnd );
|
|
req->x = pt.x;
|
|
req->y = pt.y;
|
|
req->dpi = get_thread_dpi();
|
|
wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
|
|
if (!wine_server_call( req )) count = reply->count;
|
|
}
|
|
SERVER_END_REQ;
|
|
if (count && count < size)
|
|
{
|
|
/* start from the end since HWND is potentially larger than user_handle_t */
|
|
for (i = count - 1; i >= 0; i--)
|
|
list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
|
|
list[count] = 0;
|
|
return list;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, list );
|
|
if (!count) break;
|
|
size = count + 1; /* restart with a large enough buffer */
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* WINPOS_WindowFromPoint
|
|
*
|
|
* Find the window and hittest for a given point.
|
|
*/
|
|
HWND WINPOS_WindowFromPoint( HWND hwndScope, POINT pt, INT *hittest )
|
|
{
|
|
int i, res;
|
|
HWND ret, *list;
|
|
POINT win_pt;
|
|
|
|
if (!hwndScope) hwndScope = GetDesktopWindow();
|
|
|
|
*hittest = HTNOWHERE;
|
|
|
|
if (!(list = list_children_from_point( hwndScope, pt ))) return 0;
|
|
|
|
/* now determine the hittest */
|
|
|
|
for (i = 0; list[i]; i++)
|
|
{
|
|
LONG style = GetWindowLongW( list[i], GWL_STYLE );
|
|
|
|
/* If window is minimized or disabled, return at once */
|
|
if (style & WS_DISABLED)
|
|
{
|
|
*hittest = HTERROR;
|
|
break;
|
|
}
|
|
/* Send WM_NCCHITTEST (if same thread) */
|
|
if (!WIN_IsCurrentThread( list[i] ))
|
|
{
|
|
*hittest = HTCLIENT;
|
|
break;
|
|
}
|
|
win_pt = point_thread_to_win_dpi( list[i], pt );
|
|
res = SendMessageW( list[i], WM_NCHITTEST, 0, MAKELPARAM( win_pt.x, win_pt.y ));
|
|
if (res != HTTRANSPARENT)
|
|
{
|
|
*hittest = res; /* Found the window */
|
|
break;
|
|
}
|
|
/* continue search with next window in z-order */
|
|
}
|
|
ret = list[i];
|
|
HeapFree( GetProcessHeap(), 0, list );
|
|
TRACE( "scope %p (%d,%d) returning %p\n", hwndScope, pt.x, pt.y, ret );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* WindowFromPoint (USER32.@)
|
|
*/
|
|
HWND WINAPI WindowFromPoint( POINT pt )
|
|
{
|
|
return NtUserWindowFromPoint( pt.x, pt.y );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* ChildWindowFromPoint (USER32.@)
|
|
*/
|
|
HWND WINAPI ChildWindowFromPoint( HWND parent, POINT pt )
|
|
{
|
|
return NtUserChildWindowFromPointEx( parent, pt.x, pt.y, CWP_ALL );
|
|
}
|
|
|
|
/*******************************************************************
|
|
* RealChildWindowFromPoint (USER32.@)
|
|
*/
|
|
HWND WINAPI RealChildWindowFromPoint( HWND parent, POINT pt )
|
|
{
|
|
return NtUserChildWindowFromPointEx( parent, pt.x, pt.y,
|
|
CWP_SKIPTRANSPARENT | CWP_SKIPINVISIBLE );
|
|
}
|
|
|
|
/*******************************************************************
|
|
* ChildWindowFromPointEx (USER32.@)
|
|
*/
|
|
HWND WINAPI ChildWindowFromPointEx( HWND parent, POINT pt, UINT flags )
|
|
{
|
|
return NtUserChildWindowFromPointEx( parent, pt.x, pt.y, flags );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* MapWindowPoints (USER32.@)
|
|
*/
|
|
INT WINAPI MapWindowPoints( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count )
|
|
{
|
|
return NtUserMapWindowPoints( hwnd_from, hwnd_to, points, count );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* ClientToScreen (USER32.@)
|
|
*/
|
|
BOOL WINAPI ClientToScreen( HWND hwnd, POINT *pt )
|
|
{
|
|
return NtUserClientToScreen( hwnd, pt );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* ScreenToClient (USER32.@)
|
|
*/
|
|
BOOL WINAPI ScreenToClient( HWND hwnd, POINT *pt )
|
|
{
|
|
return NtUserScreenToClient( hwnd, pt );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* IsIconic (USER32.@)
|
|
*/
|
|
BOOL WINAPI IsIconic(HWND hWnd)
|
|
{
|
|
return (GetWindowLongW( hWnd, GWL_STYLE ) & WS_MINIMIZE) != 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* IsZoomed (USER32.@)
|
|
*/
|
|
BOOL WINAPI IsZoomed(HWND hWnd)
|
|
{
|
|
return (GetWindowLongW( hWnd, GWL_STYLE ) & WS_MAXIMIZE) != 0;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* AllowSetForegroundWindow (USER32.@)
|
|
*/
|
|
BOOL WINAPI AllowSetForegroundWindow( DWORD procid )
|
|
{
|
|
/* FIXME: If Win98/2000 style SetForegroundWindow behavior is
|
|
* implemented, then fix this function. */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* LockSetForegroundWindow (USER32.@)
|
|
*/
|
|
BOOL WINAPI LockSetForegroundWindow( UINT lockcode )
|
|
{
|
|
/* FIXME: If Win98/2000 style SetForegroundWindow behavior is
|
|
* implemented, then fix this function. */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* BringWindowToTop (USER32.@)
|
|
*/
|
|
BOOL WINAPI BringWindowToTop( HWND hwnd )
|
|
{
|
|
return NtUserSetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* get_work_rect
|
|
*
|
|
* Get the work area that a maximized window can cover, depending on style.
|
|
*/
|
|
static BOOL get_work_rect( HWND hwnd, RECT *rect )
|
|
{
|
|
HMONITOR monitor = MonitorFromWindow( hwnd, MONITOR_DEFAULTTOPRIMARY );
|
|
MONITORINFO mon_info;
|
|
DWORD style;
|
|
|
|
if (!monitor) return FALSE;
|
|
|
|
mon_info.cbSize = sizeof(mon_info);
|
|
GetMonitorInfoW( monitor, &mon_info );
|
|
*rect = mon_info.rcMonitor;
|
|
|
|
style = GetWindowLongW( hwnd, GWL_STYLE );
|
|
if (style & WS_MAXIMIZEBOX)
|
|
{
|
|
if ((style & WS_CAPTION) == WS_CAPTION || !(style & (WS_CHILD | WS_POPUP)))
|
|
*rect = mon_info.rcWork;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetInternalWindowPos (USER32.@)
|
|
*/
|
|
UINT WINAPI GetInternalWindowPos( HWND hwnd, LPRECT rectWnd,
|
|
LPPOINT ptIcon )
|
|
{
|
|
WINDOWPLACEMENT wndpl;
|
|
|
|
wndpl.length = sizeof(wndpl);
|
|
if (GetWindowPlacement( hwnd, &wndpl ))
|
|
{
|
|
if (rectWnd) *rectWnd = wndpl.rcNormalPosition;
|
|
if (ptIcon) *ptIcon = wndpl.ptMinPosition;
|
|
return wndpl.showCmd;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static RECT get_maximized_work_rect( HWND hwnd )
|
|
{
|
|
RECT work_rect = { 0 };
|
|
|
|
if ((GetWindowLongW( hwnd, GWL_STYLE ) & (WS_MINIMIZE | WS_MAXIMIZE)) == WS_MAXIMIZE)
|
|
{
|
|
if (!get_work_rect( hwnd, &work_rect ))
|
|
work_rect = get_primary_monitor_rect();
|
|
}
|
|
return work_rect;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* update_maximized_pos
|
|
*
|
|
* For top level windows covering the work area, we might have to
|
|
* "forget" the maximized position. Windows presumably does this
|
|
* to avoid situations where the border style changes, which would
|
|
* lead the window to be outside the screen, or the window gets
|
|
* reloaded on a different screen, and the "saved" position no
|
|
* longer applies to it (despite being maximized).
|
|
*
|
|
* Some applications (e.g. Imperiums: Greek Wars) depend on this.
|
|
*/
|
|
static void update_maximized_pos( WND *wnd, RECT *work_rect )
|
|
{
|
|
if (wnd->parent && wnd->parent != GetDesktopWindow())
|
|
return;
|
|
|
|
if (wnd->dwStyle & WS_MAXIMIZE)
|
|
{
|
|
if (wnd->window_rect.left <= work_rect->left && wnd->window_rect.top <= work_rect->top &&
|
|
wnd->window_rect.right >= work_rect->right && wnd->window_rect.bottom >= work_rect->bottom)
|
|
wnd->max_pos.x = wnd->max_pos.y = -1;
|
|
}
|
|
else
|
|
wnd->max_pos.x = wnd->max_pos.y = -1;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetWindowPlacement (USER32.@)
|
|
*
|
|
* Win95:
|
|
* Fails if wndpl->length of Win95 (!) apps is invalid.
|
|
*/
|
|
BOOL WINAPI GetWindowPlacement( HWND hwnd, WINDOWPLACEMENT *wndpl )
|
|
{
|
|
return NtUserGetWindowPlacement( hwnd, wndpl );
|
|
}
|
|
|
|
/* make sure the specified rect is visible on screen */
|
|
static void make_rect_onscreen( RECT *rect )
|
|
{
|
|
MONITORINFO info;
|
|
HMONITOR monitor = MonitorFromRect( rect, MONITOR_DEFAULTTONEAREST );
|
|
|
|
info.cbSize = sizeof(info);
|
|
if (!monitor || !GetMonitorInfoW( monitor, &info )) return;
|
|
/* FIXME: map coordinates from rcWork to rcMonitor */
|
|
if (rect->right <= info.rcWork.left)
|
|
{
|
|
rect->right += info.rcWork.left - rect->left;
|
|
rect->left = info.rcWork.left;
|
|
}
|
|
else if (rect->left >= info.rcWork.right)
|
|
{
|
|
rect->left += info.rcWork.right - rect->right;
|
|
rect->right = info.rcWork.right;
|
|
}
|
|
if (rect->bottom <= info.rcWork.top)
|
|
{
|
|
rect->bottom += info.rcWork.top - rect->top;
|
|
rect->top = info.rcWork.top;
|
|
}
|
|
else if (rect->top >= info.rcWork.bottom)
|
|
{
|
|
rect->top += info.rcWork.bottom - rect->bottom;
|
|
rect->bottom = info.rcWork.bottom;
|
|
}
|
|
}
|
|
|
|
/* make sure the specified point is visible on screen */
|
|
static void make_point_onscreen( POINT *pt )
|
|
{
|
|
RECT rect;
|
|
|
|
SetRect( &rect, pt->x, pt->y, pt->x + 1, pt->y + 1 );
|
|
make_rect_onscreen( &rect );
|
|
pt->x = rect.left;
|
|
pt->y = rect.top;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* WINPOS_SetPlacement
|
|
*/
|
|
static BOOL WINPOS_SetPlacement( HWND hwnd, const WINDOWPLACEMENT *wndpl, UINT flags )
|
|
{
|
|
DWORD style;
|
|
RECT work_rect = get_maximized_work_rect( hwnd );
|
|
WND *pWnd = WIN_GetPtr( hwnd );
|
|
WINDOWPLACEMENT wp = *wndpl;
|
|
|
|
if (flags & PLACE_MIN) make_point_onscreen( &wp.ptMinPosition );
|
|
if (flags & PLACE_MAX) make_point_onscreen( &wp.ptMaxPosition );
|
|
if (flags & PLACE_RECT) make_rect_onscreen( &wp.rcNormalPosition );
|
|
|
|
TRACE( "%p: setting min %d,%d max %d,%d normal %s flags %x adjusted to min %d,%d max %d,%d normal %s\n",
|
|
hwnd, wndpl->ptMinPosition.x, wndpl->ptMinPosition.y,
|
|
wndpl->ptMaxPosition.x, wndpl->ptMaxPosition.y,
|
|
wine_dbgstr_rect(&wndpl->rcNormalPosition), flags,
|
|
wp.ptMinPosition.x, wp.ptMinPosition.y, wp.ptMaxPosition.x, wp.ptMaxPosition.y,
|
|
wine_dbgstr_rect(&wp.rcNormalPosition) );
|
|
|
|
if (!pWnd || pWnd == WND_OTHER_PROCESS || pWnd == WND_DESKTOP) return FALSE;
|
|
|
|
if (flags & PLACE_MIN) pWnd->min_pos = point_thread_to_win_dpi( hwnd, wp.ptMinPosition );
|
|
if (flags & PLACE_MAX)
|
|
{
|
|
pWnd->max_pos = point_thread_to_win_dpi( hwnd, wp.ptMaxPosition );
|
|
update_maximized_pos( pWnd, &work_rect );
|
|
}
|
|
if (flags & PLACE_RECT) pWnd->normal_rect = rect_thread_to_win_dpi( hwnd, wp.rcNormalPosition );
|
|
|
|
style = pWnd->dwStyle;
|
|
|
|
WIN_ReleasePtr( pWnd );
|
|
|
|
if( style & WS_MINIMIZE )
|
|
{
|
|
if (flags & PLACE_MIN)
|
|
{
|
|
NtUserSetWindowPos( hwnd, 0, wp.ptMinPosition.x, wp.ptMinPosition.y, 0, 0,
|
|
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
|
|
}
|
|
}
|
|
else if( style & WS_MAXIMIZE )
|
|
{
|
|
if (flags & PLACE_MAX)
|
|
NtUserSetWindowPos( hwnd, 0, wp.ptMaxPosition.x, wp.ptMaxPosition.y, 0, 0,
|
|
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
|
|
}
|
|
else if( flags & PLACE_RECT )
|
|
NtUserSetWindowPos( hwnd, 0, wp.rcNormalPosition.left, wp.rcNormalPosition.top,
|
|
wp.rcNormalPosition.right - wp.rcNormalPosition.left,
|
|
wp.rcNormalPosition.bottom - wp.rcNormalPosition.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE );
|
|
|
|
NtUserShowWindow( hwnd, wndpl->showCmd );
|
|
|
|
if (IsIconic( hwnd ))
|
|
{
|
|
/* SDK: ...valid only the next time... */
|
|
if( wndpl->flags & WPF_RESTORETOMAXIMIZED )
|
|
win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetWindowPlacement (USER32.@)
|
|
*
|
|
* Win95:
|
|
* Fails if wndpl->length of Win95 (!) apps is invalid.
|
|
*/
|
|
BOOL WINAPI SetWindowPlacement( HWND hwnd, const WINDOWPLACEMENT *wpl )
|
|
{
|
|
UINT flags = PLACE_MAX | PLACE_RECT;
|
|
if (!wpl) return FALSE;
|
|
if (wpl->flags & WPF_SETMINPOSITION) flags |= PLACE_MIN;
|
|
return WINPOS_SetPlacement( hwnd, wpl, flags );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* AnimateWindow (USER32.@)
|
|
* Shows/Hides a window with an animation
|
|
* NO ANIMATION YET
|
|
*/
|
|
BOOL WINAPI AnimateWindow(HWND hwnd, DWORD dwTime, DWORD dwFlags)
|
|
{
|
|
FIXME("partial stub\n");
|
|
|
|
/* If trying to show/hide and it's already *
|
|
* shown/hidden or invalid window, fail with *
|
|
* invalid parameter */
|
|
if(!IsWindow(hwnd) ||
|
|
(IsWindowVisible(hwnd) && !(dwFlags & AW_HIDE)) ||
|
|
(!IsWindowVisible(hwnd) && (dwFlags & AW_HIDE)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
NtUserShowWindow( hwnd, (dwFlags & AW_HIDE) ? SW_HIDE : ((dwFlags & AW_ACTIVATE) ? SW_SHOW : SW_SHOWNA) );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetInternalWindowPos (USER32.@)
|
|
*/
|
|
void WINAPI SetInternalWindowPos( HWND hwnd, UINT showCmd,
|
|
LPRECT rect, LPPOINT pt )
|
|
{
|
|
WINDOWPLACEMENT wndpl;
|
|
UINT flags;
|
|
|
|
wndpl.length = sizeof(wndpl);
|
|
wndpl.showCmd = showCmd;
|
|
wndpl.flags = flags = 0;
|
|
|
|
if( pt )
|
|
{
|
|
flags |= PLACE_MIN;
|
|
wndpl.flags |= WPF_SETMINPOSITION;
|
|
wndpl.ptMinPosition = *pt;
|
|
}
|
|
if( rect )
|
|
{
|
|
flags |= PLACE_RECT;
|
|
wndpl.rcNormalPosition = *rect;
|
|
}
|
|
WINPOS_SetPlacement( hwnd, &wndpl, flags );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* can_activate_window
|
|
*
|
|
* Check if we can activate the specified window.
|
|
*/
|
|
static BOOL can_activate_window( HWND hwnd )
|
|
{
|
|
LONG style;
|
|
|
|
if (!hwnd) return FALSE;
|
|
style = GetWindowLongW( hwnd, GWL_STYLE );
|
|
if (!(style & WS_VISIBLE)) return FALSE;
|
|
if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
|
|
return !(style & WS_DISABLED);
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* WINPOS_ActivateOtherWindow
|
|
*
|
|
* Activates window other than pWnd.
|
|
*/
|
|
void WINPOS_ActivateOtherWindow(HWND hwnd)
|
|
{
|
|
HWND hwndTo, fg;
|
|
|
|
if ((GetWindowLongW( hwnd, GWL_STYLE ) & WS_POPUP) && (hwndTo = GetWindow( hwnd, GW_OWNER )))
|
|
{
|
|
hwndTo = NtUserGetAncestor( hwndTo, GA_ROOT );
|
|
if (can_activate_window( hwndTo )) goto done;
|
|
}
|
|
|
|
hwndTo = hwnd;
|
|
for (;;)
|
|
{
|
|
if (!(hwndTo = GetWindow( hwndTo, GW_HWNDNEXT ))) break;
|
|
if (can_activate_window( hwndTo )) goto done;
|
|
}
|
|
|
|
hwndTo = GetTopWindow( 0 );
|
|
for (;;)
|
|
{
|
|
if (hwndTo == hwnd)
|
|
{
|
|
hwndTo = 0;
|
|
break;
|
|
}
|
|
if (can_activate_window( hwndTo )) goto done;
|
|
if (!(hwndTo = GetWindow( hwndTo, GW_HWNDNEXT ))) break;
|
|
}
|
|
|
|
done:
|
|
fg = NtUserGetForegroundWindow();
|
|
TRACE("win = %p fg = %p\n", hwndTo, fg);
|
|
if (!fg || (hwnd == fg))
|
|
{
|
|
if (SetForegroundWindow( hwndTo )) return;
|
|
}
|
|
if (!NtUserSetActiveWindow( hwndTo )) NtUserSetActiveWindow( 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* WINPOS_HandleWindowPosChanging
|
|
*
|
|
* Default handling for a WM_WINDOWPOSCHANGING. Called from DefWindowProc().
|
|
*/
|
|
LONG WINPOS_HandleWindowPosChanging( HWND hwnd, WINDOWPOS *winpos )
|
|
{
|
|
LONG style = GetWindowLongW( hwnd, GWL_STYLE );
|
|
|
|
if (winpos->flags & SWP_NOSIZE) return 0;
|
|
if ((style & WS_THICKFRAME) || ((style & (WS_POPUP | WS_CHILD)) == 0))
|
|
{
|
|
MINMAXINFO info = NtUserGetMinMaxInfo( hwnd );
|
|
winpos->cx = min( winpos->cx, info.ptMaxTrackSize.x );
|
|
winpos->cy = min( winpos->cy, info.ptMaxTrackSize.y );
|
|
if (!(style & WS_MINIMIZE))
|
|
{
|
|
winpos->cx = max( winpos->cx, info.ptMinTrackSize.x );
|
|
winpos->cy = max( winpos->cy, info.ptMinTrackSize.y );
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* BeginDeferWindowPos (USER32.@)
|
|
*/
|
|
HDWP WINAPI BeginDeferWindowPos( INT count )
|
|
{
|
|
return NtUserBeginDeferWindowPos( count );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* DeferWindowPos (USER32.@)
|
|
*/
|
|
HDWP WINAPI DeferWindowPos( HDWP hdwp, HWND hwnd, HWND after, INT x, INT y,
|
|
INT cx, INT cy, UINT flags )
|
|
{
|
|
return NtUserDeferWindowPosAndBand( hdwp, hwnd, after, x, y, cx, cy, flags, 0, 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* EndDeferWindowPos (USER32.@)
|
|
*/
|
|
BOOL WINAPI EndDeferWindowPos( HDWP hdwp )
|
|
{
|
|
return NtUserEndDeferWindowPosEx( hdwp, FALSE );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ArrangeIconicWindows (USER32.@)
|
|
*/
|
|
UINT WINAPI ArrangeIconicWindows( HWND parent )
|
|
{
|
|
return NtUserArrangeIconicWindows( parent );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* draw_moving_frame
|
|
*
|
|
* Draw the frame used when moving or resizing window.
|
|
*/
|
|
static void draw_moving_frame( HWND parent, HDC hdc, RECT *screen_rect, BOOL thickframe )
|
|
{
|
|
RECT rect = *screen_rect;
|
|
|
|
if (parent) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
|
|
if (thickframe)
|
|
{
|
|
const int width = GetSystemMetrics(SM_CXFRAME);
|
|
const int height = GetSystemMetrics(SM_CYFRAME);
|
|
|
|
HBRUSH hbrush = SelectObject( hdc, GetStockObject( GRAY_BRUSH ) );
|
|
PatBlt( hdc, rect.left, rect.top,
|
|
rect.right - rect.left - width, height, PATINVERT );
|
|
PatBlt( hdc, rect.left, rect.top + height, width,
|
|
rect.bottom - rect.top - height, PATINVERT );
|
|
PatBlt( hdc, rect.left + width, rect.bottom - 1,
|
|
rect.right - rect.left - width, -height, PATINVERT );
|
|
PatBlt( hdc, rect.right - 1, rect.top, -width,
|
|
rect.bottom - rect.top - height, PATINVERT );
|
|
SelectObject( hdc, hbrush );
|
|
}
|
|
else DrawFocusRect( hdc, &rect );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* start_size_move
|
|
*
|
|
* Initialization of a move or resize, when initiated from a menu choice.
|
|
* Return hit test code for caption or sizing border.
|
|
*/
|
|
static LONG start_size_move( HWND hwnd, WPARAM wParam, POINT *capturePoint, LONG style )
|
|
{
|
|
LONG hittest = 0;
|
|
POINT pt;
|
|
MSG msg;
|
|
RECT rectWindow;
|
|
|
|
GetWindowRect( hwnd, &rectWindow );
|
|
|
|
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 */
|
|
rect.top += GetSystemMetrics(SM_CYBORDER);
|
|
if (style & WS_SYSMENU)
|
|
rect.left += GetSystemMetrics(SM_CXSIZE) + 1;
|
|
if (style & WS_MINIMIZEBOX)
|
|
rect.right -= GetSystemMetrics(SM_CXSIZE) + 1;
|
|
if (style & WS_MAXIMIZEBOX)
|
|
rect.right -= GetSystemMetrics(SM_CXSIZE) + 1;
|
|
pt.x = (rect.right + rect.left) / 2;
|
|
pt.y = rect.top + GetSystemMetrics(SM_CYSIZE)/2;
|
|
hittest = HTCAPTION;
|
|
*capturePoint = pt;
|
|
}
|
|
else /* SC_SIZE */
|
|
{
|
|
NtUserSetCursor( LoadCursorW( 0, (LPWSTR)IDC_SIZEALL ) );
|
|
pt.x = pt.y = 0;
|
|
while(!hittest)
|
|
{
|
|
if (!GetMessageW( &msg, 0, 0, 0 )) return 0;
|
|
if (CallMsgFilterW( &msg, MSGF_SIZE )) continue;
|
|
|
|
switch(msg.message)
|
|
{
|
|
case WM_MOUSEMOVE:
|
|
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 = SendMessageW( hwnd, WM_NCHITTEST, 0, MAKELONG( pt.x, pt.y ) );
|
|
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 + GetSystemMetrics(SM_CYFRAME) / 2;
|
|
break;
|
|
case VK_DOWN:
|
|
hittest = HTBOTTOM;
|
|
pt.x =(rectWindow.left+rectWindow.right)/2;
|
|
pt.y = rectWindow.bottom - GetSystemMetrics(SM_CYFRAME) / 2;
|
|
break;
|
|
case VK_LEFT:
|
|
hittest = HTLEFT;
|
|
pt.x = rectWindow.left + GetSystemMetrics(SM_CXFRAME) / 2;
|
|
pt.y =(rectWindow.top+rectWindow.bottom)/2;
|
|
break;
|
|
case VK_RIGHT:
|
|
hittest = HTRIGHT;
|
|
pt.x = rectWindow.right - GetSystemMetrics(SM_CXFRAME) / 2;
|
|
pt.y =(rectWindow.top+rectWindow.bottom)/2;
|
|
break;
|
|
case VK_RETURN:
|
|
case VK_ESCAPE:
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
TranslateMessage( &msg );
|
|
DispatchMessageW( &msg );
|
|
break;
|
|
}
|
|
}
|
|
*capturePoint = pt;
|
|
}
|
|
NtUserSetCursorPos( pt.x, pt.y );
|
|
SendMessageW( hwnd, WM_SETCURSOR, (WPARAM)hwnd, MAKELONG( hittest, WM_MOUSEMOVE ));
|
|
return hittest;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* WINPOS_SysCommandSizeMove
|
|
*
|
|
* Perform SC_MOVE and SC_SIZE commands.
|
|
*/
|
|
void WINPOS_SysCommandSizeMove( HWND hwnd, WPARAM wParam )
|
|
{
|
|
MSG msg;
|
|
RECT sizingRect, mouseRect, origRect;
|
|
HDC hdc;
|
|
HWND parent;
|
|
LONG hittest = (LONG)(wParam & 0x0f);
|
|
WPARAM syscommand = wParam & 0xfff0;
|
|
MINMAXINFO minmax;
|
|
POINT capturePoint, pt;
|
|
LONG style = GetWindowLongW( hwnd, GWL_STYLE );
|
|
BOOL thickframe = HAS_THICKFRAME( style );
|
|
BOOL moved = FALSE;
|
|
DWORD dwPoint = GetMessagePos ();
|
|
BOOL DragFullWindows = TRUE;
|
|
HMONITOR mon = 0;
|
|
|
|
if (IsZoomed(hwnd) || !IsWindowVisible(hwnd)) return;
|
|
|
|
pt.x = (short)LOWORD(dwPoint);
|
|
pt.y = (short)HIWORD(dwPoint);
|
|
capturePoint = pt;
|
|
NtUserClipCursor( NULL );
|
|
|
|
TRACE("hwnd %p command %04lx, hittest %d, pos %d,%d\n",
|
|
hwnd, syscommand, hittest, pt.x, pt.y);
|
|
|
|
if (syscommand == SC_MOVE)
|
|
{
|
|
if (!hittest) hittest = start_size_move( hwnd, wParam, &capturePoint, style );
|
|
if (!hittest) return;
|
|
}
|
|
else /* SC_SIZE */
|
|
{
|
|
if ( hittest && (syscommand != SC_MOUSEMENU) )
|
|
hittest += (HTLEFT - WMSZ_LEFT);
|
|
else
|
|
{
|
|
set_capture_window( hwnd, GUI_INMOVESIZE, NULL );
|
|
hittest = start_size_move( hwnd, wParam, &capturePoint, style );
|
|
if (!hittest)
|
|
{
|
|
set_capture_window( 0, GUI_INMOVESIZE, NULL );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get min/max info */
|
|
|
|
minmax = NtUserGetMinMaxInfo( hwnd );
|
|
WIN_GetRectangles( hwnd, COORDS_PARENT, &sizingRect, NULL );
|
|
origRect = sizingRect;
|
|
if (style & WS_CHILD)
|
|
{
|
|
parent = GetParent(hwnd);
|
|
GetClientRect( parent, &mouseRect );
|
|
MapWindowPoints( parent, 0, (LPPOINT)&mouseRect, 2 );
|
|
MapWindowPoints( parent, 0, (LPPOINT)&sizingRect, 2 );
|
|
}
|
|
else
|
|
{
|
|
parent = 0;
|
|
mouseRect = get_virtual_screen_rect();
|
|
mon = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
|
|
}
|
|
|
|
if (ON_LEFT_BORDER(hittest))
|
|
{
|
|
mouseRect.left = max( mouseRect.left, sizingRect.right-minmax.ptMaxTrackSize.x+capturePoint.x-sizingRect.left );
|
|
mouseRect.right = min( mouseRect.right, sizingRect.right-minmax.ptMinTrackSize.x+capturePoint.x-sizingRect.left );
|
|
}
|
|
else if (ON_RIGHT_BORDER(hittest))
|
|
{
|
|
mouseRect.left = max( mouseRect.left, sizingRect.left+minmax.ptMinTrackSize.x+capturePoint.x-sizingRect.right );
|
|
mouseRect.right = min( mouseRect.right, sizingRect.left+minmax.ptMaxTrackSize.x+capturePoint.x-sizingRect.right );
|
|
}
|
|
if (ON_TOP_BORDER(hittest))
|
|
{
|
|
mouseRect.top = max( mouseRect.top, sizingRect.bottom-minmax.ptMaxTrackSize.y+capturePoint.y-sizingRect.top );
|
|
mouseRect.bottom = min( mouseRect.bottom,sizingRect.bottom-minmax.ptMinTrackSize.y+capturePoint.y-sizingRect.top);
|
|
}
|
|
else if (ON_BOTTOM_BORDER(hittest))
|
|
{
|
|
mouseRect.top = max( mouseRect.top, sizingRect.top+minmax.ptMinTrackSize.y+capturePoint.y-sizingRect.bottom );
|
|
mouseRect.bottom = min( mouseRect.bottom, sizingRect.top+minmax.ptMaxTrackSize.y+capturePoint.y-sizingRect.bottom );
|
|
}
|
|
|
|
/* Retrieve a default cache DC (without using the window style) */
|
|
hdc = NtUserGetDCEx( parent, 0, DCX_CACHE );
|
|
|
|
/* we only allow disabling the full window drag for child windows */
|
|
if (parent) SystemParametersInfoW( SPI_GETDRAGFULLWINDOWS, 0, &DragFullWindows, 0 );
|
|
|
|
/* repaint the window before moving it around */
|
|
NtUserRedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
|
|
|
|
SendMessageW( hwnd, WM_ENTERSIZEMOVE, 0, 0 );
|
|
set_capture_window( hwnd, GUI_INMOVESIZE, NULL );
|
|
|
|
while(1)
|
|
{
|
|
int dx = 0, dy = 0;
|
|
|
|
if (!GetMessageW( &msg, 0, 0, 0 )) break;
|
|
if (CallMsgFilterW( &msg, MSGF_SIZE )) continue;
|
|
|
|
/* Exit on button-up, Return, or Esc */
|
|
if ((msg.message == WM_LBUTTONUP) ||
|
|
((msg.message == WM_KEYDOWN) &&
|
|
((msg.wParam == VK_RETURN) || (msg.wParam == VK_ESCAPE)))) break;
|
|
|
|
if ((msg.message != WM_KEYDOWN) && (msg.message != WM_MOUSEMOVE))
|
|
{
|
|
TranslateMessage( &msg );
|
|
DispatchMessageW( &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 );
|
|
|
|
if (!parent)
|
|
{
|
|
HMONITOR newmon;
|
|
MONITORINFO info;
|
|
|
|
if ((newmon = MonitorFromPoint( pt, MONITOR_DEFAULTTONULL )))
|
|
mon = newmon;
|
|
|
|
info.cbSize = sizeof(info);
|
|
if (mon && GetMonitorInfoW( mon, &info ))
|
|
{
|
|
pt.x = max( pt.x, info.rcWork.left );
|
|
pt.x = min( pt.x, info.rcWork.right - 1 );
|
|
pt.y = max( pt.y, info.rcWork.top );
|
|
pt.y = min( pt.y, info.rcWork.bottom - 1 );
|
|
}
|
|
}
|
|
|
|
dx = pt.x - capturePoint.x;
|
|
dy = pt.y - capturePoint.y;
|
|
|
|
if (dx || dy)
|
|
{
|
|
if( !moved )
|
|
{
|
|
moved = TRUE;
|
|
if (!DragFullWindows)
|
|
draw_moving_frame( parent, hdc, &sizingRect, thickframe );
|
|
}
|
|
|
|
if (msg.message == WM_KEYDOWN) NtUserSetCursorPos( pt.x, pt.y );
|
|
else
|
|
{
|
|
if (!DragFullWindows) draw_moving_frame( parent, hdc, &sizingRect, thickframe );
|
|
if (hittest == HTCAPTION || hittest == HTBORDER) OffsetRect( &sizingRect, dx, dy );
|
|
if (ON_LEFT_BORDER(hittest)) sizingRect.left += dx;
|
|
else if (ON_RIGHT_BORDER(hittest)) sizingRect.right += dx;
|
|
if (ON_TOP_BORDER(hittest)) sizingRect.top += dy;
|
|
else if (ON_BOTTOM_BORDER(hittest)) sizingRect.bottom += dy;
|
|
capturePoint = pt;
|
|
|
|
/* determine the hit location */
|
|
if (syscommand == SC_SIZE && hittest != HTBORDER)
|
|
{
|
|
WPARAM wpSizingHit = 0;
|
|
|
|
if (hittest >= HTLEFT && hittest <= HTBOTTOMRIGHT)
|
|
wpSizingHit = WMSZ_LEFT + (hittest - HTLEFT);
|
|
SendMessageW( hwnd, WM_SIZING, wpSizingHit, (LPARAM)&sizingRect );
|
|
}
|
|
else
|
|
SendMessageW( hwnd, WM_MOVING, 0, (LPARAM)&sizingRect );
|
|
|
|
if (!DragFullWindows)
|
|
draw_moving_frame( parent, hdc, &sizingRect, thickframe );
|
|
else
|
|
{
|
|
RECT rect = sizingRect;
|
|
MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
|
|
NtUserSetWindowPos( hwnd, 0, rect.left, rect.top,
|
|
rect.right - rect.left, rect.bottom - rect.top,
|
|
(hittest == HTCAPTION) ? SWP_NOSIZE : 0 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (moved && !DragFullWindows)
|
|
{
|
|
draw_moving_frame( parent, hdc, &sizingRect, thickframe );
|
|
}
|
|
|
|
set_capture_window( 0, GUI_INMOVESIZE, NULL );
|
|
NtUserReleaseDC( parent, hdc );
|
|
if (parent) MapWindowPoints( 0, parent, (POINT *)&sizingRect, 2 );
|
|
|
|
if (HOOK_CallHooks( WH_CBT, HCBT_MOVESIZE, (WPARAM)hwnd, (LPARAM)&sizingRect, TRUE ))
|
|
moved = FALSE;
|
|
|
|
SendMessageW( hwnd, WM_EXITSIZEMOVE, 0, 0 );
|
|
SendMessageW( hwnd, WM_SETVISIBLE, !IsIconic(hwnd), 0L);
|
|
|
|
/* window moved or resized */
|
|
if (moved)
|
|
{
|
|
/* 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)
|
|
NtUserSetWindowPos( hwnd, 0, sizingRect.left, sizingRect.top,
|
|
sizingRect.right - sizingRect.left,
|
|
sizingRect.bottom - sizingRect.top,
|
|
( hittest == HTCAPTION ) ? SWP_NOSIZE : 0 );
|
|
}
|
|
else
|
|
{ /* restore previous size/position */
|
|
if(DragFullWindows)
|
|
NtUserSetWindowPos( hwnd, 0, origRect.left, origRect.top,
|
|
origRect.right - origRect.left,
|
|
origRect.bottom - origRect.top,
|
|
( hittest == HTCAPTION ) ? SWP_NOSIZE : 0 );
|
|
}
|
|
}
|
|
|
|
if (IsIconic(hwnd))
|
|
{
|
|
/* Single click brings up the system menu when iconized */
|
|
|
|
if( !moved )
|
|
{
|
|
if(style & WS_SYSMENU )
|
|
SendMessageW( hwnd, WM_SYSCOMMAND,
|
|
SC_MOUSEMENU + HTSYSMENU, MAKELONG(pt.x,pt.y));
|
|
}
|
|
}
|
|
}
|