Commit | Line | Data |
---|---|---|
e399fc36 AJ |
1 | /* |
2 | * Scroll windows and DCs | |
3 | * | |
4 | * Copyright David W. Metcalfe, 1993 | |
8cc3a5e4 | 5 | * Alex Korobka 1995,1996 |
d471965c | 6 | * |
0799c1a7 AJ |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
e399fc36 | 11 | * |
0799c1a7 AJ |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
e399fc36 AJ |
20 | */ |
21 | ||
e37c6e18 | 22 | #include <stdarg.h> |
e399fc36 | 23 | #include <stdlib.h> |
b87fe2e9 | 24 | |
d3e22d9d | 25 | #include "windef.h" |
e37c6e18 | 26 | #include "winbase.h" |
d3e22d9d | 27 | #include "wingdi.h" |
1ed4ecff | 28 | #include "wine/winuser16.h" |
61afa33b | 29 | #include "winuser.h" |
6a78c16a | 30 | #include "user_private.h" |
9ffa91ef | 31 | #include "win.h" |
0799c1a7 | 32 | #include "wine/debug.h" |
d471965c | 33 | |
0799c1a7 | 34 | WINE_DEFAULT_DEBUG_CHANNEL(scroll); |
b4b9fae6 | 35 | |
9ffa91ef DP |
36 | /************************************************************************* |
37 | * fix_caret | |
38 | */ | |
39 | static HWND fix_caret(HWND hWnd, LPRECT lprc, UINT flags) | |
40 | { | |
11e35239 | 41 | GUITHREADINFO info; |
9ffa91ef | 42 | |
11e35239 AJ |
43 | if (!GetGUIThreadInfo( GetCurrentThreadId(), &info )) return 0; |
44 | if (!info.hwndCaret) return 0; | |
45 | if (info.hwndCaret == hWnd || | |
46 | ((flags & SW_SCROLLCHILDREN) && IsChild(hWnd, info.hwndCaret))) | |
47 | { | |
48 | POINT pt; | |
49 | pt.x = info.rcCaret.left; | |
50 | pt.y = info.rcCaret.top; | |
51 | MapWindowPoints( info.hwndCaret, hWnd, (LPPOINT)&info.rcCaret, 2 ); | |
52 | if( IntersectRect(lprc, lprc, &info.rcCaret) ) | |
53 | { | |
54 | HideCaret(0); | |
55 | lprc->left = pt.x; | |
56 | lprc->top = pt.y; | |
57 | return info.hwndCaret; | |
58 | } | |
59 | } | |
60 | return 0; | |
9ffa91ef DP |
61 | } |
62 | ||
06a60621 | 63 | /************************************************************************* |
e8698e9e | 64 | * ScrollWindowEx (USER32.@) |
06a60621 UC |
65 | * |
66 | * Note: contrary to what the doc says, pixels that are scrolled from the | |
67 | * outside of clipRect to the inside are NOT painted. | |
68 | * | |
06a60621 | 69 | */ |
e8698e9e RK |
70 | INT WINAPI ScrollWindowEx( HWND hwnd, INT dx, INT dy, |
71 | const RECT *rect, const RECT *clipRect, | |
72 | HRGN hrgnUpdate, LPRECT rcUpdate, | |
73 | UINT flags ) | |
06a60621 | 74 | { |
e8698e9e | 75 | INT retVal = NULLREGION; |
06a60621 UC |
76 | BOOL bOwnRgn = TRUE; |
77 | BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE)); | |
312f09b8 | 78 | int rdw_flags; |
06a60621 UC |
79 | HRGN hrgnTemp; |
80 | HDC hDC; | |
81 | RECT rc, cliprc; | |
e8698e9e RK |
82 | RECT caretrc; |
83 | HWND hwndCaret = NULL; | |
06a60621 UC |
84 | |
85 | TRACE( "%p, %d,%d hrgnUpdate=%p rcUpdate = %p %s %04x\n", | |
86 | hwnd, dx, dy, hrgnUpdate, rcUpdate, wine_dbgstr_rect(rect), flags ); | |
87 | TRACE( "clipRect = %s\n", wine_dbgstr_rect(clipRect)); | |
312f09b8 RK |
88 | if( flags & ~( SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE)) |
89 | FIXME("some flags (%04x) are unhandled\n", flags); | |
90 | ||
91 | rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ? | |
92 | RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE ; | |
06a60621 | 93 | |
e8698e9e RK |
94 | if (!WIN_IsWindowDrawable( hwnd, TRUE )) return ERROR; |
95 | hwnd = WIN_GetFullHandle( hwnd ); | |
96 | ||
06a60621 UC |
97 | GetClientRect(hwnd, &rc); |
98 | if (rect) IntersectRect(&rc, &rc, rect); | |
99 | ||
100 | if (clipRect) IntersectRect(&cliprc,&rc,clipRect); | |
101 | else cliprc = rc; | |
102 | ||
103 | if( hrgnUpdate ) bOwnRgn = FALSE; | |
104 | else if( bUpdate ) hrgnUpdate = CreateRectRgn( 0, 0, 0, 0 ); | |
105 | ||
e8698e9e RK |
106 | if( !IsRectEmpty(&cliprc) && (dx || dy)) { |
107 | caretrc = rc; | |
108 | hwndCaret = fix_caret(hwnd, &caretrc, flags); | |
06a60621 | 109 | |
e8698e9e RK |
110 | hDC = GetDCEx( hwnd, 0, DCX_CACHE | DCX_USESTYLE ); |
111 | if (hDC) | |
112 | { | |
113 | ScrollDC( hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate ); | |
06a60621 | 114 | |
e8698e9e | 115 | ReleaseDC( hwnd, hDC ); |
06a60621 | 116 | |
e8698e9e | 117 | if (!bUpdate) |
312f09b8 | 118 | RedrawWindow( hwnd, NULL, hrgnUpdate, rdw_flags); |
e8698e9e RK |
119 | } |
120 | ||
121 | /* Take into account the fact that some damage may have occurred during | |
122 | * the scroll */ | |
123 | hrgnTemp = CreateRectRgn( 0, 0, 0, 0 ); | |
124 | retVal = GetUpdateRgn( hwnd, hrgnTemp, FALSE ); | |
125 | if (retVal != NULLREGION) | |
126 | { | |
127 | HRGN hrgnClip = CreateRectRgnIndirect(&cliprc); | |
128 | OffsetRgn( hrgnTemp, dx, dy ); | |
129 | CombineRgn( hrgnTemp, hrgnTemp, hrgnClip, RGN_AND ); | |
312f09b8 | 130 | RedrawWindow( hwnd, NULL, hrgnTemp, rdw_flags); |
e8698e9e RK |
131 | DeleteObject( hrgnClip ); |
132 | } | |
133 | DeleteObject( hrgnTemp ); | |
134 | } else { | |
135 | /* nothing was scrolled */ | |
136 | if( !bOwnRgn) | |
137 | SetRectRgn( hrgnUpdate, 0, 0, 0, 0 ); | |
138 | SetRectEmpty( rcUpdate); | |
06a60621 | 139 | } |
06a60621 UC |
140 | |
141 | if( flags & SW_SCROLLCHILDREN ) | |
142 | { | |
143 | HWND *list = WIN_ListChildren( hwnd ); | |
144 | if (list) | |
145 | { | |
146 | int i; | |
147 | RECT r, dummy; | |
148 | for (i = 0; list[i]; i++) | |
149 | { | |
150 | GetWindowRect( list[i], &r ); | |
151 | MapWindowPoints( 0, hwnd, (POINT *)&r, 2 ); | |
e8698e9e | 152 | if (!rect || IntersectRect(&dummy, &r, rect)) |
06a60621 UC |
153 | SetWindowPos( list[i], 0, r.left + dx, r.top + dy, 0, 0, |
154 | SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | | |
155 | SWP_NOREDRAW | SWP_DEFERERASE ); | |
156 | } | |
157 | HeapFree( GetProcessHeap(), 0, list ); | |
158 | } | |
159 | } | |
160 | ||
161 | if( flags & (SW_INVALIDATE | SW_ERASE) ) | |
312f09b8 | 162 | RedrawWindow( hwnd, NULL, hrgnUpdate, rdw_flags | |
06a60621 UC |
163 | ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0 ) ); |
164 | ||
e8698e9e RK |
165 | if( hwndCaret ) { |
166 | SetCaretPos( caretrc.left + dx, caretrc.top + dy ); | |
167 | ShowCaret(hwndCaret); | |
168 | } | |
169 | ||
06a60621 UC |
170 | if( bOwnRgn && hrgnUpdate ) DeleteObject( hrgnUpdate ); |
171 | ||
172 | return retVal; | |
173 | } | |
9ffa91ef | 174 | |
75d86e1f | 175 | /************************************************************************* |
2ece70e7 | 176 | * ScrollWindow (USER32.@) |
670cdc45 | 177 | * |
75d86e1f | 178 | */ |
a3960292 AJ |
179 | BOOL WINAPI ScrollWindow( HWND hwnd, INT dx, INT dy, |
180 | const RECT *rect, const RECT *clipRect ) | |
e399fc36 | 181 | { |
1da6dbab | 182 | return |
a3960292 | 183 | (ERROR != ScrollWindowEx( hwnd, dx, dy, rect, clipRect, 0, NULL, |
1da6dbab JV |
184 | (rect ? 0 : SW_SCROLLCHILDREN) | |
185 | SW_INVALIDATE )); | |
e399fc36 AJ |
186 | } |
187 | ||
75d86e1f | 188 | /************************************************************************* |
2ece70e7 | 189 | * ScrollDC (USER32.@) |
9a624916 | 190 | * |
a51bb815 AJ |
191 | * dx, dy, lprcScroll and lprcClip are all in logical coordinates (msdn is wrong) |
192 | * hrgnUpdate is returned in device coordinates with rcUpdate in logical coordinates. | |
75d86e1f | 193 | */ |
a51bb815 AJ |
194 | BOOL WINAPI ScrollDC( HDC hdc, INT dx, INT dy, const RECT *lprcScroll, |
195 | const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate ) | |
196 | ||
e399fc36 | 197 | { |
a51bb815 AJ |
198 | RECT rSrc, rClipped_src, rClip, rDst, offset; |
199 | ||
200 | TRACE( "%p %d,%d hrgnUpdate=%p lprcUpdate = %p\n", hdc, dx, dy, hrgnUpdate, lprcUpdate ); | |
201 | if (lprcClip) TRACE( "lprcClip = %s\n", wine_dbgstr_rect(lprcClip)); | |
202 | if (lprcScroll) TRACE( "lprcScroll = %s\n", wine_dbgstr_rect(lprcScroll)); | |
203 | ||
06a60621 UC |
204 | if (USER_Driver.pScrollDC) |
205 | return USER_Driver.pScrollDC( hdc, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate ); | |
a51bb815 | 206 | |
06a60621 | 207 | /* compute device clipping region (in device coordinates) */ |
a51bb815 AJ |
208 | if (lprcScroll) rSrc = *lprcScroll; |
209 | else GetClipBox( hdc, &rSrc ); | |
210 | LPtoDP(hdc, (LPPOINT)&rSrc, 2); | |
211 | ||
212 | if (lprcClip) rClip = *lprcClip; | |
213 | else GetClipBox( hdc, &rClip ); | |
214 | LPtoDP(hdc, (LPPOINT)&rClip, 2); | |
215 | ||
216 | IntersectRect( &rClipped_src, &rSrc, &rClip ); | |
217 | TRACE("rSrc %s rClip %s clipped rSrc %s\n", wine_dbgstr_rect(&rSrc), | |
218 | wine_dbgstr_rect(&rClip), wine_dbgstr_rect(&rClipped_src)); | |
219 | ||
220 | rDst = rClipped_src; | |
221 | SetRect(&offset, 0, 0, dx, dy); | |
222 | LPtoDP(hdc, (LPPOINT)&offset, 2); | |
223 | OffsetRect( &rDst, offset.right - offset.left, offset.bottom - offset.top ); | |
224 | TRACE("rDst before clipping %s\n", wine_dbgstr_rect(&rDst)); | |
225 | IntersectRect( &rDst, &rDst, &rClip ); | |
226 | TRACE("rDst after clipping %s\n", wine_dbgstr_rect(&rDst)); | |
227 | ||
228 | if (!IsRectEmpty(&rDst)) | |
229 | { | |
230 | /* copy bits */ | |
231 | RECT rDst_lp = rDst, rSrc_lp = rDst; | |
232 | ||
233 | OffsetRect( &rSrc_lp, offset.left - offset.right, offset.top - offset.bottom ); | |
234 | DPtoLP(hdc, (LPPOINT)&rDst_lp, 2); | |
235 | DPtoLP(hdc, (LPPOINT)&rSrc_lp, 2); | |
236 | ||
237 | if (!BitBlt( hdc, rDst_lp.left, rDst_lp.top, | |
238 | rDst_lp.right - rDst_lp.left, rDst_lp.bottom - rDst_lp.top, | |
239 | hdc, rSrc_lp.left, rSrc_lp.top, SRCCOPY)) | |
240 | return FALSE; | |
241 | } | |
242 | ||
243 | /* compute update areas. This is the clipped source or'ed with the unclipped source translated minus the | |
244 | clipped src translated (rDst) all clipped to rClip */ | |
245 | ||
246 | if (hrgnUpdate || lprcUpdate) | |
247 | { | |
248 | HRGN hrgn = hrgnUpdate, hrgn2; | |
249 | ||
250 | if (hrgn) SetRectRgn( hrgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom ); | |
251 | else hrgn = CreateRectRgn( rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom ); | |
252 | ||
253 | hrgn2 = CreateRectRgnIndirect( &rSrc ); | |
254 | OffsetRgn(hrgn2, offset.right - offset.left, offset.bottom - offset.top ); | |
255 | CombineRgn(hrgn, hrgn, hrgn2, RGN_OR); | |
256 | ||
257 | SetRectRgn( hrgn2, rDst.left, rDst.top, rDst.right, rDst.bottom ); | |
258 | CombineRgn( hrgn, hrgn, hrgn2, RGN_DIFF ); | |
259 | ||
260 | SetRectRgn( hrgn2, rClip.left, rClip.top, rClip.right, rClip.bottom ); | |
261 | CombineRgn( hrgn, hrgn, hrgn2, RGN_AND ); | |
262 | ||
263 | if( lprcUpdate ) | |
264 | { | |
265 | GetRgnBox( hrgn, lprcUpdate ); | |
266 | ||
267 | /* Put the lprcUpdate in logical coordinate */ | |
268 | DPtoLP( hdc, (LPPOINT)lprcUpdate, 2 ); | |
269 | TRACE("returning lprcUpdate %s\n", wine_dbgstr_rect(lprcUpdate)); | |
270 | } | |
271 | if (!hrgnUpdate) DeleteObject( hrgn ); | |
272 | DeleteObject( hrgn2 ); | |
273 | } | |
274 | return TRUE; | |
e399fc36 | 275 | } |