| /* | |
| * Window painting functions | |
| * | |
| * Copyright 1993, 1994, 1995 Alexandre Julliard | |
| * 1999 Alex Korobka | |
| */ | |
| #include <string.h> | |
| #include "windef.h" | |
| #include "wingdi.h" | |
| #include "wine/winuser16.h" | |
| #include "wine/unicode.h" | |
| #include "server.h" | |
| #include "region.h" | |
| #include "user.h" | |
| #include "win.h" | |
| #include "queue.h" | |
| #include "dce.h" | |
| #include "heap.h" | |
| #include "debugtools.h" | |
| DEFAULT_DEBUG_CHANNEL(win); | |
| 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 | |
| /* Last CTLCOLOR id */ | |
| #define CTLCOLOR_MAX CTLCOLOR_STATIC | |
| /*********************************************************************** | |
| * 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_queue_paint_count ) | |
| { | |
| req->id = (void *)GetWindowThreadProcessId( hwnd, NULL ); | |
| req->incr = incr; | |
| SERVER_CALL(); | |
| } | |
| 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( | |
| WND* wndPtr, | |
| UINT uncFlags) | |
| { | |
| WND* parentWnd = NULL; | |
| /* | |
| * 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. | |
| */ | |
| parentWnd = WIN_LockWndPtr(wndPtr->parent); | |
| while (parentWnd!=NULL) | |
| { | |
| if ( ((parentWnd->dwStyle & WS_CLIPCHILDREN) == 0) && | |
| (parentWnd->hrgnUpdate != 0) ) | |
| { | |
| WIN_ReleaseWndPtr(parentWnd); | |
| return TRUE; | |
| } | |
| WIN_UpdateWndPtr(&parentWnd, parentWnd->parent); | |
| } | |
| 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 == WIN_GetDesktop()) | |
| { | |
| wnd->flags &= ~WIN_NEEDS_NCPAINT; | |
| if( wnd->hrgnUpdate > 1 ) | |
| hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL ); | |
| else | |
| { | |
| hrgnRet = wnd->hrgnUpdate; | |
| } | |
| WIN_ReleaseDesktop(); | |
| return hrgnRet; | |
| } | |
| WIN_ReleaseDesktop(); | |
| 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, 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; | |
| } | |
| /*********************************************************************** | |
| * BeginPaint (USER.39) | |
| */ | |
| HDC16 WINAPI BeginPaint16( HWND16 hwnd, LPPAINTSTRUCT16 lps ) | |
| { | |
| PAINTSTRUCT ps; | |
| BeginPaint( hwnd, &ps ); | |
| lps->hdc = ps.hdc; | |
| lps->fErase = ps.fErase; | |
| lps->rcPaint.top = ps.rcPaint.top; | |
| lps->rcPaint.left = ps.rcPaint.left; | |
| lps->rcPaint.right = ps.rcPaint.right; | |
| lps->rcPaint.bottom = ps.rcPaint.bottom; | |
| lps->fRestore = ps.fRestore; | |
| lps->fIncUpdate = ps.fIncUpdate; | |
| return lps->hdc; | |
| } | |
| /*********************************************************************** | |
| * BeginPaint (USER32.@) | |
| */ | |
| HDC WINAPI BeginPaint( HWND hwnd, PAINTSTRUCT *lps ) | |
| { | |
| BOOL bIcon; | |
| HRGN hrgnUpdate; | |
| RECT clipRect, clientRect; | |
| WND *wndPtr = WIN_FindWndPtr( hwnd ); | |
| if (!wndPtr) return 0; | |
| bIcon = (wndPtr->dwStyle & WS_MINIMIZE && GetClassWord(wndPtr->hwndSelf, GCW_HICON)); | |
| wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT; | |
| /* send WM_NCPAINT and make sure hrgnUpdate is a valid rgn handle */ | |
| WIN_UpdateNCRgn( wndPtr, 0, UNC_UPDATE | UNC_IN_BEGINPAINT); | |
| /* | |
| * Make sure the window is still a window. All window locks are suspended | |
| * when the WM_NCPAINT is sent. | |
| */ | |
| if (!IsWindow(wndPtr->hwndSelf)) | |
| { | |
| WIN_ReleaseWndPtr(wndPtr); | |
| return 0; | |
| } | |
| if( ((hrgnUpdate = wndPtr->hrgnUpdate) != 0) || (wndPtr->flags & WIN_INTERNAL_PAINT)) | |
| add_paint_count( hwnd, -1 ); | |
| wndPtr->hrgnUpdate = 0; | |
| wndPtr->flags &= ~WIN_INTERNAL_PAINT; | |
| HideCaret( hwnd ); | |
| TRACE("hrgnUpdate = %04x, \n", hrgnUpdate); | |
| if (GetClassLongA(wndPtr->hwndSelf, GCL_STYLE) & CS_PARENTDC) | |
| { | |
| /* Don't clip the output to the update region for CS_PARENTDC window */ | |
| if( hrgnUpdate ) | |
| DeleteObject(hrgnUpdate); | |
| lps->hdc = GetDCEx( hwnd, 0, DCX_WINDOWPAINT | DCX_USESTYLE | | |
| (bIcon ? DCX_WINDOW : 0) ); | |
| } | |
| else | |
| { | |
| if( hrgnUpdate ) /* convert to client coordinates */ | |
| OffsetRgn( hrgnUpdate, wndPtr->rectWindow.left - wndPtr->rectClient.left, | |
| wndPtr->rectWindow.top - wndPtr->rectClient.top ); | |
| lps->hdc = GetDCEx(hwnd, hrgnUpdate, DCX_INTERSECTRGN | | |
| DCX_WINDOWPAINT | DCX_USESTYLE | (bIcon ? DCX_WINDOW : 0) ); | |
| /* ReleaseDC() in EndPaint() will delete the region */ | |
| } | |
| TRACE("hdc = %04x\n", lps->hdc); | |
| if (!lps->hdc) | |
| { | |
| WARN("GetDCEx() failed in BeginPaint(), hwnd=%04x\n", hwnd); | |
| WIN_ReleaseWndPtr(wndPtr); | |
| return 0; | |
| } | |
| /* It is possible that the clip box is bigger than the window itself, | |
| if CS_PARENTDC flag is set. Windows never return a paint rect bigger | |
| than the window itself, so we need to intersect the cliprect with | |
| the window */ | |
| GetClientRect( hwnd, &clientRect ); | |
| GetClipBox( lps->hdc, &clipRect ); | |
| LPtoDP(lps->hdc, (LPPOINT)&clipRect, 2); /* GetClipBox returns LP */ | |
| IntersectRect(&lps->rcPaint, &clientRect, &clipRect); | |
| DPtoLP(lps->hdc, (LPPOINT)&lps->rcPaint, 2); /* we must return LP */ | |
| TRACE("box = (%i,%i - %i,%i)\n", lps->rcPaint.left, lps->rcPaint.top, | |
| lps->rcPaint.right, lps->rcPaint.bottom ); | |
| if (wndPtr->flags & WIN_NEEDS_ERASEBKGND) | |
| { | |
| wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND; | |
| lps->fErase = !SendMessageA(hwnd, (bIcon) ? WM_ICONERASEBKGND : WM_ERASEBKGND, | |
| (WPARAM16)lps->hdc, 0 ); | |
| } | |
| else lps->fErase = TRUE; | |
| WIN_ReleaseWndPtr(wndPtr); | |
| return lps->hdc; | |
| } | |
| /*********************************************************************** | |
| * EndPaint (USER.40) | |
| */ | |
| BOOL16 WINAPI EndPaint16( HWND16 hwnd, const PAINTSTRUCT16* lps ) | |
| { | |
| ReleaseDC16( hwnd, lps->hdc ); | |
| ShowCaret( hwnd ); | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * EndPaint (USER32.@) | |
| */ | |
| BOOL WINAPI EndPaint( HWND hwnd, const PAINTSTRUCT *lps ) | |
| { | |
| ReleaseDC( hwnd, lps->hdc ); | |
| ShowCaret( hwnd ); | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * FillWindow (USER.324) | |
| */ | |
| void WINAPI FillWindow16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc, HBRUSH16 hbrush ) | |
| { | |
| RECT rect; | |
| RECT16 rc16; | |
| GetClientRect( hwnd, &rect ); | |
| DPtoLP( hdc, (LPPOINT)&rect, 2 ); | |
| CONV_RECT32TO16( &rect, &rc16 ); | |
| PaintRect16( hwndParent, hwnd, hdc, hbrush, &rc16 ); | |
| } | |
| /*********************************************************************** | |
| * PAINT_GetControlBrush | |
| */ | |
| static HBRUSH16 PAINT_GetControlBrush( HWND hParent, HWND hWnd, HDC16 hDC, UINT16 ctlType ) | |
| { | |
| HBRUSH16 bkgBrush = (HBRUSH16)SendMessageA( hParent, WM_CTLCOLORMSGBOX + ctlType, | |
| (WPARAM)hDC, (LPARAM)hWnd ); | |
| if( !IsGDIObject16(bkgBrush) ) | |
| bkgBrush = DEFWND_ControlColor( hDC, ctlType ); | |
| return bkgBrush; | |
| } | |
| /*********************************************************************** | |
| * PaintRect (USER.325) | |
| */ | |
| void WINAPI PaintRect16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc, | |
| HBRUSH16 hbrush, const RECT16 *rect) | |
| { | |
| if( hbrush <= CTLCOLOR_MAX ) | |
| { | |
| if( hwndParent ) | |
| hbrush = PAINT_GetControlBrush( hwndParent, hwnd, hdc, (UINT16)hbrush ); | |
| else | |
| return; | |
| } | |
| if( hbrush ) | |
| FillRect16( hdc, rect, hbrush ); | |
| } | |
| /*********************************************************************** | |
| * GetControlBrush (USER.326) | |
| */ | |
| HBRUSH16 WINAPI GetControlBrush16( HWND16 hwnd, HDC16 hdc, UINT16 ctlType ) | |
| { | |
| WND* wndPtr = WIN_FindWndPtr( hwnd ); | |
| HBRUSH16 retvalue; | |
| if((ctlType <= CTLCOLOR_MAX) && wndPtr ) | |
| { | |
| WND* parent; | |
| if( wndPtr->dwStyle & WS_POPUP ) parent = WIN_LockWndPtr(wndPtr->owner); | |
| else parent = WIN_LockWndPtr(wndPtr->parent); | |
| if( !parent ) parent = wndPtr; | |
| retvalue = (HBRUSH16)PAINT_GetControlBrush( parent->hwndSelf, hwnd, hdc, ctlType ); | |
| WIN_ReleaseWndPtr(parent); | |
| goto END; | |
| } | |
| retvalue = (HBRUSH16)0; | |
| END: | |
| WIN_ReleaseWndPtr(wndPtr); | |
| return retvalue; | |
| } | |
| /*********************************************************************** | |
| * 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) | |
| { | |
| WND *wndParent = WIN_LockWndPtr(wndChild->parent); | |
| WND *wndDesktop = WIN_GetDesktop(); | |
| 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; | |
| while ((wndParent) && (wndParent != wndDesktop) ) { | |
| 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_UpdateWndPtr(&wndParent, wndParent->parent); | |
| } | |
| if (hrg != wndChild->hrgnUpdate) DeleteObject( hrg ); | |
| WIN_ReleaseWndPtr(wndParent); | |
| WIN_ReleaseDesktop(); | |
| } | |
| /*********************************************************************** | |
| * 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 = ( wndPtr->child && !(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) ) | |
| { | |
| if( hRgn > 1 && bChildren ) | |
| { | |
| WND* wnd = wndPtr->child; | |
| POINT ptTotal, prevOrigin = {0,0}; | |
| POINT ptClient; | |
| ptClient.x = wndPtr->rectClient.left - wndPtr->rectWindow.left; | |
| ptClient.y = wndPtr->rectClient.top - wndPtr->rectWindow.top; | |
| for( ptTotal.x = ptTotal.y = 0; wnd; wnd = wnd->next ) | |
| { | |
| 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; | |
| } | |
| } | |
| } | |
| OffsetRgn( hRgn, ptTotal.x, ptTotal.y ); | |
| bChildren = 0; | |
| } | |
| } | |
| /* handle hRgn == 1 (alias for entire window) and/or internal paint recursion */ | |
| if( bChildren ) | |
| { | |
| WND* wnd; | |
| for( wnd = wndPtr->child; wnd; wnd = wnd->next ) | |
| if( wnd->dwStyle & WS_VISIBLE ) | |
| RDW_UpdateRgns( wnd, hRgn, flags, FALSE ); | |
| } | |
| 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) && GetClassWord(wndPtr->hwndSelf, GCW_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, 0) ) | |
| ex |= RDW_EX_DELAY_NCPAINT; | |
| if (flags & RDW_UPDATENOW) | |
| { | |
| if (wndPtr->hrgnUpdate) /* wm_painticon wparam is 1 */ | |
| SendMessage16( 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 (SendMessage16( hWnd, (bIcon) ? WM_ICONERASEBKGND | |
| : WM_ERASEBKGND, (WPARAM16)hDC, 0 )) | |
| wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND; | |
| ReleaseDC( hWnd, hDC ); | |
| } | |
| } | |
| } | |
| } | |
| if( !IsWindow(hWnd) ) return hrgn; | |
| /* ... and its child windows */ | |
| if( wndPtr->child && !(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) | |
| && ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) ) | |
| { | |
| WND** list, **ppWnd; | |
| if( (list = WIN_BuildWinArray( wndPtr, 0, NULL )) ) | |
| { | |
| wndPtr = NULL; | |
| for (ppWnd = list; *ppWnd; ppWnd++) | |
| { | |
| WIN_UpdateWndPtr(&wndPtr,*ppWnd); | |
| if (!IsWindow(wndPtr->hwndSelf)) continue; | |
| if ( (wndPtr->dwStyle & WS_VISIBLE) && | |
| (wndPtr->hrgnUpdate || (wndPtr->flags & WIN_INTERNAL_PAINT)) ) | |
| hrgn = RDW_Paint( wndPtr, hrgn, flags, ex ); | |
| } | |
| WIN_ReleaseWndPtr(wndPtr); | |
| WIN_ReleaseWinArray(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(); | |
| if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE; | |
| /* check if the window or its parents are visible/not minimized */ | |
| if (!WIN_IsWindowDrawable( wndPtr, !(flags & RDW_FRAME) ) ) | |
| { | |
| WIN_ReleaseWndPtr(wndPtr); | |
| return TRUE; | |
| } | |
| 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 ); | |
| } | |
| } | |
| /* 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 ); | |
| rect2i: | |
| 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 ) | |
| DeleteObject( wndPtr->hrgnUpdate ); | |
| wndPtr->hrgnUpdate = 1; | |
| } | |
| else | |
| { | |
| GETCLIENTRECTW( wndPtr, r2 ); | |
| goto rect2i; | |
| } | |
| } | |
| } | |
| 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; | |
| } | |
| /*********************************************************************** | |
| * RedrawWindow (USER.290) | |
| */ | |
| BOOL16 WINAPI RedrawWindow16( HWND16 hwnd, const RECT16 *rectUpdate, | |
| HRGN16 hrgnUpdate, UINT16 flags ) | |
| { | |
| if (rectUpdate) | |
| { | |
| RECT r; | |
| CONV_RECT16TO32( rectUpdate, &r ); | |
| return (BOOL16)RedrawWindow( (HWND)hwnd, &r, hrgnUpdate, flags ); | |
| } | |
| return RedrawWindow( hwnd, NULL, hrgnUpdate, flags ); | |
| } | |
| /*********************************************************************** | |
| * UpdateWindow (USER.124) | |
| */ | |
| void WINAPI UpdateWindow16( HWND16 hwnd ) | |
| { | |
| RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN ); | |
| } | |
| /*********************************************************************** | |
| * UpdateWindow (USER32.@) | |
| */ | |
| void WINAPI UpdateWindow( HWND hwnd ) | |
| { | |
| RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN ); | |
| } | |
| /*********************************************************************** | |
| * InvalidateRgn (USER.126) | |
| */ | |
| void WINAPI InvalidateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase ) | |
| { | |
| RedrawWindow((HWND)hwnd, NULL, (HRGN)hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) ); | |
| } | |
| /*********************************************************************** | |
| * InvalidateRgn (USER32.@) | |
| */ | |
| BOOL WINAPI InvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase ) | |
| { | |
| return RedrawWindow(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) ); | |
| } | |
| /*********************************************************************** | |
| * InvalidateRect (USER.125) | |
| */ | |
| void WINAPI InvalidateRect16( HWND16 hwnd, const RECT16 *rect, BOOL16 erase ) | |
| { | |
| RedrawWindow16( hwnd, rect, 0, 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 (USER.128) | |
| */ | |
| void WINAPI ValidateRgn16( HWND16 hwnd, HRGN16 hrgn ) | |
| { | |
| RedrawWindow( (HWND)hwnd, NULL, (HRGN)hrgn, RDW_VALIDATE | RDW_NOCHILDREN ); | |
| } | |
| /*********************************************************************** | |
| * ValidateRgn (USER32.@) | |
| */ | |
| void WINAPI ValidateRgn( HWND hwnd, HRGN hrgn ) | |
| { | |
| RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOCHILDREN ); | |
| } | |
| /*********************************************************************** | |
| * ValidateRect (USER.127) | |
| */ | |
| void WINAPI ValidateRect16( HWND16 hwnd, const RECT16 *rect ) | |
| { | |
| RedrawWindow16( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN ); | |
| } | |
| /*********************************************************************** | |
| * ValidateRect (USER32.@) | |
| */ | |
| void WINAPI ValidateRect( HWND hwnd, const RECT *rect ) | |
| { | |
| RedrawWindow( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN ); | |
| } | |
| /*********************************************************************** | |
| * GetUpdateRect (USER.190) | |
| */ | |
| BOOL16 WINAPI GetUpdateRect16( HWND16 hwnd, LPRECT16 rect, BOOL16 erase ) | |
| { | |
| RECT r; | |
| BOOL16 ret; | |
| if (!rect) return GetUpdateRect( hwnd, NULL, erase ); | |
| ret = GetUpdateRect( hwnd, &r, erase ); | |
| CONV_RECT32TO16( &r, rect ); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * 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 (USER.237) | |
| */ | |
| INT16 WINAPI GetUpdateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase ) | |
| { | |
| return GetUpdateRgn( hwnd, hrgn, erase ); | |
| } | |
| /*********************************************************************** | |
| * 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 (USER.238) | |
| */ | |
| INT16 WINAPI ExcludeUpdateRgn16( HDC16 hdc, HWND16 hwnd ) | |
| { | |
| return ExcludeUpdateRgn( hdc, hwnd ); | |
| } | |
| /*********************************************************************** | |
| * 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, wndPtr, 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 (USER.448) | |
| */ | |
| BOOL16 WINAPI DrawAnimatedRects16( HWND16 hwnd, INT16 idAni, | |
| const RECT16* lprcFrom, | |
| const RECT16* lprcTo ) | |
| { | |
| RECT rcFrom32, rcTo32; | |
| rcFrom32.left = (INT)lprcFrom->left; | |
| rcFrom32.top = (INT)lprcFrom->top; | |
| rcFrom32.right = (INT)lprcFrom->right; | |
| rcFrom32.bottom = (INT)lprcFrom->bottom; | |
| rcTo32.left = (INT)lprcTo->left; | |
| rcTo32.top = (INT)lprcTo->top; | |
| rcTo32.right = (INT)lprcTo->right; | |
| rcTo32.bottom = (INT)lprcTo->bottom; | |
| return DrawAnimatedRects((HWND)hwnd, (INT)idAni, &rcFrom32, &rcTo32); | |
| } | |
| /********************************************************************** | |
| * 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, BOOL _32bit) | |
| { | |
| 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 if(_32bit) | |
| return DrawTextA(hdc, (LPSTR)lp, (INT)wp, rc, dtflags); | |
| else | |
| return DrawTextA(hdc, MapSL(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); | |
| if(_32bit) | |
| bRet = func(hdc, lp, wp, cx, cy); | |
| else | |
| bRet = (BOOL)((DRAWSTATEPROC16)func)((HDC16)hdc, (LPARAM)lp, (WPARAM16)wp, (INT16)cx, (INT16)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, BOOL _32bit) | |
| { | |
| 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 if(_32bit) | |
| len = strlen((LPSTR)lp); | |
| else | |
| len = strlen(MapSL(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 if(_32bit) | |
| retval = GetTextExtentPoint32A(hdc, (LPSTR)lp, len, &s); | |
| else | |
| retval = GetTextExtentPoint32A(hdc, MapSL(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, _32bit); | |
| } | |
| /* 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, _32bit); | |
| 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, TRUE); | |
| } | |
| /********************************************************************** | |
| * 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, 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) | |
| { | |
| return PAINTING_DrawState(hdc, hbr, (DRAWSTATEPROC)func, ldata, wdata, x, y, cx, cy, flags, FALSE, FALSE); | |
| } | |
| /*********************************************************************** | |
| * 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, hWnd, 0L); | |
| } | |
| return realized; | |
| } | |
| /*********************************************************************** | |
| * UserRealizePalette (USER32.@) | |
| */ | |
| UINT WINAPI UserRealizePalette( HDC hDC ) | |
| { | |
| return RealizePalette16( hDC ); | |
| } |