Added SystemParametersInfo unit test.
[wine] / dlls / x11drv / scroll.c
1 /*
2  * Scroll windows and DCs
3  *
4  * Copyright 1993  David W. Metcalfe
5  * Copyright 1995, 1996 Alex Korobka
6  * Copyright 2001 Alexandre Julliard
7  */
8
9 #include "config.h"
10
11 #include "ts_xlib.h"
12 #include "ts_xutil.h"
13
14 #include "winbase.h"
15 #include "wingdi.h"
16 #include "winuser.h"
17
18 #include "x11drv.h"
19 #include "win.h"
20 #include "debugtools.h"
21
22 DEFAULT_DEBUG_CHANNEL(scroll);
23
24
25 /*************************************************************************
26  *             fix_caret
27  */
28 static BOOL fix_caret(HWND hWnd, LPRECT lprc, UINT flags)
29 {
30    HWND hCaret = CARET_GetHwnd();
31
32    if( hCaret )
33    {
34        RECT rc;
35        CARET_GetRect( &rc );
36        if( hCaret == hWnd ||
37           (flags & SW_SCROLLCHILDREN && IsChild(hWnd, hCaret)) )
38        {
39            POINT pt;
40            pt.x = rc.left;
41            pt.y = rc.top;
42            MapWindowPoints( hCaret, hWnd, (LPPOINT)&rc, 2 );
43            if( IntersectRect(lprc, lprc, &rc) )
44            {
45                HideCaret(0);
46                lprc->left = pt.x;
47                lprc->top = pt.y;
48                return TRUE;
49            }
50        }
51    }
52    return FALSE;
53 }
54
55
56 /*************************************************************************
57  *              ScrollDC   (X11DRV.@)
58  * 
59  * Only the hrgnUpdate is returned in device coordinates.
60  * rcUpdate must be returned in logical coordinates to comply with win API.
61  * FIXME: the doc explicitly states the opposite, to be checked
62  */
63 BOOL X11DRV_ScrollDC( HDC hdc, INT dx, INT dy, const RECT *rc,
64                       const RECT *clipRect, HRGN hrgnUpdate, LPRECT rcUpdate )
65 {
66     RECT rect, rClip, rDst;
67
68     TRACE( "%04x %d,%d hrgnUpdate=%04x rcUpdate = %p\n", hdc, dx, dy, hrgnUpdate, rcUpdate );
69     if (clipRect) TRACE( "cliprc = (%d,%d,%d,%d)\n",
70                          clipRect->left, clipRect->top, clipRect->right, clipRect->bottom );
71     if (rc) TRACE( "rc = (%d,%d,%d,%d)\n", rc->left, rc->top, rc->right, rc->bottom );
72
73     /* compute device clipping region (in device coordinates) */
74
75     if (rc) rect = *rc;
76     else GetClipBox( hdc, &rect );
77
78     if (clipRect)
79     {
80         rClip = *clipRect;
81         IntersectRect( &rClip, &rect, &rClip );
82     }
83     else rClip = rect;
84
85     rDst = rClip;
86     OffsetRect( &rDst, dx,  dy );
87     IntersectRect( &rDst, &rDst, &rClip );
88
89     if (!IsRectEmpty(&rDst))
90     {
91         /* copy bits */
92         if (!BitBlt( hdc, rDst.left, rDst.top,
93                      rDst.right - rDst.left, rDst.bottom - rDst.top,
94                      hdc, rDst.left - dx, rDst.top - dy, SRCCOPY))
95             return FALSE;
96     }
97
98     /* compute update areas */
99
100     if (hrgnUpdate || rcUpdate)
101     {
102         HRGN hrgn = hrgnUpdate, hrgn2;
103
104         /* map everything to device coordinates */
105         LPtoDP( hdc, (LPPOINT)&rClip, 2 );
106         LPtoDP( hdc, (LPPOINT)&rDst, 2 );
107
108         hrgn2 = CreateRectRgnIndirect( &rDst );
109         if (hrgn) SetRectRgn( hrgn, rClip.left, rClip.top, rClip.right, rClip.bottom );
110         else hrgn = CreateRectRgn( rClip.left, rClip.top, rClip.right, rClip.bottom );
111         CombineRgn( hrgn, hrgn, hrgn2, RGN_DIFF );
112
113         if( rcUpdate )
114         {
115             GetRgnBox( hrgn, rcUpdate );
116
117             /* Put the rcUpdate in logical coordinate */
118             DPtoLP( hdc, (LPPOINT)rcUpdate, 2 );
119         }
120         if (!hrgnUpdate) DeleteObject( hrgn );
121         DeleteObject( hrgn2 );
122     }
123     return TRUE;
124 }
125
126
127 /*************************************************************************
128  *              ScrollWindowEx   (X11DRV.@)
129  *
130  * Note: contrary to what the doc says, pixels that are scrolled from the 
131  *      outside of clipRect to the inside are NOT painted.
132  */
133 INT X11DRV_ScrollWindowEx( HWND hwnd, INT dx, INT dy,
134                            const RECT *rect, const RECT *clipRect,
135                            HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags )
136 {
137     INT  retVal = NULLREGION;
138     BOOL bCaret = FALSE, bOwnRgn = TRUE;
139     RECT rc, cliprc;
140
141     if (!WIN_IsWindowDrawable( hwnd, TRUE )) return ERROR;
142     hwnd = WIN_GetFullHandle( hwnd );
143
144     GetClientRect(hwnd, &rc);
145     if (rect) IntersectRect(&rc, &rc, rect);
146
147     if (clipRect) IntersectRect(&cliprc,&rc,clipRect);
148     else cliprc = rc;
149
150     if (!IsRectEmpty(&cliprc) && (dx || dy))
151     {
152         HDC   hDC;
153         BOOL  bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
154         HRGN  hrgnClip = CreateRectRgnIndirect(&cliprc);
155         HRGN  hrgnTemp;
156         RECT  caretrc;
157
158         TRACE( "%04x, %d,%d hrgnUpdate=%04x rcUpdate = %p rc=(%d,%d-%d,%d) %04x\n",
159                hwnd, dx, dy, hrgnUpdate, rcUpdate,
160                rc.left, rc.top, rc.right, rc.bottom, flags );
161         if (clipRect) TRACE( "cliprc = (%d,%d,%d,%d)\n",
162                              clipRect->left, clipRect->top,
163                              clipRect->right, clipRect->bottom );
164
165         caretrc = rc;
166         bCaret = fix_caret(hwnd, &caretrc, flags);
167
168         if( hrgnUpdate ) bOwnRgn = FALSE;
169         else if( bUpdate ) hrgnUpdate = CreateRectRgn( 0, 0, 0, 0 );
170
171         hDC = GetDCEx( hwnd, 0, DCX_CACHE | DCX_USESTYLE );
172         if (hDC)
173         {
174             HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
175             X11DRV_StartGraphicsExposures( hDC );
176             X11DRV_ScrollDC( hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate );
177             X11DRV_EndGraphicsExposures( hDC, hrgn );
178             ReleaseDC( hwnd, hDC );
179             if (bUpdate) CombineRgn( hrgnUpdate, hrgnUpdate, hrgn, RGN_OR );
180             else RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_ERASE );
181             DeleteObject( hrgn );
182         }
183
184         /* Take into account the fact that some damages may have occured during the scroll */
185         hrgnTemp = CreateRectRgn( 0, 0, 0, 0 );
186         if (GetUpdateRgn( hwnd, hrgnTemp, FALSE ) != NULLREGION)
187         {
188             OffsetRgn( hrgnTemp, dx, dy );
189             CombineRgn( hrgnTemp, hrgnTemp, hrgnClip, RGN_AND );
190             RedrawWindow( hwnd, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE );
191         }
192         DeleteObject( hrgnTemp );
193
194         if( flags & SW_SCROLLCHILDREN )
195         {
196             HWND *list = WIN_ListChildren( hwnd );
197
198             if (list)
199             {
200                 int i;
201                 RECT r, dummy;
202                 for (i = 0; list[i]; i++)
203                 {
204                     GetWindowRect( list[i], &r );
205                     MapWindowPoints( 0, hwnd, (POINT *)&r, 2 );
206                     if (!rect || IntersectRect(&dummy, &r, &rc))
207                         SetWindowPos( list[i], 0, r.left + dx, r.top  + dy, 0, 0,
208                                       SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
209                                       SWP_NOREDRAW | SWP_DEFERERASE );
210                 }
211                 HeapFree( GetProcessHeap(), 0, list );
212             }
213         }
214
215         if( flags & (SW_INVALIDATE | SW_ERASE) )
216             RedrawWindow( hwnd, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
217                           ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
218                           ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0 ) );
219
220         if( bCaret )
221         {
222             SetCaretPos( caretrc.left + dx, caretrc.top + dy );
223             ShowCaret(0);
224         }
225
226         if( bOwnRgn && hrgnUpdate ) DeleteObject( hrgnUpdate );
227         DeleteObject( hrgnClip );
228     }
229     return retVal;
230 }