mirror of
https://github.com/reactos/wine.git
synced 2024-11-25 12:49:45 +00:00
d8a9244fc4
Removed some direct accesses to the bitmap structure. Fixed handling of stock bitmap if selected in multiple DCs. Some minor fixes to the DC function table.
1445 lines
43 KiB
C
1445 lines
43 KiB
C
/*
|
|
* Window painting functions
|
|
*
|
|
* Copyright 1993, 1994, 1995 Alexandre Julliard
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "windef.h"
|
|
#include "wingdi.h"
|
|
#include "wine/winuser16.h"
|
|
#include "wine/unicode.h"
|
|
#include "wine/server.h"
|
|
#include "gdi.h"
|
|
#include "user.h"
|
|
#include "win.h"
|
|
#include "queue.h"
|
|
#include "dce.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(win);
|
|
WINE_DECLARE_DEBUG_CHANNEL(nonclient);
|
|
|
|
/* client rect in window coordinates */
|
|
|
|
#define GETCLIENTRECTW( wnd, r ) (r).left = (wnd)->rectClient.left - (wnd)->rectWindow.left; \
|
|
(r).top = (wnd)->rectClient.top - (wnd)->rectWindow.top; \
|
|
(r).right = (wnd)->rectClient.right - (wnd)->rectWindow.left; \
|
|
(r).bottom = (wnd)->rectClient.bottom - (wnd)->rectWindow.top
|
|
|
|
/* PAINT_RedrawWindow() control flags */
|
|
#define RDW_EX_DELAY_NCPAINT 0x0020
|
|
|
|
/* WIN_UpdateNCRgn() flags */
|
|
#define UNC_CHECK 0x0001
|
|
#define UNC_ENTIRE 0x0002
|
|
#define UNC_REGION 0x0004
|
|
#define UNC_UPDATE 0x0008
|
|
#define UNC_DELAY_NCPAINT 0x0010
|
|
#define UNC_IN_BEGINPAINT 0x0020
|
|
|
|
/* Last COLOR id */
|
|
#define COLOR_MAX COLOR_GRADIENTINACTIVECAPTION
|
|
|
|
|
|
/* ### start build ### */
|
|
extern WORD CALLBACK PAINTING_CallTo16_word_wlwww(DRAWSTATEPROC16,WORD,LONG,WORD,WORD,WORD);
|
|
/* ### stop build ### */
|
|
|
|
struct draw_state_info
|
|
{
|
|
DRAWSTATEPROC16 proc;
|
|
LPARAM param;
|
|
};
|
|
|
|
/* callback for 16-bit DrawState functions */
|
|
static BOOL CALLBACK draw_state_callback( HDC hdc, LPARAM lparam, WPARAM wparam, int cx, int cy )
|
|
{
|
|
const struct draw_state_info *info = (struct draw_state_info *)lparam;
|
|
return PAINTING_CallTo16_word_wlwww( info->proc, hdc, info->param, wparam, cx, cy );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* add_paint_count
|
|
*
|
|
* Add an increment (normally 1 or -1) to the current paint count of a window.
|
|
*/
|
|
static void add_paint_count( HWND hwnd, int incr )
|
|
{
|
|
SERVER_START_REQ( inc_window_paint_count )
|
|
{
|
|
req->handle = hwnd;
|
|
req->incr = incr;
|
|
wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* WIN_HaveToDelayNCPAINT
|
|
*
|
|
* Currently, in the Wine painting mechanism, the WM_NCPAINT message
|
|
* is generated as soon as a region intersecting the non-client area
|
|
* of a window is invalidated.
|
|
*
|
|
* This technique will work fine for all windows whose parents
|
|
* have the WS_CLIPCHILDREN style. When the parents have that style,
|
|
* they are not going to override the contents of their children.
|
|
* However, when the parent doesn't have that style, Windows relies
|
|
* on a "painter's algorithm" to display the contents of the windows.
|
|
* That is, windows are painted from back to front. This includes the
|
|
* non-client area.
|
|
*
|
|
* This method looks at the current state of a window to determine
|
|
* if the sending of the WM_NCPAINT message should be delayed until
|
|
* the BeginPaint call.
|
|
*
|
|
* PARAMS:
|
|
* wndPtr - A Locked window pointer to the window we're
|
|
* examining.
|
|
* uncFlags - This is the flag passed to the WIN_UpdateNCRgn
|
|
* function. This is a shortcut for the cases when
|
|
* we already know when to avoid scanning all the
|
|
* parents of a window. If you already know that this
|
|
* window's NCPAINT should be delayed, set the
|
|
* UNC_DELAY_NCPAINT flag for this parameter.
|
|
*
|
|
* This shortcut behavior is implemented in the
|
|
* RDW_Paint() method.
|
|
*
|
|
*/
|
|
static BOOL WIN_HaveToDelayNCPAINT( HWND hwnd, UINT uncFlags)
|
|
{
|
|
/*
|
|
* Test the shortcut first. (duh)
|
|
*/
|
|
if (uncFlags & UNC_DELAY_NCPAINT)
|
|
return TRUE;
|
|
|
|
/*
|
|
* The UNC_IN_BEGINPAINT flag is set in the BeginPaint
|
|
* method only. This is another shortcut to avoid going
|
|
* up the parent's chain of the window to finally
|
|
* figure-out that none of them has an invalid region.
|
|
*/
|
|
if (uncFlags & UNC_IN_BEGINPAINT)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Scan all the parents of this window to find a window
|
|
* that doesn't have the WS_CLIPCHILDREN style and that
|
|
* has an invalid region.
|
|
*/
|
|
while ((hwnd = GetAncestor( hwnd, GA_PARENT )))
|
|
{
|
|
WND* parentWnd = WIN_FindWndPtr( hwnd );
|
|
if (!(parentWnd->dwStyle & WS_CLIPCHILDREN) && parentWnd->hrgnUpdate)
|
|
{
|
|
WIN_ReleaseWndPtr( parentWnd );
|
|
return TRUE;
|
|
}
|
|
WIN_ReleaseWndPtr( parentWnd );
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WIN_UpdateNCRgn
|
|
*
|
|
* Things to do:
|
|
* Send WM_NCPAINT if required (when nonclient is invalid or UNC_ENTIRE flag is set)
|
|
* Crop hrgnUpdate to a client rect, especially if it 1.
|
|
* If UNC_REGION is set return update region for the client rect.
|
|
*
|
|
* NOTE: UNC_REGION is mainly for the RDW_Paint() chunk that sends WM_ERASEBKGND message.
|
|
* The trick is that when the returned region handle may be different from hRgn.
|
|
* In this case the old hRgn must be considered gone. BUT, if the returned value
|
|
* is 1 then the hRgn is preserved and RDW_Paint() will have to get
|
|
* a DC without extra clipping region.
|
|
*/
|
|
static HRGN WIN_UpdateNCRgn(WND* wnd, HRGN hRgn, UINT uncFlags )
|
|
{
|
|
RECT r;
|
|
HRGN hClip = 0;
|
|
HRGN hrgnRet = 0;
|
|
|
|
TRACE_(nonclient)("hwnd %04x [%04x] hrgn %04x, unc %04x, ncf %i\n",
|
|
wnd->hwndSelf, wnd->hrgnUpdate, hRgn, uncFlags, wnd->flags & WIN_NEEDS_NCPAINT);
|
|
|
|
/* desktop window doesn't have a nonclient area */
|
|
if(wnd->hwndSelf == GetDesktopWindow())
|
|
{
|
|
wnd->flags &= ~WIN_NEEDS_NCPAINT;
|
|
if( wnd->hrgnUpdate > 1 )
|
|
hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
|
|
else
|
|
{
|
|
hrgnRet = wnd->hrgnUpdate;
|
|
}
|
|
return hrgnRet;
|
|
}
|
|
|
|
if ((wnd->hwndSelf == GetForegroundWindow()) &&
|
|
!(wnd->flags & WIN_NCACTIVATED) )
|
|
{
|
|
wnd->flags |= WIN_NCACTIVATED;
|
|
uncFlags |= UNC_ENTIRE;
|
|
}
|
|
|
|
/*
|
|
* If the window's non-client area needs to be painted,
|
|
*/
|
|
if ( ( wnd->flags & WIN_NEEDS_NCPAINT ) &&
|
|
!WIN_HaveToDelayNCPAINT(wnd->hwndSelf, uncFlags) )
|
|
{
|
|
RECT r2, r3;
|
|
|
|
wnd->flags &= ~WIN_NEEDS_NCPAINT;
|
|
GETCLIENTRECTW( wnd, r );
|
|
|
|
TRACE_(nonclient)( "\tclient box (%i,%i-%i,%i), hrgnUpdate %04x\n",
|
|
r.left, r.top, r.right, r.bottom, wnd->hrgnUpdate );
|
|
if( wnd->hrgnUpdate > 1 )
|
|
{
|
|
/* Check if update rgn overlaps with nonclient area */
|
|
|
|
GetRgnBox( wnd->hrgnUpdate, &r2 );
|
|
UnionRect( &r3, &r2, &r );
|
|
if( r3.left != r.left || r3.top != r.top ||
|
|
r3.right != r.right || r3.bottom != r.bottom ) /* it does */
|
|
{
|
|
/* crop hrgnUpdate, save old one in hClip - the only
|
|
* case that places a valid region handle in hClip */
|
|
|
|
hClip = wnd->hrgnUpdate;
|
|
wnd->hrgnUpdate = REGION_CropRgn( hRgn, hClip, &r, NULL );
|
|
if( uncFlags & UNC_REGION ) hrgnRet = hClip;
|
|
}
|
|
|
|
if( uncFlags & UNC_CHECK )
|
|
{
|
|
GetRgnBox( wnd->hrgnUpdate, &r3 );
|
|
if( IsRectEmpty( &r3 ) )
|
|
{
|
|
/* delete the update region since all invalid
|
|
* parts were in the nonclient area */
|
|
|
|
DeleteObject( wnd->hrgnUpdate );
|
|
wnd->hrgnUpdate = 0;
|
|
if(!(wnd->flags & WIN_INTERNAL_PAINT))
|
|
add_paint_count( wnd->hwndSelf, -1 );
|
|
|
|
wnd->flags &= ~WIN_NEEDS_ERASEBKGND;
|
|
}
|
|
}
|
|
|
|
if(!hClip && wnd->hrgnUpdate ) goto copyrgn;
|
|
}
|
|
else
|
|
if( wnd->hrgnUpdate == 1 )/* entire window */
|
|
{
|
|
if( uncFlags & UNC_UPDATE ) wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
|
|
if( uncFlags & UNC_REGION ) hrgnRet = 1;
|
|
uncFlags |= UNC_ENTIRE;
|
|
}
|
|
}
|
|
else /* no WM_NCPAINT unless forced */
|
|
{
|
|
if( wnd->hrgnUpdate > 1 )
|
|
{
|
|
copyrgn:
|
|
if( uncFlags & UNC_REGION )
|
|
hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
|
|
}
|
|
else
|
|
if( wnd->hrgnUpdate == 1 && (uncFlags & UNC_UPDATE) )
|
|
{
|
|
GETCLIENTRECTW( wnd, r );
|
|
wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
|
|
if( uncFlags & UNC_REGION ) hrgnRet = 1;
|
|
}
|
|
}
|
|
|
|
if(!hClip && (uncFlags & UNC_ENTIRE) )
|
|
{
|
|
/* still don't do anything if there is no nonclient area */
|
|
hClip = (memcmp( &wnd->rectWindow, &wnd->rectClient, sizeof(RECT) ) != 0);
|
|
}
|
|
|
|
if( hClip ) /* NOTE: WM_NCPAINT allows wParam to be 1 */
|
|
{
|
|
if ( hClip == hrgnRet && hrgnRet > 1 ) {
|
|
hClip = CreateRectRgn( 0, 0, 0, 0 );
|
|
CombineRgn( hClip, hrgnRet, 0, RGN_COPY );
|
|
}
|
|
|
|
SendMessageA( wnd->hwndSelf, WM_NCPAINT, hClip, 0L );
|
|
if( (hClip > 1) && (hClip != hRgn) && (hClip != hrgnRet) )
|
|
DeleteObject( hClip );
|
|
/*
|
|
* Since all Window locks are suspended while processing the WM_NCPAINT
|
|
* we want to make sure the window still exists before continuing.
|
|
*/
|
|
if (!IsWindow(wnd->hwndSelf))
|
|
{
|
|
DeleteObject(hrgnRet);
|
|
hrgnRet=0;
|
|
}
|
|
}
|
|
|
|
TRACE_(nonclient)("returning %04x (hClip = %04x, hrgnUpdate = %04x)\n", hrgnRet, hClip, wnd->hrgnUpdate );
|
|
|
|
return hrgnRet;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* RDW_ValidateParent [RDW_UpdateRgns() helper]
|
|
*
|
|
* Validate the portions of parents that are covered by a validated child
|
|
* wndPtr = child
|
|
*/
|
|
static void RDW_ValidateParent(WND *wndChild)
|
|
{
|
|
HWND parent;
|
|
HRGN hrg;
|
|
|
|
if (wndChild->hrgnUpdate == 1 ) {
|
|
RECT r;
|
|
r.left = 0;
|
|
r.top = 0;
|
|
r.right = wndChild->rectWindow.right - wndChild->rectWindow.left;
|
|
r.bottom = wndChild->rectWindow.bottom - wndChild->rectWindow.top;
|
|
hrg = CreateRectRgnIndirect( &r );
|
|
} else
|
|
hrg = wndChild->hrgnUpdate;
|
|
|
|
parent = GetAncestor( wndChild->hwndSelf, GA_PARENT );
|
|
while (parent && parent != GetDesktopWindow())
|
|
{
|
|
WND *wndParent = WIN_FindWndPtr( parent );
|
|
if (!(wndParent->dwStyle & WS_CLIPCHILDREN))
|
|
{
|
|
if (wndParent->hrgnUpdate != 0)
|
|
{
|
|
POINT ptOffset;
|
|
RECT rect, rectParent;
|
|
if( wndParent->hrgnUpdate == 1 )
|
|
{
|
|
RECT r;
|
|
|
|
r.left = 0;
|
|
r.top = 0;
|
|
r.right = wndParent->rectWindow.right - wndParent->rectWindow.left;
|
|
r.bottom = wndParent->rectWindow.bottom - wndParent->rectWindow.top;
|
|
|
|
wndParent->hrgnUpdate = CreateRectRgnIndirect( &r );
|
|
}
|
|
/* we must offset the child region by the offset of the child rect in the parent */
|
|
GetWindowRect(wndParent->hwndSelf, &rectParent);
|
|
GetWindowRect(wndChild->hwndSelf, &rect);
|
|
ptOffset.x = rect.left - rectParent.left;
|
|
ptOffset.y = rect.top - rectParent.top;
|
|
OffsetRgn( hrg, ptOffset.x, ptOffset.y );
|
|
CombineRgn( wndParent->hrgnUpdate, wndParent->hrgnUpdate, hrg, RGN_DIFF );
|
|
OffsetRgn( hrg, -ptOffset.x, -ptOffset.y );
|
|
}
|
|
}
|
|
WIN_ReleaseWndPtr( wndParent );
|
|
parent = GetAncestor( parent, GA_PARENT );
|
|
}
|
|
if (hrg != wndChild->hrgnUpdate) DeleteObject( hrg );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RDW_UpdateRgns [RedrawWindow() helper]
|
|
*
|
|
* Walks the window tree and adds/removes parts of the hRgn to/from update
|
|
* regions of windows that overlap it. Also, manages internal paint flags.
|
|
*
|
|
* NOTE: Walks the window tree so the caller must lock it.
|
|
* MUST preserve hRgn (can modify but then has to restore).
|
|
*/
|
|
static void RDW_UpdateRgns( WND* wndPtr, HRGN hRgn, UINT flags, BOOL firstRecursLevel )
|
|
{
|
|
/*
|
|
* Called only when one of the following is set:
|
|
* (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT)
|
|
*/
|
|
|
|
BOOL bHadOne = wndPtr->hrgnUpdate && hRgn;
|
|
BOOL bChildren = (!(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) &&
|
|
((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) );
|
|
RECT r;
|
|
|
|
r.left = 0;
|
|
r.top = 0;
|
|
r.right = wndPtr->rectWindow.right - wndPtr->rectWindow.left;
|
|
r.bottom = wndPtr->rectWindow.bottom - wndPtr->rectWindow.top;
|
|
|
|
TRACE("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", wndPtr->hwndSelf, wndPtr->hrgnUpdate, hRgn, flags );
|
|
|
|
if( flags & RDW_INVALIDATE )
|
|
{
|
|
if( hRgn > 1 )
|
|
{
|
|
switch( wndPtr->hrgnUpdate )
|
|
{
|
|
default:
|
|
CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_OR );
|
|
/* fall through */
|
|
case 0:
|
|
wndPtr->hrgnUpdate = REGION_CropRgn( wndPtr->hrgnUpdate,
|
|
wndPtr->hrgnUpdate ? wndPtr->hrgnUpdate : hRgn,
|
|
&r, NULL );
|
|
if( !bHadOne )
|
|
{
|
|
GetRgnBox( wndPtr->hrgnUpdate, &r );
|
|
if( IsRectEmpty( &r ) )
|
|
{
|
|
DeleteObject( wndPtr->hrgnUpdate );
|
|
wndPtr->hrgnUpdate = 0;
|
|
goto end;
|
|
}
|
|
}
|
|
break;
|
|
case 1: /* already an entire window */
|
|
break;
|
|
}
|
|
}
|
|
else if( hRgn == 1 )
|
|
{
|
|
if( wndPtr->hrgnUpdate > 1 )
|
|
DeleteObject( wndPtr->hrgnUpdate );
|
|
wndPtr->hrgnUpdate = 1;
|
|
}
|
|
else
|
|
hRgn = wndPtr->hrgnUpdate; /* this is a trick that depends on code in PAINT_RedrawWindow() */
|
|
|
|
if( !bHadOne && !(wndPtr->flags & WIN_INTERNAL_PAINT) )
|
|
add_paint_count( wndPtr->hwndSelf, 1 );
|
|
|
|
if (flags & RDW_FRAME) wndPtr->flags |= WIN_NEEDS_NCPAINT;
|
|
if (flags & RDW_ERASE) wndPtr->flags |= WIN_NEEDS_ERASEBKGND;
|
|
flags |= RDW_FRAME;
|
|
}
|
|
else if( flags & RDW_VALIDATE )
|
|
{
|
|
if( wndPtr->hrgnUpdate )
|
|
{
|
|
if( hRgn > 1 )
|
|
{
|
|
if( wndPtr->hrgnUpdate == 1 )
|
|
wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r );
|
|
|
|
if( CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_DIFF )
|
|
== NULLREGION )
|
|
{
|
|
DeleteObject( wndPtr->hrgnUpdate );
|
|
wndPtr->hrgnUpdate = 0;
|
|
}
|
|
}
|
|
else /* validate everything */
|
|
{
|
|
if( wndPtr->hrgnUpdate > 1 ) DeleteObject( wndPtr->hrgnUpdate );
|
|
wndPtr->hrgnUpdate = 0;
|
|
}
|
|
|
|
if( !wndPtr->hrgnUpdate )
|
|
{
|
|
wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
|
|
if( !(wndPtr->flags & WIN_INTERNAL_PAINT) )
|
|
add_paint_count( wndPtr->hwndSelf, -1 );
|
|
}
|
|
}
|
|
|
|
if (flags & RDW_NOFRAME) wndPtr->flags &= ~WIN_NEEDS_NCPAINT;
|
|
if (flags & RDW_NOERASE) wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
|
|
|
|
}
|
|
|
|
if ((firstRecursLevel) && (wndPtr->hrgnUpdate != 0) && (flags & RDW_UPDATENOW))
|
|
RDW_ValidateParent(wndPtr); /* validate parent covered by region */
|
|
|
|
/* in/validate child windows that intersect with the region if it
|
|
* is a valid handle. */
|
|
|
|
if( flags & (RDW_INVALIDATE | RDW_VALIDATE) )
|
|
{
|
|
HWND *list;
|
|
if( hRgn > 1 && bChildren && (list = WIN_ListChildren( wndPtr->hwndSelf )))
|
|
{
|
|
POINT ptTotal, prevOrigin = {0,0};
|
|
POINT ptClient;
|
|
INT i;
|
|
|
|
ptClient.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
|
|
ptClient.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
|
|
|
|
for(i = ptTotal.x = ptTotal.y = 0; list[i]; i++)
|
|
{
|
|
WND *wnd = WIN_FindWndPtr( list[i] );
|
|
if (!wnd) continue;
|
|
if( wnd->dwStyle & WS_VISIBLE )
|
|
{
|
|
POINT ptOffset;
|
|
|
|
r.left = wnd->rectWindow.left + ptClient.x;
|
|
r.right = wnd->rectWindow.right + ptClient.x;
|
|
r.top = wnd->rectWindow.top + ptClient.y;
|
|
r.bottom = wnd->rectWindow.bottom + ptClient.y;
|
|
|
|
ptOffset.x = r.left - prevOrigin.x;
|
|
ptOffset.y = r.top - prevOrigin.y;
|
|
OffsetRect( &r, -ptTotal.x, -ptTotal.y );
|
|
|
|
if( RectInRegion( hRgn, &r ) )
|
|
{
|
|
OffsetRgn( hRgn, -ptOffset.x, -ptOffset.y );
|
|
RDW_UpdateRgns( wnd, hRgn, flags, FALSE );
|
|
prevOrigin.x = r.left + ptTotal.x;
|
|
prevOrigin.y = r.top + ptTotal.y;
|
|
ptTotal.x += ptOffset.x;
|
|
ptTotal.y += ptOffset.y;
|
|
}
|
|
}
|
|
WIN_ReleaseWndPtr( wnd );
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, list );
|
|
OffsetRgn( hRgn, ptTotal.x, ptTotal.y );
|
|
bChildren = 0;
|
|
}
|
|
}
|
|
|
|
/* handle hRgn == 1 (alias for entire window) and/or internal paint recursion */
|
|
|
|
if( bChildren )
|
|
{
|
|
HWND *list;
|
|
if ((list = WIN_ListChildren( wndPtr->hwndSelf )))
|
|
{
|
|
INT i;
|
|
for (i = 0; list[i]; i++)
|
|
{
|
|
WND *wnd = WIN_FindWndPtr( list[i] );
|
|
if (!wnd) continue;
|
|
if( wnd->dwStyle & WS_VISIBLE )
|
|
RDW_UpdateRgns( wnd, hRgn, flags, FALSE );
|
|
WIN_ReleaseWndPtr( wnd );
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, list );
|
|
}
|
|
}
|
|
|
|
end:
|
|
|
|
/* Set/clear internal paint flag */
|
|
|
|
if (flags & RDW_INTERNALPAINT)
|
|
{
|
|
if ( !wndPtr->hrgnUpdate && !(wndPtr->flags & WIN_INTERNAL_PAINT))
|
|
add_paint_count( wndPtr->hwndSelf, 1 );
|
|
wndPtr->flags |= WIN_INTERNAL_PAINT;
|
|
}
|
|
else if (flags & RDW_NOINTERNALPAINT)
|
|
{
|
|
if ( !wndPtr->hrgnUpdate && (wndPtr->flags & WIN_INTERNAL_PAINT))
|
|
add_paint_count( wndPtr->hwndSelf, -1 );
|
|
wndPtr->flags &= ~WIN_INTERNAL_PAINT;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RDW_Paint [RedrawWindow() helper]
|
|
*
|
|
* Walks the window tree and paints/erases windows that have
|
|
* nonzero update regions according to redraw flags. hrgn is a scratch
|
|
* region passed down during recursion. Must not be 1.
|
|
*
|
|
*/
|
|
static HRGN RDW_Paint( WND* wndPtr, HRGN hrgn, UINT flags, UINT ex )
|
|
{
|
|
/* NOTE: wndPtr is locked by caller.
|
|
*
|
|
* FIXME: Windows uses WM_SYNCPAINT to cut down the number of intertask
|
|
* SendMessage() calls. This is a comment inside DefWindowProc() source
|
|
* from 16-bit SDK:
|
|
*
|
|
* This message avoids lots of inter-app message traffic
|
|
* by switching to the other task and continuing the
|
|
* recursion there.
|
|
*
|
|
* wParam = flags
|
|
* LOWORD(lParam) = hrgnClip
|
|
* HIWORD(lParam) = hwndSkip (not used; always NULL)
|
|
*
|
|
*/
|
|
HDC hDC;
|
|
HWND hWnd = wndPtr->hwndSelf;
|
|
BOOL bIcon = ((wndPtr->dwStyle & WS_MINIMIZE) && GetClassLongA(hWnd, GCL_HICON));
|
|
|
|
/* Erase/update the window itself ... */
|
|
|
|
TRACE("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", hWnd, wndPtr->hrgnUpdate, hrgn, flags );
|
|
|
|
/*
|
|
* Check if this window should delay it's processing of WM_NCPAINT.
|
|
* See WIN_HaveToDelayNCPAINT for a description of the mechanism
|
|
*/
|
|
if ((ex & RDW_EX_DELAY_NCPAINT) || WIN_HaveToDelayNCPAINT(wndPtr->hwndSelf, 0) )
|
|
ex |= RDW_EX_DELAY_NCPAINT;
|
|
|
|
if (flags & RDW_UPDATENOW)
|
|
{
|
|
if (wndPtr->hrgnUpdate) /* wm_painticon wparam is 1 */
|
|
SendMessageW( hWnd, (bIcon) ? WM_PAINTICON : WM_PAINT, bIcon, 0 );
|
|
}
|
|
else if (flags & RDW_ERASENOW)
|
|
{
|
|
UINT dcx = DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN | DCX_WINDOWPAINT | DCX_CACHE;
|
|
HRGN hrgnRet;
|
|
|
|
hrgnRet = WIN_UpdateNCRgn(wndPtr,
|
|
hrgn,
|
|
UNC_REGION | UNC_CHECK |
|
|
((ex & RDW_EX_DELAY_NCPAINT) ? UNC_DELAY_NCPAINT : 0) );
|
|
|
|
if( hrgnRet )
|
|
{
|
|
if( hrgnRet > 1 ) hrgn = hrgnRet; else hrgnRet = 0; /* entire client */
|
|
if( wndPtr->flags & WIN_NEEDS_ERASEBKGND )
|
|
{
|
|
if( bIcon ) dcx |= DCX_WINDOW;
|
|
else
|
|
if( hrgnRet )
|
|
OffsetRgn( hrgnRet, wndPtr->rectWindow.left - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.top - wndPtr->rectClient.top);
|
|
else
|
|
dcx &= ~DCX_INTERSECTRGN;
|
|
if (( hDC = GetDCEx( hWnd, hrgnRet, dcx )) )
|
|
{
|
|
if (SendMessageW( hWnd, (bIcon) ? WM_ICONERASEBKGND : WM_ERASEBKGND,
|
|
(WPARAM)hDC, 0 ))
|
|
wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
|
|
ReleaseDC( hWnd, hDC );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !IsWindow(hWnd) ) return hrgn;
|
|
|
|
/* ... and its child windows */
|
|
|
|
if(!(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) &&
|
|
((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) )
|
|
{
|
|
HWND *list, *phwnd;
|
|
|
|
if( (list = WIN_ListChildren( wndPtr->hwndSelf )) )
|
|
{
|
|
for (phwnd = list; *phwnd; phwnd++)
|
|
{
|
|
if (!(wndPtr = WIN_FindWndPtr( *phwnd ))) continue;
|
|
if ( (wndPtr->dwStyle & WS_VISIBLE) &&
|
|
(wndPtr->hrgnUpdate || (wndPtr->flags & WIN_INTERNAL_PAINT)) )
|
|
hrgn = RDW_Paint( wndPtr, hrgn, flags, ex );
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, list );
|
|
}
|
|
}
|
|
|
|
return hrgn;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* RedrawWindow (USER32.@)
|
|
*/
|
|
BOOL WINAPI RedrawWindow( HWND hwnd, const RECT *rectUpdate,
|
|
HRGN hrgnUpdate, UINT flags )
|
|
{
|
|
HRGN hRgn = 0;
|
|
RECT r, r2;
|
|
POINT pt;
|
|
WND* wndPtr;
|
|
|
|
if (!hwnd) hwnd = GetDesktopWindow();
|
|
|
|
/* check if the window or its parents are visible/not minimized */
|
|
|
|
if (!WIN_IsWindowDrawable( hwnd, !(flags & RDW_FRAME) )) return TRUE;
|
|
|
|
if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
|
|
if (TRACE_ON(win))
|
|
{
|
|
if( hrgnUpdate )
|
|
{
|
|
GetRgnBox( hrgnUpdate, &r );
|
|
TRACE( "%04x (%04x) NULL %04x box (%i,%i-%i,%i) flags=%04x\n",
|
|
hwnd, wndPtr->hrgnUpdate, hrgnUpdate, r.left, r.top, r.right, r.bottom, flags );
|
|
}
|
|
else
|
|
{
|
|
if( rectUpdate )
|
|
r = *rectUpdate;
|
|
else
|
|
SetRectEmpty( &r );
|
|
TRACE( "%04x (%04x) %s %d,%d-%d,%d %04x flags=%04x\n",
|
|
hwnd, wndPtr->hrgnUpdate, rectUpdate ? "rect" : "NULL", r.left,
|
|
r.top, r.right, r.bottom, hrgnUpdate, flags );
|
|
}
|
|
}
|
|
|
|
|
|
/* process pending events and messages before painting */
|
|
if (flags & RDW_UPDATENOW)
|
|
MsgWaitForMultipleObjects( 0, NULL, FALSE, 0, QS_ALLINPUT );
|
|
|
|
/* prepare an update region in window coordinates */
|
|
|
|
if( flags & RDW_FRAME )
|
|
r = wndPtr->rectWindow;
|
|
else
|
|
r = wndPtr->rectClient;
|
|
|
|
pt.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
|
|
pt.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
|
|
OffsetRect( &r, -wndPtr->rectClient.left, -wndPtr->rectClient.top );
|
|
|
|
if (flags & RDW_INVALIDATE) /* ------------------------- Invalidate */
|
|
{
|
|
/* If the window doesn't have hrgnUpdate we leave hRgn zero
|
|
* and put a new region straight into wndPtr->hrgnUpdate
|
|
* so that RDW_UpdateRgns() won't have to do any extra work.
|
|
*/
|
|
|
|
if( hrgnUpdate )
|
|
{
|
|
if( wndPtr->hrgnUpdate )
|
|
hRgn = REGION_CropRgn( 0, hrgnUpdate, NULL, &pt );
|
|
else
|
|
wndPtr->hrgnUpdate = REGION_CropRgn( 0, hrgnUpdate, &r, &pt );
|
|
}
|
|
else if( rectUpdate )
|
|
{
|
|
if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
|
|
OffsetRect( &r2, pt.x, pt.y );
|
|
if( wndPtr->hrgnUpdate == 0 )
|
|
wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r2 );
|
|
else
|
|
hRgn = CreateRectRgnIndirect( &r2 );
|
|
}
|
|
else /* entire window or client depending on RDW_FRAME */
|
|
{
|
|
if( flags & RDW_FRAME )
|
|
{
|
|
if (wndPtr->hrgnUpdate) hRgn = 1;
|
|
else wndPtr->hrgnUpdate = 1;
|
|
}
|
|
else
|
|
{
|
|
GETCLIENTRECTW( wndPtr, r2 );
|
|
if( wndPtr->hrgnUpdate == 0 )
|
|
wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r2 );
|
|
else
|
|
hRgn = CreateRectRgnIndirect( &r2 );
|
|
}
|
|
}
|
|
}
|
|
else if (flags & RDW_VALIDATE) /* ------------------------- Validate */
|
|
{
|
|
/* In this we cannot leave with zero hRgn */
|
|
if( hrgnUpdate )
|
|
{
|
|
hRgn = REGION_CropRgn( hRgn, hrgnUpdate, &r, &pt );
|
|
GetRgnBox( hRgn, &r2 );
|
|
if( IsRectEmpty( &r2 ) ) goto END;
|
|
}
|
|
else if( rectUpdate )
|
|
{
|
|
if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
|
|
OffsetRect( &r2, pt.x, pt.y );
|
|
hRgn = CreateRectRgnIndirect( &r2 );
|
|
}
|
|
else /* entire window or client depending on RDW_FRAME */
|
|
{
|
|
if( flags & RDW_FRAME )
|
|
hRgn = 1;
|
|
else
|
|
{
|
|
GETCLIENTRECTW( wndPtr, r2 );
|
|
hRgn = CreateRectRgnIndirect( &r2 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* At this point hRgn is either an update region in window coordinates or 1 or 0 */
|
|
|
|
RDW_UpdateRgns( wndPtr, hRgn, flags, TRUE );
|
|
|
|
/* Erase/update windows, from now on hRgn is a scratch region */
|
|
|
|
hRgn = RDW_Paint( wndPtr, (hRgn == 1) ? 0 : hRgn, flags, 0 );
|
|
|
|
END:
|
|
if( hRgn > 1 && (hRgn != hrgnUpdate) )
|
|
DeleteObject(hRgn );
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* UpdateWindow (USER32.@)
|
|
*/
|
|
void WINAPI UpdateWindow( HWND hwnd )
|
|
{
|
|
RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* InvalidateRgn (USER32.@)
|
|
*/
|
|
BOOL WINAPI InvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
|
|
{
|
|
return RedrawWindow(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* InvalidateRect (USER32.@)
|
|
*/
|
|
BOOL WINAPI InvalidateRect( HWND hwnd, const RECT *rect, BOOL erase )
|
|
{
|
|
return RedrawWindow( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ValidateRgn (USER32.@)
|
|
*/
|
|
void WINAPI ValidateRgn( HWND hwnd, HRGN hrgn )
|
|
{
|
|
RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOCHILDREN );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ValidateRect (USER32.@)
|
|
*/
|
|
void WINAPI ValidateRect( HWND hwnd, const RECT *rect )
|
|
{
|
|
RedrawWindow( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetUpdateRect (USER32.@)
|
|
*/
|
|
BOOL WINAPI GetUpdateRect( HWND hwnd, LPRECT rect, BOOL erase )
|
|
{
|
|
BOOL retvalue;
|
|
WND * wndPtr = WIN_FindWndPtr( hwnd );
|
|
if (!wndPtr) return FALSE;
|
|
|
|
if (rect)
|
|
{
|
|
if (wndPtr->hrgnUpdate > 1)
|
|
{
|
|
HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
|
|
if (GetUpdateRgn( hwnd, hrgn, erase ) == ERROR)
|
|
{
|
|
retvalue = FALSE;
|
|
goto END;
|
|
}
|
|
GetRgnBox( hrgn, rect );
|
|
DeleteObject( hrgn );
|
|
if (GetClassLongA(wndPtr->hwndSelf, GCL_STYLE) & CS_OWNDC)
|
|
{
|
|
if (GetMapMode(wndPtr->dce->hDC) != MM_TEXT)
|
|
{
|
|
DPtoLP (wndPtr->dce->hDC, (LPPOINT)rect, 2);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if( wndPtr->hrgnUpdate == 1 )
|
|
{
|
|
GetClientRect( hwnd, rect );
|
|
if (erase) RedrawWindow( hwnd, NULL, 0, RDW_FRAME | RDW_ERASENOW | RDW_NOCHILDREN );
|
|
}
|
|
else
|
|
SetRectEmpty( rect );
|
|
}
|
|
retvalue = (wndPtr->hrgnUpdate >= 1);
|
|
END:
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return retvalue;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetUpdateRgn (USER32.@)
|
|
*/
|
|
INT WINAPI GetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
|
|
{
|
|
INT retval;
|
|
WND * wndPtr = WIN_FindWndPtr( hwnd );
|
|
if (!wndPtr) return ERROR;
|
|
|
|
if (wndPtr->hrgnUpdate == 0)
|
|
{
|
|
SetRectRgn( hrgn, 0, 0, 0, 0 );
|
|
retval = NULLREGION;
|
|
goto END;
|
|
}
|
|
else
|
|
if (wndPtr->hrgnUpdate == 1)
|
|
{
|
|
SetRectRgn( hrgn, 0, 0, wndPtr->rectClient.right - wndPtr->rectClient.left,
|
|
wndPtr->rectClient.bottom - wndPtr->rectClient.top );
|
|
retval = SIMPLEREGION;
|
|
}
|
|
else
|
|
{
|
|
retval = CombineRgn( hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY );
|
|
OffsetRgn( hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.top - wndPtr->rectClient.top );
|
|
}
|
|
if (erase) RedrawWindow( hwnd, NULL, 0, RDW_ERASENOW | RDW_NOCHILDREN );
|
|
END:
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return retval;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ExcludeUpdateRgn (USER32.@)
|
|
*/
|
|
INT WINAPI ExcludeUpdateRgn( HDC hdc, HWND hwnd )
|
|
{
|
|
RECT rect;
|
|
WND * wndPtr;
|
|
|
|
if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return ERROR;
|
|
|
|
if (wndPtr->hrgnUpdate)
|
|
{
|
|
INT ret;
|
|
HRGN hrgn = CreateRectRgn(wndPtr->rectWindow.left - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.top - wndPtr->rectClient.top,
|
|
wndPtr->rectWindow.right - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.bottom - wndPtr->rectClient.top);
|
|
if( wndPtr->hrgnUpdate > 1 )
|
|
{
|
|
CombineRgn(hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY);
|
|
OffsetRgn(hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.top - wndPtr->rectClient.top );
|
|
}
|
|
|
|
/* do ugly coordinate translations in dce.c */
|
|
|
|
ret = DCE_ExcludeRgn( hdc, hwnd, hrgn );
|
|
DeleteObject( hrgn );
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return ret;
|
|
}
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return GetClipBox( hdc, &rect );
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
* FillRect (USER.81)
|
|
* NOTE
|
|
* The Win16 variant doesn't support special color brushes like
|
|
* the Win32 one, despite the fact that Win16, as well as Win32,
|
|
* supports special background brushes for a window class.
|
|
*/
|
|
INT16 WINAPI FillRect16( HDC16 hdc, const RECT16 *rect, HBRUSH16 hbrush )
|
|
{
|
|
HBRUSH prevBrush;
|
|
|
|
/* coordinates are logical so we cannot fast-check 'rect',
|
|
* it will be done later in the PatBlt().
|
|
*/
|
|
|
|
if (!(prevBrush = SelectObject( hdc, hbrush ))) return 0;
|
|
PatBlt( hdc, rect->left, rect->top,
|
|
rect->right - rect->left, rect->bottom - rect->top, PATCOPY );
|
|
SelectObject( hdc, prevBrush );
|
|
return 1;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FillRect (USER32.@)
|
|
*/
|
|
INT WINAPI FillRect( HDC hdc, const RECT *rect, HBRUSH hbrush )
|
|
{
|
|
HBRUSH prevBrush;
|
|
|
|
if (hbrush <= (HBRUSH) (COLOR_MAX + 1)) {
|
|
hbrush = GetSysColorBrush( (INT) hbrush - 1 );
|
|
}
|
|
|
|
if (!(prevBrush = SelectObject( hdc, hbrush ))) return 0;
|
|
PatBlt( hdc, rect->left, rect->top,
|
|
rect->right - rect->left, rect->bottom - rect->top, PATCOPY );
|
|
SelectObject( hdc, prevBrush );
|
|
return 1;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* InvertRect (USER.82)
|
|
*/
|
|
void WINAPI InvertRect16( HDC16 hdc, const RECT16 *rect )
|
|
{
|
|
PatBlt( hdc, rect->left, rect->top,
|
|
rect->right - rect->left, rect->bottom - rect->top, DSTINVERT );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* InvertRect (USER32.@)
|
|
*/
|
|
BOOL WINAPI InvertRect( HDC hdc, const RECT *rect )
|
|
{
|
|
return PatBlt( hdc, rect->left, rect->top,
|
|
rect->right - rect->left, rect->bottom - rect->top,
|
|
DSTINVERT );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FrameRect (USER32.@)
|
|
*/
|
|
INT WINAPI FrameRect( HDC hdc, const RECT *rect, HBRUSH hbrush )
|
|
{
|
|
HBRUSH prevBrush;
|
|
RECT r = *rect;
|
|
|
|
if ( (r.right <= r.left) || (r.bottom <= r.top) ) return 0;
|
|
if (!(prevBrush = SelectObject( hdc, hbrush ))) return 0;
|
|
|
|
PatBlt( hdc, r.left, r.top, 1,
|
|
r.bottom - r.top, PATCOPY );
|
|
PatBlt( hdc, r.right - 1, r.top, 1,
|
|
r.bottom - r.top, PATCOPY );
|
|
PatBlt( hdc, r.left, r.top,
|
|
r.right - r.left, 1, PATCOPY );
|
|
PatBlt( hdc, r.left, r.bottom - 1,
|
|
r.right - r.left, 1, PATCOPY );
|
|
|
|
SelectObject( hdc, prevBrush );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FrameRect (USER.83)
|
|
*/
|
|
INT16 WINAPI FrameRect16( HDC16 hdc, const RECT16 *rect16, HBRUSH16 hbrush )
|
|
{
|
|
RECT rect;
|
|
CONV_RECT16TO32( rect16, &rect );
|
|
return FrameRect( hdc, &rect, hbrush );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* DrawFocusRect (USER.466)
|
|
*/
|
|
void WINAPI DrawFocusRect16( HDC16 hdc, const RECT16* rc )
|
|
{
|
|
RECT rect32;
|
|
CONV_RECT16TO32( rc, &rect32 );
|
|
DrawFocusRect( hdc, &rect32 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* DrawFocusRect (USER32.@)
|
|
*
|
|
* FIXME: PatBlt(PATINVERT) with background brush.
|
|
*/
|
|
BOOL WINAPI DrawFocusRect( HDC hdc, const RECT* rc )
|
|
{
|
|
HBRUSH hOldBrush;
|
|
HPEN hOldPen, hNewPen;
|
|
INT oldDrawMode, oldBkMode;
|
|
|
|
hOldBrush = SelectObject(hdc, GetStockObject(NULL_BRUSH));
|
|
hNewPen = CreatePen(PS_ALTERNATE, 1, GetSysColor(COLOR_WINDOWTEXT));
|
|
hOldPen = SelectObject(hdc, hNewPen);
|
|
oldDrawMode = SetROP2(hdc, R2_XORPEN);
|
|
oldBkMode = SetBkMode(hdc, TRANSPARENT);
|
|
|
|
Rectangle(hdc, rc->left, rc->top, rc->right, rc->bottom);
|
|
|
|
SetBkMode(hdc, oldBkMode);
|
|
SetROP2(hdc, oldDrawMode);
|
|
SelectObject(hdc, hOldPen);
|
|
DeleteObject(hNewPen);
|
|
SelectObject(hdc, hOldBrush);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DrawAnimatedRects (USER32.@)
|
|
*/
|
|
BOOL WINAPI DrawAnimatedRects( HWND hwnd, INT idAni,
|
|
const RECT* lprcFrom,
|
|
const RECT* lprcTo )
|
|
{
|
|
FIXME_(win)("(0x%x,%d,%p,%p): stub\n",hwnd,idAni,lprcFrom,lprcTo);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* PAINTING_DrawStateJam
|
|
*
|
|
* Jams in the requested type in the dc
|
|
*/
|
|
static BOOL PAINTING_DrawStateJam(HDC hdc, UINT opcode,
|
|
DRAWSTATEPROC func, LPARAM lp, WPARAM wp,
|
|
LPRECT rc, UINT dtflags, BOOL unicode )
|
|
{
|
|
HDC memdc;
|
|
HBITMAP hbmsave;
|
|
BOOL retval;
|
|
INT cx = rc->right - rc->left;
|
|
INT cy = rc->bottom - rc->top;
|
|
|
|
switch(opcode)
|
|
{
|
|
case DST_TEXT:
|
|
case DST_PREFIXTEXT:
|
|
if(unicode)
|
|
return DrawTextW(hdc, (LPWSTR)lp, (INT)wp, rc, dtflags);
|
|
else
|
|
return DrawTextA(hdc, (LPSTR)lp, (INT)wp, rc, dtflags);
|
|
|
|
case DST_ICON:
|
|
return DrawIcon(hdc, rc->left, rc->top, (HICON)lp);
|
|
|
|
case DST_BITMAP:
|
|
memdc = CreateCompatibleDC(hdc);
|
|
if(!memdc) return FALSE;
|
|
hbmsave = (HBITMAP)SelectObject(memdc, (HBITMAP)lp);
|
|
if(!hbmsave)
|
|
{
|
|
DeleteDC(memdc);
|
|
return FALSE;
|
|
}
|
|
retval = BitBlt(hdc, rc->left, rc->top, cx, cy, memdc, 0, 0, SRCCOPY);
|
|
SelectObject(memdc, hbmsave);
|
|
DeleteDC(memdc);
|
|
return retval;
|
|
|
|
case DST_COMPLEX:
|
|
if(func) {
|
|
BOOL bRet;
|
|
/* DRAWSTATEPROC assumes that it draws at the center of coordinates */
|
|
|
|
OffsetViewportOrgEx(hdc, rc->left, rc->top, NULL);
|
|
bRet = func(hdc, lp, wp, cx, cy);
|
|
/* Restore origin */
|
|
OffsetViewportOrgEx(hdc, -rc->left, -rc->top, NULL);
|
|
return bRet;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* PAINTING_DrawState()
|
|
*/
|
|
static BOOL PAINTING_DrawState(HDC hdc, HBRUSH hbr, DRAWSTATEPROC func, LPARAM lp, WPARAM wp,
|
|
INT x, INT y, INT cx, INT cy, UINT flags, BOOL unicode )
|
|
{
|
|
HBITMAP hbm, hbmsave;
|
|
HFONT hfsave;
|
|
HBRUSH hbsave, hbrtmp = 0;
|
|
HDC memdc;
|
|
RECT rc;
|
|
UINT dtflags = DT_NOCLIP;
|
|
COLORREF fg, bg;
|
|
UINT opcode = flags & 0xf;
|
|
INT len = wp;
|
|
BOOL retval, tmp;
|
|
|
|
if((opcode == DST_TEXT || opcode == DST_PREFIXTEXT) && !len) /* The string is '\0' terminated */
|
|
{
|
|
if(unicode)
|
|
len = strlenW((LPWSTR)lp);
|
|
else
|
|
len = strlen((LPSTR)lp);
|
|
}
|
|
|
|
/* Find out what size the image has if not given by caller */
|
|
if(!cx || !cy)
|
|
{
|
|
SIZE s;
|
|
CURSORICONINFO *ici;
|
|
BITMAP bm;
|
|
|
|
switch(opcode)
|
|
{
|
|
case DST_TEXT:
|
|
case DST_PREFIXTEXT:
|
|
if(unicode)
|
|
retval = GetTextExtentPoint32W(hdc, (LPWSTR)lp, len, &s);
|
|
else
|
|
retval = GetTextExtentPoint32A(hdc, (LPSTR)lp, len, &s);
|
|
if(!retval) return FALSE;
|
|
break;
|
|
|
|
case DST_ICON:
|
|
ici = (CURSORICONINFO *)GlobalLock16((HGLOBAL16)lp);
|
|
if(!ici) return FALSE;
|
|
s.cx = ici->nWidth;
|
|
s.cy = ici->nHeight;
|
|
GlobalUnlock16((HGLOBAL16)lp);
|
|
break;
|
|
|
|
case DST_BITMAP:
|
|
if(!GetObjectA((HBITMAP)lp, sizeof(bm), &bm))
|
|
return FALSE;
|
|
s.cx = bm.bmWidth;
|
|
s.cy = bm.bmHeight;
|
|
break;
|
|
|
|
case DST_COMPLEX: /* cx and cy must be set in this mode */
|
|
return FALSE;
|
|
}
|
|
|
|
if(!cx) cx = s.cx;
|
|
if(!cy) cy = s.cy;
|
|
}
|
|
|
|
rc.left = x;
|
|
rc.top = y;
|
|
rc.right = x + cx;
|
|
rc.bottom = y + cy;
|
|
|
|
if(flags & DSS_RIGHT) /* This one is not documented in the win32.hlp file */
|
|
dtflags |= DT_RIGHT;
|
|
if(opcode == DST_TEXT)
|
|
dtflags |= DT_NOPREFIX;
|
|
|
|
/* For DSS_NORMAL we just jam in the image and return */
|
|
if((flags & 0x7ff0) == DSS_NORMAL)
|
|
{
|
|
return PAINTING_DrawStateJam(hdc, opcode, func, lp, len, &rc, dtflags, unicode);
|
|
}
|
|
|
|
/* For all other states we need to convert the image to B/W in a local bitmap */
|
|
/* before it is displayed */
|
|
fg = SetTextColor(hdc, RGB(0, 0, 0));
|
|
bg = SetBkColor(hdc, RGB(255, 255, 255));
|
|
hbm = (HBITMAP)NULL; hbmsave = (HBITMAP)NULL;
|
|
memdc = (HDC)NULL; hbsave = (HBRUSH)NULL;
|
|
retval = FALSE; /* assume failure */
|
|
|
|
/* From here on we must use "goto cleanup" when something goes wrong */
|
|
hbm = CreateBitmap(cx, cy, 1, 1, NULL);
|
|
if(!hbm) goto cleanup;
|
|
memdc = CreateCompatibleDC(hdc);
|
|
if(!memdc) goto cleanup;
|
|
hbmsave = (HBITMAP)SelectObject(memdc, hbm);
|
|
if(!hbmsave) goto cleanup;
|
|
rc.left = rc.top = 0;
|
|
rc.right = cx;
|
|
rc.bottom = cy;
|
|
if(!FillRect(memdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH))) goto cleanup;
|
|
SetBkColor(memdc, RGB(255, 255, 255));
|
|
SetTextColor(memdc, RGB(0, 0, 0));
|
|
hfsave = (HFONT)SelectObject(memdc, GetCurrentObject(hdc, OBJ_FONT));
|
|
|
|
/* DST_COMPLEX may draw text as well,
|
|
* so we must be sure that correct font is selected
|
|
*/
|
|
if(!hfsave && (opcode <= DST_PREFIXTEXT)) goto cleanup;
|
|
tmp = PAINTING_DrawStateJam(memdc, opcode, func, lp, len, &rc, dtflags, unicode);
|
|
if(hfsave) SelectObject(memdc, hfsave);
|
|
if(!tmp) goto cleanup;
|
|
|
|
/* This state cause the image to be dithered */
|
|
if(flags & DSS_UNION)
|
|
{
|
|
hbsave = (HBRUSH)SelectObject(memdc, CACHE_GetPattern55AABrush());
|
|
if(!hbsave) goto cleanup;
|
|
tmp = PatBlt(memdc, 0, 0, cx, cy, 0x00FA0089);
|
|
SelectObject(memdc, hbsave);
|
|
if(!tmp) goto cleanup;
|
|
}
|
|
|
|
if (flags & DSS_DISABLED)
|
|
hbrtmp = CreateSolidBrush(GetSysColor(COLOR_3DHILIGHT));
|
|
else if (flags & DSS_DEFAULT)
|
|
hbrtmp = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
|
|
|
|
/* Draw light or dark shadow */
|
|
if (flags & (DSS_DISABLED|DSS_DEFAULT))
|
|
{
|
|
if(!hbrtmp) goto cleanup;
|
|
hbsave = (HBRUSH)SelectObject(hdc, hbrtmp);
|
|
if(!hbsave) goto cleanup;
|
|
if(!BitBlt(hdc, x+1, y+1, cx, cy, memdc, 0, 0, 0x00B8074A)) goto cleanup;
|
|
SelectObject(hdc, hbsave);
|
|
DeleteObject(hbrtmp);
|
|
hbrtmp = 0;
|
|
}
|
|
|
|
if (flags & DSS_DISABLED)
|
|
{
|
|
hbr = hbrtmp = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
|
|
if(!hbrtmp) goto cleanup;
|
|
}
|
|
else if (!hbr)
|
|
{
|
|
hbr = (HBRUSH)GetStockObject(BLACK_BRUSH);
|
|
}
|
|
|
|
hbsave = (HBRUSH)SelectObject(hdc, hbr);
|
|
|
|
if(!BitBlt(hdc, x, y, cx, cy, memdc, 0, 0, 0x00B8074A)) goto cleanup;
|
|
|
|
retval = TRUE; /* We succeeded */
|
|
|
|
cleanup:
|
|
SetTextColor(hdc, fg);
|
|
SetBkColor(hdc, bg);
|
|
|
|
if(hbsave) SelectObject(hdc, hbsave);
|
|
if(hbmsave) SelectObject(memdc, hbmsave);
|
|
if(hbrtmp) DeleteObject(hbrtmp);
|
|
if(hbm) DeleteObject(hbm);
|
|
if(memdc) DeleteDC(memdc);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* DrawStateA (USER32.@)
|
|
*/
|
|
BOOL WINAPI DrawStateA(HDC hdc, HBRUSH hbr,
|
|
DRAWSTATEPROC func, LPARAM ldata, WPARAM wdata,
|
|
INT x, INT y, INT cx, INT cy, UINT flags)
|
|
{
|
|
return PAINTING_DrawState(hdc, hbr, func, ldata, wdata, x, y, cx, cy, flags, FALSE);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* DrawStateW (USER32.@)
|
|
*/
|
|
BOOL WINAPI DrawStateW(HDC hdc, HBRUSH hbr,
|
|
DRAWSTATEPROC func, LPARAM ldata, WPARAM wdata,
|
|
INT x, INT y, INT cx, INT cy, UINT flags)
|
|
{
|
|
return PAINTING_DrawState(hdc, hbr, func, ldata, wdata, x, y, cx, cy, flags, TRUE);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DrawState (USER.449)
|
|
*/
|
|
BOOL16 WINAPI DrawState16( HDC16 hdc, HBRUSH16 hbr, DRAWSTATEPROC16 func, LPARAM ldata,
|
|
WPARAM16 wdata, INT16 x, INT16 y, INT16 cx, INT16 cy, UINT16 flags )
|
|
{
|
|
struct draw_state_info info;
|
|
UINT opcode = flags & 0xf;
|
|
|
|
if (opcode == DST_TEXT || opcode == DST_PREFIXTEXT)
|
|
{
|
|
/* make sure DrawStateA doesn't try to use ldata as a pointer */
|
|
if (!wdata) wdata = strlen( MapSL(ldata) );
|
|
if (!cx || !cy)
|
|
{
|
|
SIZE s;
|
|
if (!GetTextExtentPoint32A( hdc, MapSL(ldata), wdata, &s )) return FALSE;
|
|
if (!cx) cx = s.cx;
|
|
if (!cy) cy = s.cy;
|
|
}
|
|
}
|
|
info.proc = func;
|
|
info.param = ldata;
|
|
return DrawStateA( hdc, hbr, draw_state_callback, (LPARAM)&info, wdata, x, y, cx, cy, flags );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SelectPalette (USER.282)
|
|
*/
|
|
HPALETTE16 WINAPI SelectPalette16( HDC16 hDC, HPALETTE16 hPal,
|
|
BOOL16 bForceBackground )
|
|
{
|
|
WORD wBkgPalette = 1;
|
|
|
|
if (!bForceBackground && (hPal != GetStockObject(DEFAULT_PALETTE)))
|
|
{
|
|
HWND hwnd = WindowFromDC( hDC );
|
|
if (hwnd)
|
|
{
|
|
HWND hForeground = GetForegroundWindow();
|
|
/* set primary palette if it's related to current active */
|
|
if (hForeground == hwnd || IsChild(hForeground,hwnd)) wBkgPalette = 0;
|
|
}
|
|
}
|
|
return GDISelectPalette16( hDC, hPal, wBkgPalette);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* RealizePalette (USER.283)
|
|
*/
|
|
UINT16 WINAPI RealizePalette16( HDC16 hDC )
|
|
{
|
|
UINT16 realized = GDIRealizePalette16( hDC );
|
|
|
|
/* do not send anything if no colors were changed */
|
|
if (realized && IsDCCurrentPalette16( hDC ))
|
|
{
|
|
/* send palette change notification */
|
|
HWND hWnd = WindowFromDC( hDC );
|
|
if (hWnd) SendMessageA( HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0L);
|
|
}
|
|
return realized;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* UserRealizePalette (USER32.@)
|
|
*/
|
|
UINT WINAPI UserRealizePalette( HDC hDC )
|
|
{
|
|
return RealizePalette16( hDC );
|
|
}
|