Sweden-Number/dlls/ttydrv/wnd.c

653 lines
21 KiB
C

/*
* TTY window driver
*
* Copyright 1998,1999 Patrik Stridvall
*/
#include "config.h"
#include "gdi.h"
#include "ttydrv.h"
#include "region.h"
#include "win.h"
#include "winpos.h"
#include "debugtools.h"
DEFAULT_DEBUG_CHANNEL(ttydrv);
#define SWP_AGG_NOGEOMETRYCHANGE \
(SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE)
#define SWP_AGG_NOPOSCHANGE \
(SWP_AGG_NOGEOMETRYCHANGE | SWP_NOZORDER)
#define SWP_AGG_STATUSFLAGS \
(SWP_AGG_NOPOSCHANGE | SWP_FRAMECHANGED | SWP_HIDEWINDOW | SWP_SHOWWINDOW)
/**********************************************************************
* CreateWindow (TTYDRV.@)
*/
BOOL TTYDRV_CreateWindow( HWND hwnd, CREATESTRUCTA *cs, BOOL unicode )
{
BOOL ret;
#ifdef WINE_CURSES
WND *wndPtr = WIN_FindWndPtr( hwnd );
WINDOW *window;
INT cellWidth=8, cellHeight=8; /* FIXME: Hardcoded */
TRACE("(%x)\n", hwnd);
/* Only create top-level windows */
if (!(wndPtr->dwStyle & WS_CHILD))
{
if (!wndPtr->parent) /* desktop */
window = root_window;
else
{
int x = wndPtr->rectWindow.left;
int y = wndPtr->rectWindow.top;
int cx = wndPtr->rectWindow.right - wndPtr->rectWindow.left;
int cy = wndPtr->rectWindow.bottom - wndPtr->rectWindow.top;
window = subwin( root_window, cy/cellHeight, cx/cellWidth,
y/cellHeight, x/cellWidth);
werase(window);
wrefresh(window);
}
wndPtr->pDriverData = window;
}
WIN_ReleaseWndPtr( wndPtr );
#else /* defined(WINE_CURSES) */
FIXME("(%x): stub\n", hwnd);
#endif /* defined(WINE_CURSES) */
if (unicode)
{
ret = SendMessageW( hwnd, WM_NCCREATE, 0, (LPARAM)cs );
if (ret) ret = (SendMessageW( hwnd, WM_CREATE, 0, (LPARAM)cs ) != -1);
}
else
{
ret = SendMessageA( hwnd, WM_NCCREATE, 0, (LPARAM)cs );
if (ret) ret = (SendMessageA( hwnd, WM_CREATE, 0, (LPARAM)cs ) != -1);
}
return ret;
}
/***********************************************************************
* DestroyWindow (TTYDRV.@)
*/
BOOL TTYDRV_DestroyWindow( HWND hwnd )
{
#ifdef WINE_CURSES
WND *wndPtr = WIN_FindWndPtr( hwnd );
WINDOW *window = wndPtr->pDriverData;
TRACE("(%x)\n", hwnd);
if (window && window != root_window) delwin(window);
wndPtr->pDriverData = NULL;
WIN_ReleaseWndPtr( wndPtr );
#else /* defined(WINE_CURSES) */
FIXME("(%x): stub\n", hwnd);
#endif /* defined(WINE_CURSES) */
return TRUE;
}
/***********************************************************************
* DCE_OffsetVisRgn
*
* Change region from DC-origin relative coordinates to screen coords.
*/
static void DCE_OffsetVisRgn( HDC hDC, HRGN hVisRgn )
{
DC *dc;
if (!(dc = DC_GetDCPtr( hDC ))) return;
OffsetRgn( hVisRgn, dc->DCOrgX, dc->DCOrgY );
GDI_ReleaseObj( hDC );
}
/***********************************************************************
* DCE_GetVisRect
*
* Calculate the visible rectangle of a window (i.e. the client or
* window area clipped by the client area of all ancestors) in the
* corresponding coordinates. Return FALSE if the visible region is empty.
*/
static BOOL DCE_GetVisRect( WND *wndPtr, BOOL clientArea, RECT *lprect )
{
*lprect = clientArea ? wndPtr->rectClient : wndPtr->rectWindow;
if (wndPtr->dwStyle & WS_VISIBLE)
{
INT xoffset = lprect->left;
INT yoffset = lprect->top;
while ((wndPtr = WIN_FindWndPtr( GetAncestor(wndPtr->hwndSelf,GA_PARENT) )))
{
if ( (wndPtr->dwStyle & (WS_ICONIC | WS_VISIBLE)) != WS_VISIBLE )
{
WIN_ReleaseWndPtr(wndPtr);
goto fail;
}
xoffset += wndPtr->rectClient.left;
yoffset += wndPtr->rectClient.top;
OffsetRect( lprect, wndPtr->rectClient.left,
wndPtr->rectClient.top );
if( (wndPtr->rectClient.left >= wndPtr->rectClient.right) ||
(wndPtr->rectClient.top >= wndPtr->rectClient.bottom) ||
(lprect->left >= wndPtr->rectClient.right) ||
(lprect->right <= wndPtr->rectClient.left) ||
(lprect->top >= wndPtr->rectClient.bottom) ||
(lprect->bottom <= wndPtr->rectClient.top) )
{
WIN_ReleaseWndPtr(wndPtr);
goto fail;
}
lprect->left = max( lprect->left, wndPtr->rectClient.left );
lprect->right = min( lprect->right, wndPtr->rectClient.right );
lprect->top = max( lprect->top, wndPtr->rectClient.top );
lprect->bottom = min( lprect->bottom, wndPtr->rectClient.bottom );
WIN_ReleaseWndPtr(wndPtr);
}
OffsetRect( lprect, -xoffset, -yoffset );
return TRUE;
}
fail:
SetRectEmpty( lprect );
return FALSE;
}
/***********************************************************************
* DCE_AddClipRects
*
* Go through the linked list of windows from pWndStart to pWndEnd,
* adding to the clip region the intersection of the target rectangle
* with an offset window rectangle.
*/
static void DCE_AddClipRects( HWND parent, HWND end, HRGN hrgnClip, LPRECT lpRect, int x, int y )
{
RECT rect;
WND *pWnd;
int i;
HWND *list = WIN_ListChildren( parent );
if (!list) return;
for (i = 0; list[i]; i++)
{
if (list[i] == end) break;
if (!(pWnd = WIN_FindWndPtr( list[i] ))) continue;
if (pWnd->dwStyle & WS_VISIBLE)
{
rect.left = pWnd->rectWindow.left + x;
rect.top = pWnd->rectWindow.top + y;
rect.right = pWnd->rectWindow.right + x;
rect.bottom = pWnd->rectWindow.bottom + y;
if( IntersectRect( &rect, &rect, lpRect ))
{
if(!REGION_UnionRectWithRgn( hrgnClip, &rect ))
{
WIN_ReleaseWndPtr( pWnd );
break;
}
}
}
WIN_ReleaseWndPtr( pWnd );
}
HeapFree( GetProcessHeap(), 0, list );
}
/***********************************************************************
* DCE_GetVisRgn
*
* Return the visible region of a window, i.e. the client or window area
* clipped by the client area of all ancestors, and then optionally
* by siblings and children.
*/
static HRGN DCE_GetVisRgn( HWND hwnd, WORD flags, HWND hwndChild, WORD cflags )
{
HRGN hrgnVis = 0;
RECT rect;
WND *wndPtr = WIN_FindWndPtr( hwnd );
WND *childWnd = WIN_FindWndPtr( hwndChild );
/* Get visible rectangle and create a region with it. */
if (wndPtr && DCE_GetVisRect(wndPtr, !(flags & DCX_WINDOW), &rect))
{
if((hrgnVis = CreateRectRgnIndirect( &rect )))
{
HRGN hrgnClip = CreateRectRgn( 0, 0, 0, 0 );
INT xoffset, yoffset;
if( hrgnClip )
{
/* Compute obscured region for the visible rectangle by
* clipping children, siblings, and ancestors. Note that
* DCE_GetVisRect() returns a rectangle either in client
* or in window coordinates (for DCX_WINDOW request). */
if (flags & DCX_CLIPCHILDREN)
{
if( flags & DCX_WINDOW )
{
/* adjust offsets since child window rectangles are
* in client coordinates */
xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
}
else
xoffset = yoffset = 0;
DCE_AddClipRects( wndPtr->hwndSelf, 0, hrgnClip, &rect, xoffset, yoffset );
}
/* We may need to clip children of child window, if a window with PARENTDC
* class style and CLIPCHILDREN window style (like in Free Agent 16
* preference dialogs) gets here, we take the region for the parent window
* but apparently still need to clip the children of the child window... */
if( (cflags & DCX_CLIPCHILDREN) && childWnd)
{
if( flags & DCX_WINDOW )
{
/* adjust offsets since child window rectangles are
* in client coordinates */
xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
}
else
xoffset = yoffset = 0;
/* client coordinates of child window */
xoffset += childWnd->rectClient.left;
yoffset += childWnd->rectClient.top;
DCE_AddClipRects( childWnd->hwndSelf, 0, hrgnClip,
&rect, xoffset, yoffset );
}
/* sibling window rectangles are in client
* coordinates of the parent window */
if (flags & DCX_WINDOW)
{
xoffset = -wndPtr->rectWindow.left;
yoffset = -wndPtr->rectWindow.top;
}
else
{
xoffset = -wndPtr->rectClient.left;
yoffset = -wndPtr->rectClient.top;
}
if (flags & DCX_CLIPSIBLINGS && wndPtr->parent )
DCE_AddClipRects( wndPtr->parent, wndPtr->hwndSelf,
hrgnClip, &rect, xoffset, yoffset );
/* Clip siblings of all ancestors that have the
* WS_CLIPSIBLINGS style
*/
while (wndPtr->parent)
{
WND *ptr = WIN_FindWndPtr( wndPtr->parent );
WIN_ReleaseWndPtr( wndPtr );
wndPtr = ptr;
xoffset -= wndPtr->rectClient.left;
yoffset -= wndPtr->rectClient.top;
if(wndPtr->dwStyle & WS_CLIPSIBLINGS && wndPtr->parent)
{
DCE_AddClipRects( wndPtr->parent, wndPtr->hwndSelf,
hrgnClip, &rect, xoffset, yoffset );
}
}
/* Now once we've got a jumbo clip region we have
* to substract it from the visible rectangle.
*/
CombineRgn( hrgnVis, hrgnVis, hrgnClip, RGN_DIFF );
DeleteObject( hrgnClip );
}
else
{
DeleteObject( hrgnVis );
hrgnVis = 0;
}
}
}
else
hrgnVis = CreateRectRgn(0, 0, 0, 0); /* empty */
WIN_ReleaseWndPtr(wndPtr);
WIN_ReleaseWndPtr(childWnd);
return hrgnVis;
}
/***********************************************************************
* GetDC (TTYDRV.@)
*
* Set the drawable, origin and dimensions for the DC associated to
* a given window.
*/
BOOL TTYDRV_GetDC( HWND hwnd, HDC hdc, HRGN hrgn, DWORD flags )
{
WND *wndPtr = WIN_FindWndPtr(hwnd);
DC *dc;
BOOL updateVisRgn;
HRGN hrgnVisible = 0;
if (!wndPtr) return FALSE;
if (!(dc = DC_GetDCPtr( hdc )))
{
WIN_ReleaseWndPtr( wndPtr );
return FALSE;
}
if(flags & DCX_WINDOW)
{
dc->DCOrgX = wndPtr->rectWindow.left;
dc->DCOrgY = wndPtr->rectWindow.top;
}
else
{
dc->DCOrgX = wndPtr->rectClient.left;
dc->DCOrgY = wndPtr->rectClient.top;
}
updateVisRgn = (dc->flags & DC_DIRTY) != 0;
GDI_ReleaseObj( hdc );
if (updateVisRgn)
{
if (flags & DCX_PARENTCLIP)
{
WND *parentPtr = WIN_FindWndPtr( wndPtr->parent );
if( wndPtr->dwStyle & WS_VISIBLE && !(parentPtr->dwStyle & WS_MINIMIZE) )
{
DWORD dcxFlags;
if( parentPtr->dwStyle & WS_CLIPSIBLINGS )
dcxFlags = DCX_CLIPSIBLINGS | (flags & ~(DCX_CLIPCHILDREN | DCX_WINDOW));
else
dcxFlags = flags & ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW);
hrgnVisible = DCE_GetVisRgn( parentPtr->hwndSelf, dcxFlags,
wndPtr->hwndSelf, flags );
if( flags & DCX_WINDOW )
OffsetRgn( hrgnVisible, -wndPtr->rectWindow.left,
-wndPtr->rectWindow.top );
else
OffsetRgn( hrgnVisible, -wndPtr->rectClient.left,
-wndPtr->rectClient.top );
DCE_OffsetVisRgn( hdc, hrgnVisible );
}
else
hrgnVisible = CreateRectRgn( 0, 0, 0, 0 );
WIN_ReleaseWndPtr(parentPtr);
}
else
{
hrgnVisible = DCE_GetVisRgn( hwnd, flags, 0, 0 );
DCE_OffsetVisRgn( hdc, hrgnVisible );
}
SelectVisRgn16( hdc, hrgnVisible );
}
/* apply additional region operation (if any) */
if( flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN) )
{
if( !hrgnVisible ) hrgnVisible = CreateRectRgn( 0, 0, 0, 0 );
TRACE("\tsaved VisRgn, clipRgn = %04x\n", hrgn);
SaveVisRgn16( hdc );
CombineRgn( hrgnVisible, hrgn, 0, RGN_COPY );
DCE_OffsetVisRgn( hdc, hrgnVisible );
CombineRgn( hrgnVisible, InquireVisRgn16( hdc ), hrgnVisible,
(flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );
SelectVisRgn16( hdc, hrgnVisible );
}
if (hrgnVisible) DeleteObject( hrgnVisible );
WIN_ReleaseWndPtr( wndPtr );
return TRUE;
}
/***********************************************************************
* SetWindowPos (TTYDRV.@)
*/
BOOL TTYDRV_SetWindowPos( WINDOWPOS *winpos )
{
WND *wndPtr;
RECT newWindowRect, newClientRect;
BOOL retvalue;
HWND hwndActive = GetForegroundWindow();
TRACE( "hwnd %04x, swp (%i,%i)-(%i,%i) flags %08x\n",
winpos->hwnd, winpos->x, winpos->y,
winpos->x + winpos->cx, winpos->y + winpos->cy, winpos->flags);
/* ------------------------------------------------------------------------ CHECKS */
/* Check window handle */
if (winpos->hwnd == GetDesktopWindow()) return FALSE;
if (!(wndPtr = WIN_FindWndPtr( winpos->hwnd ))) return FALSE;
TRACE("\tcurrent (%i,%i)-(%i,%i), style %08x\n",
wndPtr->rectWindow.left, wndPtr->rectWindow.top,
wndPtr->rectWindow.right, wndPtr->rectWindow.bottom, (unsigned)wndPtr->dwStyle );
/* Fix redundant flags */
if(wndPtr->dwStyle & WS_VISIBLE)
winpos->flags &= ~SWP_SHOWWINDOW;
else
{
if (!(winpos->flags & SWP_SHOWWINDOW)) winpos->flags |= SWP_NOREDRAW;
winpos->flags &= ~SWP_HIDEWINDOW;
}
if ( winpos->cx < 0 ) winpos->cx = 0;
if ( winpos->cy < 0 ) winpos->cy = 0;
if ((wndPtr->rectWindow.right - wndPtr->rectWindow.left == winpos->cx) &&
(wndPtr->rectWindow.bottom - wndPtr->rectWindow.top == winpos->cy))
winpos->flags |= SWP_NOSIZE; /* Already the right size */
if ((wndPtr->rectWindow.left == winpos->x) && (wndPtr->rectWindow.top == winpos->y))
winpos->flags |= SWP_NOMOVE; /* Already the right position */
if (winpos->hwnd == hwndActive)
winpos->flags |= SWP_NOACTIVATE; /* Already active */
else if ( (wndPtr->dwStyle & (WS_POPUP | WS_CHILD)) != WS_CHILD )
{
if(!(winpos->flags & SWP_NOACTIVATE)) /* Bring to the top when activating */
{
winpos->flags &= ~SWP_NOZORDER;
winpos->hwndInsertAfter = HWND_TOP;
goto Pos;
}
}
/* Check hwndInsertAfter */
/* FIXME: TOPMOST not supported yet */
if ((winpos->hwndInsertAfter == HWND_TOPMOST) ||
(winpos->hwndInsertAfter == HWND_NOTOPMOST)) winpos->hwndInsertAfter = HWND_TOP;
/* hwndInsertAfter must be a sibling of the window */
if ((winpos->hwndInsertAfter != HWND_TOP) && (winpos->hwndInsertAfter != HWND_BOTTOM))
{
WND* wnd = WIN_FindWndPtr(winpos->hwndInsertAfter);
if( wnd ) {
if( wnd->parent != wndPtr->parent )
{
retvalue = FALSE;
WIN_ReleaseWndPtr(wnd);
goto END;
}
/* don't need to change the Zorder of hwnd if it's already inserted
* after hwndInsertAfter or when inserting hwnd after itself.
*/
if ((winpos->hwnd == winpos->hwndInsertAfter) ||
(winpos->hwnd == GetWindow( winpos->hwndInsertAfter, GW_HWNDNEXT )))
winpos->flags |= SWP_NOZORDER;
}
WIN_ReleaseWndPtr(wnd);
}
Pos: /* ------------------------------------------------------------------------ MAIN part */
/* Send WM_WINDOWPOSCHANGING message */
if (!(winpos->flags & SWP_NOSENDCHANGING))
SendMessageA( wndPtr->hwndSelf, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos );
/* Calculate new position and size */
newWindowRect = wndPtr->rectWindow;
newClientRect = (wndPtr->dwStyle & WS_MINIMIZE) ? wndPtr->rectWindow
: wndPtr->rectClient;
if (!(winpos->flags & SWP_NOSIZE))
{
newWindowRect.right = newWindowRect.left + winpos->cx;
newWindowRect.bottom = newWindowRect.top + winpos->cy;
}
if (!(winpos->flags & SWP_NOMOVE))
{
newWindowRect.left = winpos->x;
newWindowRect.top = winpos->y;
newWindowRect.right += winpos->x - wndPtr->rectWindow.left;
newWindowRect.bottom += winpos->y - wndPtr->rectWindow.top;
OffsetRect( &newClientRect, winpos->x - wndPtr->rectWindow.left,
winpos->y - wndPtr->rectWindow.top );
}
if( winpos->hwndInsertAfter == HWND_TOP )
{
if (GetWindow( wndPtr->hwndSelf, GW_HWNDFIRST ) == wndPtr->hwndSelf)
winpos->flags |= SWP_NOZORDER;
}
else
if( winpos->hwndInsertAfter == HWND_BOTTOM )
{
if (!GetWindow( wndPtr->hwndSelf, GW_HWNDNEXT ))
winpos->flags |= SWP_NOZORDER;
}
else
if( !(winpos->flags & SWP_NOZORDER) )
if( GetWindow(winpos->hwndInsertAfter, GW_HWNDNEXT) == wndPtr->hwndSelf )
winpos->flags |= SWP_NOZORDER;
/* Common operations */
/* Send WM_NCCALCSIZE message to get new client area */
if( (winpos->flags & (SWP_FRAMECHANGED | SWP_NOSIZE)) != SWP_NOSIZE )
{
NCCALCSIZE_PARAMS params;
WINDOWPOS winposCopy;
params.rgrc[0] = newWindowRect;
params.rgrc[1] = wndPtr->rectWindow;
params.rgrc[2] = wndPtr->rectClient;
params.lppos = &winposCopy;
winposCopy = *winpos;
SendMessageW( winpos->hwnd, WM_NCCALCSIZE, TRUE, (LPARAM)&params );
TRACE( "%d,%d-%d,%d\n", params.rgrc[0].left, params.rgrc[0].top,
params.rgrc[0].right, params.rgrc[0].bottom );
/* If the application send back garbage, ignore it */
if (params.rgrc[0].left <= params.rgrc[0].right &&
params.rgrc[0].top <= params.rgrc[0].bottom)
newClientRect = params.rgrc[0];
/* FIXME: WVR_ALIGNxxx */
if( newClientRect.left != wndPtr->rectClient.left ||
newClientRect.top != wndPtr->rectClient.top )
winpos->flags &= ~SWP_NOCLIENTMOVE;
if( (newClientRect.right - newClientRect.left !=
wndPtr->rectClient.right - wndPtr->rectClient.left) ||
(newClientRect.bottom - newClientRect.top !=
wndPtr->rectClient.bottom - wndPtr->rectClient.top) )
winpos->flags &= ~SWP_NOCLIENTSIZE;
}
if(!(winpos->flags & SWP_NOZORDER) && winpos->hwnd != winpos->hwndInsertAfter)
{
HWND parent = GetAncestor( winpos->hwnd, GA_PARENT );
if (parent) WIN_LinkWindow( winpos->hwnd, parent, winpos->hwndInsertAfter );
}
/* FIXME: actually do something with WVR_VALIDRECTS */
WIN_SetRectangles( winpos->hwnd, &newWindowRect, &newClientRect );
if( winpos->flags & SWP_SHOWWINDOW )
{
wndPtr->dwStyle |= WS_VISIBLE;
}
else if( winpos->flags & SWP_HIDEWINDOW )
{
wndPtr->dwStyle &= ~WS_VISIBLE;
}
/* ------------------------------------------------------------------------ FINAL */
/* repaint invalidated region (if any)
*
* FIXME: if SWP_NOACTIVATE is not set then set invalid regions here without any painting
* and force update after ChangeActiveWindow() to avoid painting frames twice.
*/
if( !(winpos->flags & SWP_NOREDRAW) )
{
RedrawWindow( wndPtr->parent, NULL, 0,
RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN );
if (wndPtr->parent == GetDesktopWindow())
RedrawWindow( wndPtr->parent, NULL, 0,
RDW_ERASENOW | RDW_NOCHILDREN );
}
if (!(winpos->flags & SWP_NOACTIVATE))
WINPOS_ChangeActiveWindow( winpos->hwnd, FALSE );
/* And last, send the WM_WINDOWPOSCHANGED message */
TRACE("\tstatus flags = %04x\n", winpos->flags & SWP_AGG_STATUSFLAGS);
if ((((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE) &&
!(winpos->flags & SWP_NOSENDCHANGING)) )
SendMessageA( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos );
retvalue = TRUE;
END:
WIN_ReleaseWndPtr(wndPtr);
return retvalue;
}